MARMOTTA-449: implemented LDP-NR accroding the last spec (sec. 6.2.3.12)
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/3c1962b6 Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/3c1962b6 Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/3c1962b6 Branch: refs/heads/develop Commit: 3c1962b6cfe05b6162751b9a8cb90ec6969ceef2 Parents: 20d8794 Author: Sergio Fernández <[email protected]> Authored: Mon Mar 3 14:23:52 2014 +0100 Committer: Sergio Fernández <[email protected]> Committed: Mon Mar 3 14:23:52 2014 +0100 ---------------------------------------------------------------------- .../marmotta/platform/ldp/api/LdpService.java | 35 +++++++- .../platform/ldp/services/LdpServiceImpl.java | 48 +++++++--- .../marmotta/platform/ldp/util/LdpUtils.java | 95 ++++++++++++++++++++ .../platform/ldp/util/LdpWebServiceUtils.java | 74 --------------- .../platform/ldp/webservices/LdpWebService.java | 39 ++++---- 5 files changed, 183 insertions(+), 108 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/3c1962b6/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 7978273..8f49e82 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 @@ -40,6 +40,7 @@ import java.util.List; * LDP Service * * @author Sergio Fernández + * @author Jakob Frank */ public interface LdpService { @@ -47,9 +48,37 @@ public interface LdpService { boolean exists(RepositoryConnection connection, URI resource) throws RepositoryException; - boolean addResource(RepositoryConnection connection, String container, String resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException; - - boolean addResource(RepositoryConnection connection, URI container, URI resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException; + boolean exists(RepositoryConnection connection, URI resource, URI type) throws RepositoryException; + + /** + * Add a LDP resource + * + * @param connection repository connection + * @param container container where add the resource + * @param resource resource to add + * @param type mimetype of the posted resource + * @param stream stream from where read the resource representation + * @return resource location + * @throws RepositoryException + * @throws IOException + * @throws RDFParseException + */ + String addResource(RepositoryConnection connection, String container, String resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException; + + /** + * Add a LDP resource + * + * @param connection repository connection + * @param container container where add the resource + * @param resource resource to add + * @param type mimetype of the posted resource + * @param stream stream from where read the resource representation + * @return resource location + * @throws RepositoryException + * @throws IOException + * @throws RDFParseException + */ + String addResource(RepositoryConnection connection, URI container, URI resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException; List<Statement> getLdpTypes(RepositoryConnection connection, String resource) throws RepositoryException; http://git-wip-us.apache.org/repos/asf/marmotta/blob/3c1962b6/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 2cd4fc3..e5eea4e 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 @@ -32,7 +32,7 @@ import org.apache.marmotta.platform.ldp.patch.RdfPatchUtil; import org.apache.marmotta.platform.ldp.patch.model.PatchLine; import org.apache.marmotta.platform.ldp.patch.parser.ParseException; import org.apache.marmotta.platform.ldp.patch.parser.RdfPatchParserImpl; -import org.apache.marmotta.platform.ldp.util.LdpWebServiceUtils; +import org.apache.marmotta.platform.ldp.util.LdpUtils; import org.openrdf.model.*; import org.openrdf.model.impl.ValueFactoryImpl; import org.openrdf.model.vocabulary.RDF; @@ -91,6 +91,11 @@ public class LdpServiceImpl implements LdpService { } @Override + public boolean exists(RepositoryConnection connection, URI resource, URI type) throws RepositoryException { + return connection.hasStatement(resource, RDF.TYPE, type, true, ldpContext); + } + + @Override public List<Statement> getLdpTypes(RepositoryConnection connection, String resource) throws RepositoryException { return getLdpTypes(connection, buildURI(resource)); } @@ -121,7 +126,7 @@ public class LdpServiceImpl implements LdpService { connection.getStatements(resource, null, null, false, ldpContext) ); try { - LdpWebServiceUtils.exportIteration(writer, resource, union); + LdpUtils.exportIteration(writer, resource, union); } finally { union.close(); } @@ -145,12 +150,12 @@ public class LdpServiceImpl implements LdpService { } @Override - public boolean addResource(RepositoryConnection connection, String container, String resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException { + public String addResource(RepositoryConnection connection, String container, String resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException { return addResource(connection, buildURI(container), buildURI(resource), type, stream); } @Override - public boolean addResource(RepositoryConnection connection, URI container, URI resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException { + public String addResource(RepositoryConnection connection, URI container, URI resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException { ValueFactory valueFactory = connection.getValueFactory(); // Add container triples (Sec. 6.4.3) @@ -158,31 +163,48 @@ public class LdpServiceImpl implements LdpService { Literal now = valueFactory.createLiteral(new Date()); - connection.add(container, RDF.TYPE, LDP.BasicContainer, ldpContext); + if (exists(connection, container, LDP.BasicContainer)) { + connection.remove(container, DCTERMS.modified, null, ldpContext); + } else { + connection.add(container, RDF.TYPE, LDP.BasicContainer, ldpContext); + } + connection.add(container, LDP.contains, resource, ldpContext); - connection.remove(container, DCTERMS.modified, null, ldpContext); connection.add(container, DCTERMS.modified, now, ldpContext); - connection.add(resource, RDF.TYPE, LDP.Resource, ldpContext); - connection.add(resource, DCTERMS.created, now, ldpContext); - connection.add(resource, DCTERMS.modified, now, ldpContext); - // Add the bodyContent log.trace("Content ({}) for new resource <{}>", type, resource); final RDFFormat rdfFormat = Rio.getParserFormatForMIMEType(type.toString()); if (rdfFormat == null) { log.debug("POST creates new LDP-BR, because no RDF parser found for type {}", type); Literal format = valueFactory.createLiteral(type.toString()); - connection.add(resource, DCTERMS.format, format, ldpContext); //nie:mimeType ? + URI binaryResource = valueFactory.createURI(resource.stringValue() + LdpUtils.getExtension(type.toString())); + + //connection.add(resource, RDF.TYPE, LDP.NonRdfResource, ldpContext); //TODO: + connection.add(binaryResource, DCTERMS.created, now, ldpContext); + connection.add(binaryResource, DCTERMS.modified, now, ldpContext); + + //extra triples + //TODO: check conformance with 6.2.3.12 + connection.add(binaryResource, DCTERMS.format, format, ldpContext); //nie:mimeType ? + connection.add(binaryResource, DCTERMS.isFormatOf, resource, ldpContext); + //TODO: something else? - return binaryStore.store(resource, stream); //TODO: control exceptions + + binaryStore.store(binaryResource, stream);//TODO: exceptions control + + return binaryResource.stringValue(); } else { log.debug("POST creates new LDP-RR, data provided as {}", rdfFormat.getName()); + connection.add(resource, RDF.TYPE, LDP.Resource, ldpContext); + connection.add(resource, DCTERMS.created, now, ldpContext); + connection.add(resource, DCTERMS.modified, now, ldpContext); + // FIXME: We are (are we?) allowed to filter out server-managed properties here connection.add(stream, resource.stringValue(), rdfFormat, resource); - return true; + return resource.stringValue(); } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/3c1962b6/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 new file mode 100644 index 0000000..dc565b4 --- /dev/null +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpUtils.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.marmotta.platform.ldp.util; + +import info.aduna.iteration.CloseableIteration; +import org.apache.marmotta.commons.vocabulary.LDP; +import org.apache.marmotta.commons.vocabulary.XSD; +import org.apache.tika.mime.MimeType; +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.vocabulary.DCTERMS; +import org.openrdf.model.vocabulary.RDF; +import org.openrdf.repository.RepositoryException; +import org.openrdf.rio.RDFHandlerException; +import org.openrdf.rio.RDFWriter; + +/** + * Various Util-Methods for the {@link org.apache.marmotta.platform.ldp.api.LdpService}. + */ +public class LdpUtils { + + /** + * Urify the Slug: header value, i.e. replace all non-url chars with a single dash. + * + * @param slugHeaderValue the client-provided Slug-header + * @return the slugHeaderValue "urified" + */ + public static String urify(String slugHeaderValue) { + return slugHeaderValue.trim() + // Replace non-url chars with '-' + .replaceAll("[^\\w]+", "-"); + } + + /** + * Get the preferred file extension for the content type + * + * @param contentType content type + * @return file extension (already including '.') + * @throws MimeTypeException + */ + public static String getExtension(String contentType) { + MimeTypes allTypes = MimeTypes.getDefaultMimeTypes(); + try { + MimeType mimeType = allTypes.forName(contentType); + return mimeType.getExtension(); + } catch (MimeTypeException e) { + return null; //FIXME + } + + } + + /** + * LDP-Style to serialize a resource. + * + * @param writer the writer to serialize to + * @param subject the resource to serialize + * @param iteration the Iteration containing the data + * @throws RDFHandlerException + * @throws RepositoryException + */ + public static void exportIteration(RDFWriter writer, URI subject, CloseableIteration<Statement, RepositoryException> iteration) throws RDFHandlerException, RepositoryException { + writer.startRDF(); + + writer.handleNamespace(LDP.PREFIX, LDP.NAMESPACE); + writer.handleNamespace(RDF.PREFIX, RDF.NAMESPACE); + writer.handleNamespace(XSD.PREFIX, XSD.NAMESPACE); + writer.handleNamespace(DCTERMS.PREFIX, DCTERMS.NAMESPACE); + + writer.handleNamespace("", subject.stringValue()); + + while (iteration.hasNext()) { + writer.handleStatement(iteration.next()); + } + + writer.endRDF(); + } + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/3c1962b6/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpWebServiceUtils.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpWebServiceUtils.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpWebServiceUtils.java deleted file mode 100644 index 1116879..0000000 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/util/LdpWebServiceUtils.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.marmotta.platform.ldp.util; - -import info.aduna.iteration.CloseableIteration; -import info.aduna.iteration.Iteration; -import info.aduna.iteration.UnionIteration; -import org.apache.marmotta.commons.vocabulary.LDP; -import org.apache.marmotta.commons.vocabulary.XSD; -import org.openrdf.model.Statement; -import org.openrdf.model.URI; -import org.openrdf.model.vocabulary.DCTERMS; -import org.openrdf.model.vocabulary.RDF; -import org.openrdf.repository.RepositoryException; -import org.openrdf.rio.RDFHandlerException; -import org.openrdf.rio.RDFWriter; - -/** - * Various Util-Methods for the {@link org.apache.marmotta.platform.ldp.webservices.LdpWebService}. - */ -public class LdpWebServiceUtils { - - /** - * Urify the Slug: header value, i.e. replace all non-url chars with a single dash. - * - * @param slugHeaderValue the client-provided Slug-header - * @return the slugHeaderValue "urified" - */ - public static String urify(String slugHeaderValue) { - return slugHeaderValue - // Replace non-url chars with '-' - .replaceAll("[^\\w]+", "-"); - } - - /** - * LDP-Style to serialize a resource. - * @param writer the writer to serialize to - * @param subject the resource to serialize - * @param iteration the Iteration containing the data - * @throws RDFHandlerException - * @throws RepositoryException - */ - public static void exportIteration(RDFWriter writer, URI subject, CloseableIteration<Statement, RepositoryException> iteration) throws RDFHandlerException, RepositoryException { - writer.startRDF(); - - writer.handleNamespace(LDP.PREFIX, LDP.NAMESPACE); - writer.handleNamespace(RDF.PREFIX, RDF.NAMESPACE); - writer.handleNamespace(XSD.PREFIX, XSD.NAMESPACE); - writer.handleNamespace(DCTERMS.PREFIX, DCTERMS.NAMESPACE); - - writer.handleNamespace("", subject.stringValue()); - - while (iteration.hasNext()) { - writer.handleStatement(iteration.next()); - } - - writer.endRDF(); - } -} http://git-wip-us.apache.org/repos/asf/marmotta/blob/3c1962b6/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 88da2e3..f10da48 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 @@ -27,7 +27,7 @@ import org.apache.marmotta.platform.ldp.patch.InvalidPatchDocumentException; import org.apache.marmotta.platform.ldp.patch.parser.ParseException; import org.apache.marmotta.platform.ldp.patch.parser.RdfPatchParser; import org.apache.marmotta.platform.ldp.util.EntityTagUtils; -import org.apache.marmotta.platform.ldp.util.LdpWebServiceUtils; +import org.apache.marmotta.platform.ldp.util.LdpUtils; import org.jboss.resteasy.spi.NoLogWebApplicationException; import org.openrdf.model.Statement; import org.openrdf.model.URI; @@ -178,11 +178,8 @@ public class LdpWebService { final String container = getResourceUri(uriInfo); log.debug("POST to LDPC <{}>", container); - final RepositoryConnection con = sesameService.getConnection(); + final RepositoryConnection conn = sesameService.getConnection(); try { - con.begin(); - // TODO: Check if resource (container) exists - log.warn("NOT CHECKING EXISTENCE OF <{}>", container); final String localName; if (StringUtils.isBlank(slug)) { @@ -193,21 +190,23 @@ public class LdpWebService { // Honor client wishes from Slug-header (Sec. 6.4.11) // http://www.ietf.org/rfc/rfc5023.txt log.trace("Slug-Header is '{}'", slug); - localName = LdpWebServiceUtils.urify(slug); + localName = LdpUtils.urify(slug); log.trace("Slug-Header urified: {}", localName); } String newResource = uriInfo.getRequestUriBuilder().path(localName).build().toString(); + conn.begin(); + log.trace("Checking possible name clash for new resource <{}>", newResource); - if (ldpService.exists(con, newResource)) { + if (ldpService.exists(conn, newResource)) { int i = 0; final String base = newResource; do { final String candidate = base + "-" + (++i); log.trace("<{}> already exists, trying <{}>", newResource, candidate); newResource = candidate; - } while (ldpService.exists(con, newResource)); + } while (ldpService.exists(conn, newResource)); log.debug("resolved name clash, new resource will be <{}>", newResource); } else { log.debug("no name clash for <{}>", newResource); @@ -215,25 +214,29 @@ public class LdpWebService { log.debug("POST to <{}> will create new LDP-R <{}>", container, newResource); + //checking if resource (container) exists is done later in the service try { - ldpService.addResource(con, container, newResource, type, postBody); - final Response.ResponseBuilder resp = createResponse(con, Response.Status.CREATED, container).location(java.net.URI.create(newResource)); - con.commit(); - return resp.build(); + String location = ldpService.addResource(conn, container, newResource, type, postBody); + final Response.ResponseBuilder response = createResponse(conn, Response.Status.CREATED, container).location(java.net.URI.create(location)); + if (newResource.compareTo(location) != 0) { + response.link(newResource, "describedby"); //Sec. 6.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15 + } + conn.commit(); + return response.build(); } catch (IOException | RDFParseException e) { - final Response.ResponseBuilder resp = createResponse(con, Response.Status.BAD_REQUEST, container).entity(e.getClass().getSimpleName() + ": " + e.getMessage()); - con.rollback(); + final Response.ResponseBuilder resp = createResponse(conn, Response.Status.BAD_REQUEST, container).entity(e.getClass().getSimpleName() + ": " + e.getMessage()); + conn.rollback(); return resp.build(); } catch (UnsupportedRDFormatException e) { - final Response.ResponseBuilder resp = createResponse(con, Response.Status.UNSUPPORTED_MEDIA_TYPE, container).entity(e); - con.rollback(); + final Response.ResponseBuilder resp = createResponse(conn, Response.Status.UNSUPPORTED_MEDIA_TYPE, container).entity(e); + conn.rollback(); return resp.build(); } } catch (final Throwable t) { - con.rollback(); + conn.rollback(); throw t; } finally { - con.close(); + conn.close(); } }
