MARMOTTA-508: correctly implemented testPutToCreate, so now Marmotta supports PUT to create LDP-RS
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/a8781fac Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/a8781fac Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/a8781fac Branch: refs/heads/ldp Commit: a8781fac772f0c14f0082d64cc37b3e2065639d8 Parents: 8f10003 Author: Sergio Fernández <[email protected]> Authored: Tue Jul 15 10:26:23 2014 +0200 Committer: Sergio Fernández <[email protected]> Committed: Tue Jul 15 10:26:23 2014 +0200 ---------------------------------------------------------------------- .../marmotta/platform/ldp/api/LdpService.java | 72 ++++++++++++++-- .../platform/ldp/services/LdpServiceImpl.java | 42 ++++++---- .../marmotta/platform/ldp/util/LdpUtils.java | 16 ++++ .../platform/ldp/webservices/LdpWebService.java | 86 ++++++++++---------- 4 files changed, 151 insertions(+), 65 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/a8781fac/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpService.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpService.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpService.java index 5a15d12..e148f0f 100644 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpService.java +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpService.java @@ -46,8 +46,6 @@ import java.util.*; */ public interface LdpService { - - public static enum InteractionModel { LDPR(LDP.Resource), LDPC(LDP.Container); @@ -151,13 +149,75 @@ public interface LdpService { */ String addResource(RepositoryConnection connection, URI container, URI resource, InteractionModel interactionModel, String type, InputStream stream) throws RepositoryException, IOException, RDFParseException; - String updateResource(RepositoryConnection con, String resource, InputStream stream, String type) throws RepositoryException, IncompatibleResourceTypeException, RDFParseException, IOException, InvalidModificationException; + /** + * Update an existing resource + * + * @param connection repository connection + * @param resource resource to add + * @param stream stream with the data + * @param type resource type + * @return updated resource uri + * @throws RepositoryException + * @throws IncompatibleResourceTypeException + * @throws RDFParseException + * @throws IOException + * @throws InvalidModificationException + */ + String updateResource(RepositoryConnection connection, String resource, InputStream stream, String type) throws RepositoryException, IncompatibleResourceTypeException, RDFParseException, IOException, InvalidModificationException; + + /** + * Update an existing resource + * + * @param connection repository connection + * @param resource resource to add + * @param stream stream with the data + * @param type resource type + * @return updated resource uri + * @throws RepositoryException + * @throws IncompatibleResourceTypeException + * @throws IOException + * @throws RDFParseException + * @throws InvalidModificationException + */ + String updateResource(RepositoryConnection connection, URI resource, InputStream stream, String type) throws RepositoryException, IncompatibleResourceTypeException, IOException, RDFParseException, InvalidModificationException; + + /** + * Update an existing resource + * + * @param connection repository connection + * @param resource resource to add + * @param stream stream with the data + * @param type resource type + * @param overwrite overwrite current resource + * @return updated resource uri + * @throws RepositoryException + * @throws IncompatibleResourceTypeException + * @throws RDFParseException + * @throws IOException + * @throws InvalidModificationException + */ + String updateResource(RepositoryConnection connection, String resource, InputStream stream, String type, boolean overwrite) throws RepositoryException, IncompatibleResourceTypeException, RDFParseException, IOException, InvalidModificationException; - String updateResource(RepositoryConnection con, URI resource, InputStream stream, String type) throws RepositoryException, IncompatibleResourceTypeException, IOException, RDFParseException, InvalidModificationException; + /** + * Update an existing resource + * + * @param connection repository connection + * @param resource resource to add + * @param stream stream with the data + * @param type resource type + * @param overwrite overwrite current resource + * @return updated resource uri + * @throws RepositoryException + * @throws IncompatibleResourceTypeException + * @throws IOException + * @throws RDFParseException + * @throws InvalidModificationException + */ + String updateResource(RepositoryConnection connection, URI resource, InputStream stream, String type, boolean overwrite) throws RepositoryException, IncompatibleResourceTypeException, IOException, RDFParseException, InvalidModificationException; List<Statement> getLdpTypes(RepositoryConnection connection, String resource) throws RepositoryException; - List<Statement> getLdpTypes(RepositoryConnection conn1, URI resource) throws RepositoryException; + List<Statement> getLdpTypes(RepositoryConnection connection, URI resource) throws RepositoryException; void exportResource(RepositoryConnection connection, String resource, OutputStream output, RDFFormat format) throws RepositoryException, RDFHandlerException; @@ -200,11 +260,13 @@ public interface LdpService { boolean isRdfSourceResource(RepositoryConnection connection, URI uri) throws RepositoryException; URI getNonRdfSourceForRdfSource(RepositoryConnection connection, String resource) throws RepositoryException; + URI getNonRdfSourceForRdfSource(RepositoryConnection connection, URI uri) throws RepositoryException; InteractionModel getInteractionModel(List<Link> linkHeaders) throws InvalidInteractionModelException; InteractionModel getInteractionModel(RepositoryConnection connection, String resource) throws RepositoryException; + InteractionModel getInteractionModel(RepositoryConnection connection, URI uri) throws RepositoryException; } http://git-wip-us.apache.org/repos/asf/marmotta/blob/a8781fac/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpServiceImpl.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpServiceImpl.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpServiceImpl.java index 09dcc69..75053b1 100644 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpServiceImpl.java +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpServiceImpl.java @@ -324,32 +324,42 @@ public class LdpServiceImpl implements LdpService { } @Override - public String updateResource(RepositoryConnection con, String resource, InputStream stream, String type) throws RepositoryException, IncompatibleResourceTypeException, RDFParseException, IOException, InvalidModificationException { - return updateResource(con, buildURI(resource), stream, type); + public String updateResource(RepositoryConnection connection, final String resource, InputStream stream, final String type) throws RepositoryException, IncompatibleResourceTypeException, RDFParseException, IOException, InvalidModificationException { + return updateResource(connection, buildURI(resource), stream, type); } @Override - public String updateResource(final RepositoryConnection con, final URI resource, InputStream stream, String type) throws RepositoryException, IncompatibleResourceTypeException, IOException, RDFParseException, InvalidModificationException { - final ValueFactory valueFactory = con.getValueFactory(); + public String updateResource(final RepositoryConnection connection, final URI resource, InputStream stream, final String type) throws RepositoryException, IncompatibleResourceTypeException, IOException, RDFParseException, InvalidModificationException { + return updateResource(connection, resource, stream, type, false); + } + + @Override + public String updateResource(RepositoryConnection connection, final String resource, InputStream stream, final String type, final boolean overwrite) throws RepositoryException, IncompatibleResourceTypeException, RDFParseException, IOException, InvalidModificationException { + return updateResource(connection, buildURI(resource), stream, type, false); + } + + @Override + public String updateResource(RepositoryConnection connection, final URI resource, InputStream stream, final String type, final boolean overwrite) throws RepositoryException, IncompatibleResourceTypeException, RDFParseException, IOException, InvalidModificationException { + final ValueFactory valueFactory = connection.getValueFactory(); final Literal now = valueFactory.createLiteral(new Date()); - con.remove(resource, DCTERMS.modified, null, ldpContext); - con.add(resource, DCTERMS.modified, now, ldpContext); + connection.remove(resource, DCTERMS.modified, null, ldpContext); + connection.add(resource, DCTERMS.modified, now, ldpContext); final RDFFormat rdfFormat = Rio.getParserFormatForMIMEType(type); // Check submitted format vs. real resource type (RDF-S vs. Non-RDF) - if (rdfFormat == null && isNonRdfSourceResource(con, resource)) { + if (rdfFormat == null && isNonRdfSourceResource(connection, resource)) { log.debug("Updating <{}> as LDP-NR (binary) - {}", resource, type); final Literal format = valueFactory.createLiteral(type); - con.remove(resource, DCTERMS.format, null, ldpContext); - con.add(resource, DCTERMS.format, format, ldpContext); //nie:mimeType ? + connection.remove(resource, DCTERMS.format, null, ldpContext); + connection.add(resource, DCTERMS.format, format, ldpContext); //nie:mimeType ? - final URI ldp_rs = getRdfSourceForNonRdfSource(con, resource); + final URI ldp_rs = getRdfSourceForNonRdfSource(connection, resource); if (ldp_rs != null) { - con.remove(ldp_rs, DCTERMS.modified, null, ldpContext); - con.add(ldp_rs, DCTERMS.modified, now, ldpContext); + connection.remove(ldp_rs, DCTERMS.modified, null, ldpContext); + connection.add(ldp_rs, DCTERMS.modified, now, ldpContext); log.trace("Updated Meta-Data of LDP-RS <{}> for LDP-NR <{}>; Modified: {}", ldp_rs, resource, now); } else { log.debug("LDP-RS for LDP-NR <{}> not found", resource); @@ -360,11 +370,11 @@ public class LdpServiceImpl implements LdpService { log.trace("LDP-NR <{}> updated", resource); return resource.stringValue(); - } else if (rdfFormat != null && isRdfSourceResource(con, resource)) { + } else if (rdfFormat != null && isRdfSourceResource(connection, resource)) { log.debug("Updating <{}> as LDP-RS - {}", resource, rdfFormat.getDefaultMIMEType()); - con.clear(resource); - final InterceptingRepositoryConnectionWrapper filtered = new InterceptingRepositoryConnectionWrapper(con.getRepository(), con); + connection.clear(resource); + final InterceptingRepositoryConnectionWrapper filtered = new InterceptingRepositoryConnectionWrapper(connection.getRepository(), connection); final Set<URI> deniedProperties = new HashSet<>(); filtered.addRepositoryConnectionInterceptor(new RepositoryConnectionInterceptorAdapter() { @Override @@ -387,7 +397,7 @@ public class LdpServiceImpl implements LdpService { log.trace("LDP-RS <{}> updated", resource); return resource.stringValue(); } else if (rdfFormat == null) { - final String mimeType = getMimeType(con, resource); + final String mimeType = getMimeType(connection, resource); log.debug("Incompatible replacement: Can't replace {} with {}", mimeType, type); throw new IncompatibleResourceTypeException(mimeType, type); } else { http://git-wip-us.apache.org/repos/asf/marmotta/blob/a8781fac/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpUtils.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpUtils.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpUtils.java index 4d50162..4ec9fe7 100644 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpUtils.java +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpUtils.java @@ -25,6 +25,7 @@ import org.apache.tika.mime.MimeTypeException; import org.apache.tika.mime.MimeTypes; import org.openrdf.model.Statement; import org.openrdf.model.URI; +import org.openrdf.model.impl.URIImpl; import org.openrdf.model.vocabulary.DCTERMS; import org.openrdf.model.vocabulary.RDF; import org.openrdf.repository.RepositoryException; @@ -34,6 +35,11 @@ import org.openrdf.rio.RDFParserRegistry; import org.openrdf.rio.RDFWriter; import javax.ws.rs.core.MediaType; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Paths; import java.util.Iterator; import java.util.Set; @@ -131,4 +137,14 @@ public class LdpUtils { return sb.toString(); } + public static URI getContainer(String resource) throws MalformedURLException, URISyntaxException { + java.net.URI uri = new java.net.URI(resource); + java.net.URI parent = uri.getPath().endsWith("/") ? uri.resolve("..") : uri.resolve("."); + return new URIImpl(parent.toASCIIString()); + } + + public static URI getContainer(URI resource) throws MalformedURLException, URISyntaxException { + return new URIImpl(resource.getNamespace()); + } + } http://git-wip-us.apache.org/repos/asf/marmotta/blob/a8781fac/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java index 9a01c94..f4afbf9 100644 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/webservices/LdpWebService.java @@ -50,6 +50,7 @@ import javax.ws.rs.core.*; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.net.URISyntaxException; import java.util.List; import java.util.Set; import java.util.UUID; @@ -302,68 +303,65 @@ public class LdpWebService { public Response PUT(@Context UriInfo uriInfo, @Context Request request, @HeaderParam(HttpHeaders.IF_MATCH) EntityTag eTag, @HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType type, InputStream postBody) - throws RepositoryException { + throws RepositoryException, IOException, InvalidModificationException, RDFParseException, IncompatibleResourceTypeException, URISyntaxException { final String resource = getResourceUri(uriInfo); - log.debug("PUT to <{}>", resource); + log.error("PUT to <{}>", resource); - final RepositoryConnection con = sesameService.getConnection(); + final RepositoryConnection conn = sesameService.getConnection(); try { - con.begin(); - - if (!ldpService.exists(con, resource)) { - log.trace("Resource does not exists: {}", resource); - final Response.ResponseBuilder resp = createResponse(con, Response.Status.NOT_FOUND, resource); - con.rollback(); - return resp.build(); - } + conn.begin(); - if (eTag == null) { - // check for If-Match header (ETag) -> 428 Precondition Required (Sec. 4.2.4.5) - log.trace("No If-Match header, but that's a MUST"); - final Response.ResponseBuilder resp = createResponse(con, 428, resource); - con.rollback(); - return resp.build(); - } else { - // check ETag -> 412 Precondition Failed (Sec. 4.2.4.5) - log.trace("Checking If-Match: {}", eTag); - EntityTag hasTag = ldpService.generateETag(con, resource); - if (!EntityTagUtils.equals(eTag, hasTag)) { - log.trace("If-Match header did not match, expected {}", hasTag); - final Response.ResponseBuilder resp = createResponse(con, Response.Status.PRECONDITION_FAILED, resource); - con.rollback(); + final String mimeType = LdpUtils.getMimeType(type); + final Response.ResponseBuilder resp; + final String newResource; // NOTE: newResource == resource for now, this might change in the future + if (ldpService.exists(conn, resource)) { + log.debug("updating resource <{}>", resource); + + if (eTag == null) { + // check for If-Match header (ETag) -> 428 Precondition Required (Sec. 4.2.4.5) + log.trace("No If-Match header, but that's a MUST"); + resp = createResponse(conn, 428, resource); + conn.rollback(); return resp.build(); + } else { + // check ETag -> 412 Precondition Failed (Sec. 4.2.4.5) + log.trace("Checking If-Match: {}", eTag); + EntityTag hasTag = ldpService.generateETag(conn, resource); + if (!EntityTagUtils.equals(eTag, hasTag)) { + log.trace("If-Match header did not match, expected {}", hasTag); + resp = createResponse(conn, Response.Status.PRECONDITION_FAILED, resource); + conn.rollback(); + return resp.build(); + } } - } - - final String mimeType = LdpUtils.getMimeType(type); - log.trace("updating resource <{}>", resource); - // NOTE: newResource == resource for now, this might change in the future. - final String newResource = ldpService.updateResource(con, resource, postBody, mimeType); - final Response.ResponseBuilder resp; - if (resource.equals(newResource)) { - log.trace("PUT update for <{}> successful", resource); - resp = createResponse(con, Response.Status.OK, resource); + newResource = ldpService.updateResource(conn, resource, postBody, mimeType); + log.info("PUT update for <{}> successful", newResource); + resp = createResponse(conn, Response.Status.OK, resource); } else { - log.trace("PUT on <{}> created new resource <{}>", resource, newResource); - resp = createResponse(con, Response.Status.CREATED, resource).location(java.net.URI.create(newResource)); + log.debug("creating resource <{}>", resource); + //LDP servers may allow resource creation using PUT (Sec. 4.2.4.6) + URI uri = conn.getValueFactory().createURI(resource); + newResource = ldpService.addResource(conn, LdpUtils.getContainer(uri), uri, LdpService.InteractionModel.LDPR, mimeType, postBody); + log.info("PUT on <{}> created new resource", newResource); + resp = createResponse(conn, Response.Status.CREATED, newResource).location(java.net.URI.create(newResource)); } - con.commit(); + conn.commit(); return resp.build(); } catch (IOException | RDFParseException e) { - final Response.ResponseBuilder resp = createResponse(con, Response.Status.BAD_REQUEST, resource).entity(e.getClass().getSimpleName() + ": " + e.getMessage()); - con.rollback(); + final Response.ResponseBuilder resp = createResponse(conn, Response.Status.BAD_REQUEST, resource).entity(e.getClass().getSimpleName() + ": " + e.getMessage()); + conn.rollback(); return resp.build(); } catch (InvalidModificationException | IncompatibleResourceTypeException e) { - final Response.ResponseBuilder resp = createResponse(con, Response.Status.CONFLICT, resource).entity(e.getClass().getSimpleName() + ": " + e.getMessage()); - con.rollback(); + final Response.ResponseBuilder resp = createResponse(conn, Response.Status.CONFLICT, resource).entity(e.getClass().getSimpleName() + ": " + e.getMessage()); + conn.rollback(); return resp.build(); } catch (final Throwable t) { - con.rollback(); + conn.rollback(); throw t; } finally { - con.close(); + conn.close(); } } /**
