MARMOTTA-440: Continued alignment with w3c ldp working draft
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/da1e8827 Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/da1e8827 Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/da1e8827 Branch: refs/heads/develop Commit: da1e8827f78c8c0c24bd95f142146886a3e3df60 Parents: bb13208 Author: Jakob Frank <[email protected]> Authored: Tue Mar 11 17:03:17 2014 +0100 Committer: Jakob Frank <[email protected]> Committed: Tue Mar 11 17:03:17 2014 +0100 ---------------------------------------------------------------------- .../apache/marmotta/commons/vocabulary/LDP.java | 31 ++---- .../platform/ldp/api/LdpBinaryStoreService.java | 3 + .../marmotta/platform/ldp/api/LdpService.java | 11 ++ .../ldp/services/LdpBinaryStoreServiceImpl.java | 16 +++ .../platform/ldp/services/LdpServiceImpl.java | 106 ++++++++++++++----- .../platform/ldp/webservices/LdpWebService.java | 50 +++++++-- 6 files changed, 158 insertions(+), 59 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/da1e8827/commons/marmotta-sesame-tools/marmotta-model-vocabs/src/main/java/org/apache/marmotta/commons/vocabulary/LDP.java ---------------------------------------------------------------------- diff --git a/commons/marmotta-sesame-tools/marmotta-model-vocabs/src/main/java/org/apache/marmotta/commons/vocabulary/LDP.java b/commons/marmotta-sesame-tools/marmotta-model-vocabs/src/main/java/org/apache/marmotta/commons/vocabulary/LDP.java index 7357b96..aa17814 100644 --- a/commons/marmotta-sesame-tools/marmotta-model-vocabs/src/main/java/org/apache/marmotta/commons/vocabulary/LDP.java +++ b/commons/marmotta-sesame-tools/marmotta-model-vocabs/src/main/java/org/apache/marmotta/commons/vocabulary/LDP.java @@ -30,17 +30,9 @@ public class LDP { public static final String PREFIX = "ldp"; /** - * A Linked Data Platform Container (LDPC) that also conforms to - * additional patterns and conventions for managing members. It is distinguished from - * CompositeContainer by the following behaviors: - * <ol> - * <li>Clients cannot assume that an AggregateContainer, when deleted, deletes its members. - * </ol> - * <p/> - * While every attempt is made to be complete in this list, readers should also refer - * to the specification defining this ontology. + * FIXME: Not yet part of the official vocab, but used in the Spec. (2014-03-11) */ - public static final URI AggregateContainer; + public static final URI DirectContainer; /** * FIXME: Not yet part of the official vocab, but used in the Spec. (2014-02-18) @@ -48,17 +40,9 @@ public class LDP { public static final URI BasicContainer; /** - * A Linked Data Platform Container (LDPC) that also conforms to - * additional patterns and conventions for managing members. It is distinguished from - * AggregateContainer by the following behaviors: - * <ol> - * <li>An CompositeContainer, when deleted, must delete all its members. - * </ol> - * <p/> - * While every attempt is made to be complete in this list, readers should also - * refer to the specification defining this ontology. + * FIXME: Not yet part of the official vocab, but used in the Spec. (2014-03-11) */ - public static final URI CompositeContainer; + public static final URI IndirectContainer; /** * A Linked Data Platform Resource (LDPR) that also conforms to @@ -86,6 +70,7 @@ public class LDP { /** * A HTTP-addressable resource with a Non-RDF Source representation. + * FIXME: Not yet part of the vocab, but used in the spec. (2014-03-11) */ public static final URI NonRdfResource; @@ -127,9 +112,9 @@ public class LDP { static { ValueFactory factory = ValueFactoryImpl.getInstance(); - AggregateContainer = factory.createURI(LDP.NAMESPACE, "AggregateContainer"); - BasicContainer = factory.createURI(LDP.NAMESPACE, "BasicContainer"); - CompositeContainer = factory.createURI(LDP.NAMESPACE, "CompositeContainer"); + DirectContainer = factory.createURI(LDP.NAMESPACE, "DirectContainer"); //TODO: missing term in the vocab + BasicContainer = factory.createURI(LDP.NAMESPACE, "BasicContainer"); //TODO: missing term in the vocab + IndirectContainer = factory.createURI(LDP.NAMESPACE, "IndirectContainer"); //TODO: missing term in the vocab Container = factory.createURI(LDP.NAMESPACE, "Container"); Page = factory.createURI(LDP.NAMESPACE, "Page"); Resource = factory.createURI(LDP.NAMESPACE, "Resource"); http://git-wip-us.apache.org/repos/asf/marmotta/blob/da1e8827/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpBinaryStoreService.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpBinaryStoreService.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpBinaryStoreService.java index 34ec302..6e8f46a 100644 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpBinaryStoreService.java +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/api/LdpBinaryStoreService.java @@ -38,4 +38,7 @@ public interface LdpBinaryStoreService { InputStream read(URI resource) throws IOException; + String getHash(String resource); + + String getHash(URI uri); } http://git-wip-us.apache.org/repos/asf/marmotta/blob/da1e8827/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 f998028..4795512 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 @@ -111,7 +111,18 @@ public interface LdpService { String getMimeType(RepositoryConnection connection, URI uri) throws RepositoryException; + boolean isNonRdfSourceResource(RepositoryConnection connection, String resource) throws RepositoryException; + + boolean isNonRdfSourceResource(RepositoryConnection connection, URI uri) throws RepositoryException; + URI getRdfSourceForNonRdfSource(RepositoryConnection connection, URI uri) throws RepositoryException; URI getRdfSourceForNonRdfSource(RepositoryConnection connection, String resource) throws RepositoryException; + + boolean isRdfSourceResource(RepositoryConnection connection, String resource) throws RepositoryException; + + boolean isRdfSourceResource(RepositoryConnection connection, URI uri) throws RepositoryException; + + URI getNonRdfSourceForRdfSource(RepositoryConnection connection, String resource) throws RepositoryException; + URI getNonRdfSourceForRdfSource(RepositoryConnection connection, URI uri) throws RepositoryException; } http://git-wip-us.apache.org/repos/asf/marmotta/blob/da1e8827/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpBinaryStoreServiceImpl.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpBinaryStoreServiceImpl.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpBinaryStoreServiceImpl.java index 01a305f..09f34c6 100644 --- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpBinaryStoreServiceImpl.java +++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/services/LdpBinaryStoreServiceImpl.java @@ -19,6 +19,7 @@ package org.apache.marmotta.platform.ldp.services; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.apache.marmotta.commons.util.HashUtils; import org.apache.marmotta.platform.core.api.config.ConfigurationService; import org.apache.marmotta.platform.core.events.ConfigurationChangedEvent; import org.apache.marmotta.platform.core.events.SystemStartupEvent; @@ -113,4 +114,19 @@ public class LdpBinaryStoreServiceImpl implements LdpBinaryStoreService { return read(resource.stringValue()); } + + @Override + public String getHash(String resource) { + try(InputStream is = Files.newInputStream(getFile(resource))) { + return HashUtils.md5sum(is); + } catch (URISyntaxException | IOException e) { + log.error("Error calculating file-md5 of {}: {}", resource, e); + return null; + } + } + + @Override + public String getHash(URI uri) { + return getHash(uri.stringValue()); + } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/da1e8827/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 4818d19..54350d4 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 @@ -101,18 +101,38 @@ public class LdpServiceImpl implements LdpService { @Override public List<Statement> getLdpTypes(RepositoryConnection connection, URI resource) throws RepositoryException { - return Iterations.asList(new FilterIteration<Statement, RepositoryException>(connection.getStatements(resource, RDF.TYPE, null, false, ldpContext)) { - @Override - protected boolean accept(Statement statement) { - final Value object = statement.getObject(); - return object instanceof URI && object.stringValue().startsWith(LDP.NAMESPACE); - } - }); //FIXME + return Iterations.asList(new FilterIteration<Statement, RepositoryException>(connection.getStatements(resource, RDF.TYPE, null, false, ldpContext)) { + @Override + protected boolean accept(Statement statement) { + final Value object = statement.getObject(); + return object instanceof URI && object.stringValue().startsWith(LDP.NAMESPACE); + } + }); //FIXME + } + + @Override + public boolean isRdfSourceResource(RepositoryConnection connection, String resource) throws RepositoryException { + return isRdfSourceResource(connection, buildURI(resource)); + } + + @Override + public boolean isRdfSourceResource(RepositoryConnection connection, URI uri) throws RepositoryException { + return connection.hasStatement(uri, RDF.TYPE, LDP.RDFSource, true, ldpContext); + } + + @Override + public boolean isNonRdfSourceResource(RepositoryConnection connection, String resource) throws RepositoryException { + return isRdfSourceResource(connection, buildURI(resource)); + } + + @Override + public boolean isNonRdfSourceResource(RepositoryConnection connection, URI uri) throws RepositoryException { + return connection.hasStatement(uri, RDF.TYPE, LDP.NonRdfResource, true, ldpContext); } + @Override public URI getRdfSourceForNonRdfSource(final RepositoryConnection connection, URI uri) throws RepositoryException { - // FIXME: someone should double check this (jakob) final FilterIteration<Statement, RepositoryException> it = new FilterIteration<Statement, RepositoryException>(connection.getStatements(uri, DCTERMS.isFormatOf, null, true, ldpContext)) { @Override @@ -138,6 +158,32 @@ public class LdpServiceImpl implements LdpService { } @Override + public URI getNonRdfSourceForRdfSource(RepositoryConnection connection, String resource) throws RepositoryException { + return getNonRdfSourceForRdfSource(connection, buildURI(resource)); + } + + @Override + public URI getNonRdfSourceForRdfSource(final RepositoryConnection connection, URI uri) throws RepositoryException { + final FilterIteration<Statement, RepositoryException> it = + new FilterIteration<Statement, RepositoryException>(connection.getStatements(uri, DCTERMS.hasFormat, null, true, ldpContext)) { + @Override + protected boolean accept(Statement statement) throws RepositoryException { + return statement.getObject() instanceof URI + && connection.hasStatement((URI) statement.getObject(), RDF.TYPE, LDP.NonRdfResource, true, ldpContext); + } + }; + try { + if (it.hasNext()) { + return (URI) it.next().getObject(); + } else { + return null; + } + }finally { + it.close(); + } + } + + @Override public void exportResource(RepositoryConnection connection, String resource, OutputStream output, RDFFormat format) throws RepositoryException, RDFHandlerException { exportResource(connection, buildURI(resource), output, format); } @@ -209,6 +255,7 @@ public class LdpServiceImpl implements LdpService { if (hasType(connection, container, LDP.BasicContainer)) { connection.remove(container, DCTERMS.modified, null, ldpContext); } else { + connection.add(container, RDF.TYPE, LDP.Container, ldpContext); connection.add(container, RDF.TYPE, LDP.BasicContainer, ldpContext); } @@ -260,26 +307,35 @@ public class LdpServiceImpl implements LdpService { @Override public EntityTag generateETag(RepositoryConnection connection, URI uri) throws RepositoryException { - final RepositoryResult<Statement> stmts = connection.getStatements(uri, DCTERMS.modified, null, true, ldpContext); - try { - // TODO: ETag is the last-modified date (explicitly managed) thus only weak. - Date latest = null; - while (stmts.hasNext()) { - Value o = stmts.next().getObject(); - if (o instanceof Literal) { - Date d = ((Literal)o).calendarValue().toGregorianCalendar().getTime(); - if (latest == null || d.after(latest)) { - latest = d; - } - } - } - if (latest != null) { - return new EntityTag(String.valueOf(latest.getTime()), true); + if (isNonRdfSourceResource(connection, uri)) { + final String hash = binaryStore.getHash(uri.stringValue()); + if (hash != null) { + return new EntityTag(hash, false); } else { return null; } - } finally { - stmts.close(); + } else { + final RepositoryResult<Statement> stmts = connection.getStatements(uri, DCTERMS.modified, null, true, ldpContext); + try { + // TODO: ETag is the last-modified date (explicitly managed) thus only weak. + Date latest = null; + while (stmts.hasNext()) { + Value o = stmts.next().getObject(); + if (o instanceof Literal) { + Date d = ((Literal)o).calendarValue().toGregorianCalendar().getTime(); + if (latest == null || d.after(latest)) { + latest = d; + } + } + } + if (latest != null) { + return new EntityTag(String.valueOf(latest.getTime()), true); + } else { + return null; + } + } finally { + stmts.close(); + } } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/da1e8827/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 c4f68d3..8bf49b4 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 @@ -110,7 +110,17 @@ public class LdpWebService { // TODO: Proper content negotiation - final RDFFormat format = Rio.getWriterFormatForMIMEType(type.toString()); + final RDFFormat format; + if (type.isWildcardType()) { // No explicit Accept Header + if (ldpService.isRdfSourceResource(conn, resource)) { + format = RDFFormat.TURTLE; + } else { + format = null; + } + } else { + format = Rio.getWriterFormatForMIMEType(LdpUtils.getMimeType(type), null); + } + if (format == null) { log.debug("GET to <{}> with non-RDF format {}, so looking for a LDP-BR", resource, type); final StreamingOutput entity = new StreamingOutput() { @@ -208,6 +218,13 @@ public class LdpWebService { conn.begin(); + if (ldpService.isNonRdfSourceResource(conn, container)) { + log.info("POSTing to a NonRdfSource is not allowed ({})", container); + final Response.ResponseBuilder response = createResponse(conn, Response.Status.METHOD_NOT_ALLOWED, container).entity("POST to NonRdfSource is not allowed\n"); + conn.commit(); + return response.build(); + } + log.trace("Checking possible name clash for new resource <{}>", newResource); if (ldpService.exists(conn, newResource)) { int i = 0; @@ -229,7 +246,7 @@ public class LdpWebService { String location = ldpService.addResource(conn, container, newResource, mimeType, 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"); //FIXME: Sec. 6.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15 + response.link(newResource, "describedby"); //FIXME: Sec. 5.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15 } conn.commit(); return response.build(); @@ -414,14 +431,18 @@ public class LdpWebService { Response.ResponseBuilder builder = createResponse(con, Response.Status.OK, resource); - // Sec. 4.2.8.2 - builder.allow("GET", "HEAD", "POST", "PATCH", "OPTIONS"); - - // Sec. 4.2.3 / Sec. ?? - builder.header("Accept-Post", "text/turtle, */*"); - - // Sec. 4.2.7.1 - builder.header("Accept-Patch", RdfPatchParser.MIME_TYPE); + if (ldpService.isNonRdfSourceResource(con, resource)) { + // Sec. 4.2.8.2 + builder.allow("GET", "HEAD", "OPTIONS"); + } else if (ldpService.isRdfSourceResource(con, resource)) { + // Sec. 4.2.8.2 + builder.allow("GET", "HEAD", "POST", "PATCH", "OPTIONS"); + // Sec. 4.2.3 / Sec. ?? + // TODO: LDP Interaction Model! + builder.header("Accept-Post", "text/turtle, */*"); + // Sec. 4.2.7.1 + builder.header("Accept-Patch", RdfPatchParser.MIME_TYPE); + } con.commit(); return builder.build(); @@ -469,7 +490,14 @@ public class LdpWebService { final URI rdfSource = ldpService.getRdfSourceForNonRdfSource(connection, resource); if (rdfSource != null) { - rb.link(rdfSource.stringValue(), "describedby"); //FIXME: Sec. 6.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15 + // FIXME: Sec. 5.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15 + rb.link(rdfSource.stringValue(), "meta"); + rb.link(rdfSource.stringValue(), "describedby"); + } + final URI nonRdfSource = ldpService.getNonRdfSourceForRdfSource(connection, resource); + if (nonRdfSource != null) { + // TODO: Propose to LDP-WG? + rb.link(nonRdfSource.stringValue(), "content"); } // ETag (Sec. 5.2.7)
