You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@marmotta.apache.org by ja...@apache.org on 2014/03/28 09:13:15 UTC
git commit: MARMOTTA-462: Implemented support for LDP Implementation
Models: LDPR and LDPC (default)
Repository: marmotta
Updated Branches:
refs/heads/ldp f171cc1f6 -> ca37361e4
MARMOTTA-462: Implemented support for LDP Implementation Models: LDPR and LDPC (default)
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/ca37361e
Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/ca37361e
Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/ca37361e
Branch: refs/heads/ldp
Commit: ca37361e46175be062fc94e658554a73e4343cc4
Parents: f171cc1
Author: Jakob Frank <ja...@apache.org>
Authored: Thu Mar 27 16:28:23 2014 +0100
Committer: Jakob Frank <ja...@apache.org>
Committed: Fri Mar 28 09:13:04 2014 +0100
----------------------------------------------------------------------
.../marmotta/platform/ldp/api/LdpService.java | 77 ++++++++++++++++++++
.../InvalidInteractionModelException.java | 48 ++++++++++++
.../platform/ldp/services/LdpServiceImpl.java | 72 ++++++++++++++++--
.../platform/ldp/webservices/LdpWebService.java | 34 +++++++--
.../ldp/webservices/LdpWebServiceTest.java | 38 +++++++++-
5 files changed, 254 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/marmotta/blob/ca37361e/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 4795512..12cbbee 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
@@ -17,6 +17,8 @@
*/
package org.apache.marmotta.platform.ldp.api;
+import org.apache.marmotta.commons.vocabulary.LDP;
+import org.apache.marmotta.platform.ldp.exceptions.InvalidInteractionModelException;
import org.apache.marmotta.platform.ldp.exceptions.InvalidModificationException;
import org.apache.marmotta.platform.ldp.patch.InvalidPatchDocumentException;
import org.apache.marmotta.platform.ldp.patch.parser.ParseException;
@@ -29,6 +31,7 @@ import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParseException;
import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.Link;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -43,6 +46,41 @@ import java.util.List;
*/
public interface LdpService {
+
+
+ public static enum InteractionModel {
+ LDPR(LDP.Resource),
+ LDPC(LDP.Container);
+
+ private final URI uri;
+
+ InteractionModel(URI uri) {
+ this.uri = uri;
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ public static InteractionModel fromURI(String uri) {
+ if (LDP.Resource.stringValue().equals(uri)) {
+ return LDPR;
+ } else if (LDP.Container.stringValue().equals(uri)) {
+ return LDPC;
+ }
+ throw new IllegalArgumentException("Invalid Interaction Model URI: " + uri);
+ }
+
+ public static InteractionModel fromURI(URI uri){
+ if (uri == null) {
+ throw new IllegalArgumentException("Invalid Interaction Model: null");
+ } else {
+ return fromURI(uri.stringValue());
+ }
+ }
+
+ }
+
boolean exists(RepositoryConnection connection, String resource) throws RepositoryException;
boolean exists(RepositoryConnection connection, URI resource) throws RepositoryException;
@@ -79,6 +117,39 @@ public interface LdpService {
*/
String addResource(RepositoryConnection connection, URI container, URI resource, String 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 interactionModel the ldp interaction model
+ * @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, InteractionModel interactionModel, String 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 interactionModel the ldp interaction model
+ * @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, InteractionModel interactionModel, String type, InputStream stream) throws RepositoryException, IOException, RDFParseException;
+
+
List<Statement> getLdpTypes(RepositoryConnection connection, String resource) throws RepositoryException;
List<Statement> getLdpTypes(RepositoryConnection conn1, URI resource) throws RepositoryException;
@@ -125,4 +196,10 @@ public interface LdpService {
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/ca37361e/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidInteractionModelException.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidInteractionModelException.java b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidInteractionModelException.java
new file mode 100644
index 0000000..c083d48
--- /dev/null
+++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidInteractionModelException.java
@@ -0,0 +1,48 @@
+/*
+ * 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.exceptions;
+
+
+/**
+ * Invalid or unknown LDP Client Interaction Model Exception
+ *
+ * @see <a href="http://www.w3.org/TR/ldp/#h5_ldpc-post-createrdf">LDP Spec</a>
+ *
+ * @author Jakob Frank (jakob@apache.org)
+ */
+public class InvalidInteractionModelException extends Exception {
+
+ private final String href;
+
+ public InvalidInteractionModelException(String href) {
+ super(String.format("Invalid LDP Client Interaction Model: <%s>", href));
+ this.href = href;
+ }
+
+ public InvalidInteractionModelException(java.net.URI uri) {
+ this(uri.toASCIIString());
+ }
+
+ public InvalidInteractionModelException(org.openrdf.model.URI uri) {
+ this(uri.stringValue());
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/marmotta/blob/ca37361e/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 821436e..da4a302 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
@@ -26,6 +26,7 @@ import org.apache.marmotta.commons.vocabulary.LDP;
import org.apache.marmotta.platform.core.api.config.ConfigurationService;
import org.apache.marmotta.platform.ldp.api.LdpBinaryStoreService;
import org.apache.marmotta.platform.ldp.api.LdpService;
+import org.apache.marmotta.platform.ldp.exceptions.InvalidInteractionModelException;
import org.apache.marmotta.platform.ldp.exceptions.InvalidModificationException;
import org.apache.marmotta.platform.ldp.patch.InvalidPatchDocumentException;
import org.apache.marmotta.platform.ldp.patch.RdfPatchUtil;
@@ -46,6 +47,7 @@ import org.slf4j.LoggerFactory;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.core.EntityTag;
+import javax.ws.rs.core.Link;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -69,10 +71,11 @@ public class LdpServiceImpl implements LdpService {
@Inject
private LdpBinaryStoreService binaryStore;
- private final URI ldpContext;
+ private final URI ldpContext, ldpInteractionModelProperty;
public LdpServiceImpl() {
ldpContext = ValueFactoryImpl.getInstance().createURI(LDP.NAMESPACE);
+ ldpInteractionModelProperty = ValueFactoryImpl.getInstance().createURI(LDP.NAMESPACE, "interactionModel");
}
private URI buildURI(String resource) {
@@ -194,8 +197,8 @@ public class LdpServiceImpl implements LdpService {
// TODO: non-membership triples flag / Prefer-header
RDFWriter writer = Rio.createWriter(format, output);
UnionIteration<Statement, RepositoryException> union = new UnionIteration<>(
- connection.getStatements(null, null, null, false, resource),
- connection.getStatements(resource, null, null, false, ldpContext)
+ connection.getStatements(resource, null, null, false, ldpContext),
+ connection.getStatements(null, null, null, false, resource)
);
try {
LdpUtils.exportIteration(writer, resource, union);
@@ -237,14 +240,23 @@ public class LdpServiceImpl implements LdpService {
}
return null;
}
-
@Override
public String addResource(RepositoryConnection connection, String container, String resource, String type, InputStream stream) throws RepositoryException, IOException, RDFParseException {
- return addResource(connection, buildURI(container), buildURI(resource), type, stream);
+ return addResource(connection, buildURI(container), buildURI(resource), InteractionModel.LDPC, type, stream);
}
@Override
public String addResource(RepositoryConnection connection, URI container, URI resource, String type, InputStream stream) throws RepositoryException, IOException, RDFParseException {
+ return addResource(connection, container, resource, InteractionModel.LDPC, type, stream);
+ }
+
+ @Override
+ public String addResource(RepositoryConnection connection, String container, String resource, InteractionModel interactionModel, String type, InputStream stream) throws RepositoryException, IOException, RDFParseException {
+ return addResource(connection, buildURI(container), buildURI(resource), interactionModel, type, stream);
+ }
+
+ @Override
+ public String addResource(RepositoryConnection connection, URI container, URI resource, InteractionModel interactionModel, String type, InputStream stream) throws RepositoryException, IOException, RDFParseException {
ValueFactory valueFactory = connection.getValueFactory();
// Add container triples (Sec. 5.2.3.2)
@@ -265,6 +277,7 @@ public class LdpServiceImpl implements LdpService {
connection.add(resource, RDF.TYPE, LDP.Resource, ldpContext);
connection.add(resource, RDF.TYPE, LDP.RDFSource, ldpContext);
+ connection.add(resource, ldpInteractionModelProperty, interactionModel.getUri(), ldpContext);
connection.add(resource, DCTERMS.created, now, ldpContext);
connection.add(resource, DCTERMS.modified, now, ldpContext);
@@ -272,8 +285,8 @@ public class LdpServiceImpl implements LdpService {
final RDFFormat rdfFormat = Rio.getParserFormatForMIMEType(type);
if (rdfFormat == null) {
log.debug("POST creates new LDP-NR, because no suitable RDF parser found for type {}", type);
- Literal format = valueFactory.createLiteral(type);
- URI binaryResource = valueFactory.createURI(resource.stringValue() + LdpUtils.getExtension(type));
+ final Literal format = valueFactory.createLiteral(type);
+ final URI binaryResource = valueFactory.createURI(resource.stringValue() + LdpUtils.getExtension(type));
connection.add(container, LDP.contains, binaryResource, ldpContext);
@@ -447,4 +460,49 @@ public class LdpServiceImpl implements LdpService {
return true;
}
+ @Override
+ public InteractionModel getInteractionModel(List<Link> linkHeaders) throws InvalidInteractionModelException {
+ if (log.isTraceEnabled()) {
+ log.trace("Checking Link-Headers for LDP Interaction Models");
+ for (Link link: linkHeaders) {
+ log.trace(" - {}", link);
+ }
+ }
+ for (Link link: linkHeaders) {
+ if ("type".equalsIgnoreCase(link.getRel())) {
+ final String href = link.getUri().toASCIIString();
+ if (LDP.Resource.stringValue().equals(href)) {
+ log.debug("LDPR Interaction Model detected");
+ return InteractionModel.LDPR;
+ } else if (LDP.Resource.stringValue().equals(href)) {
+ log.debug("LDPC Interaction Model detected");
+ return InteractionModel.LDPC;
+ } else {
+ log.debug("Invalid/Unknown LDP Interaction Model: {}", href);
+ throw new InvalidInteractionModelException(href);
+ }
+ }
+ }
+ log.debug("No LDP Interaction Model specified, defaulting to {}", InteractionModel.LDPC);
+ // Default Interaction Model is LDPC
+ return InteractionModel.LDPC;
+ }
+
+ @Override
+ public InteractionModel getInteractionModel(RepositoryConnection connection, String resource) throws RepositoryException {
+ return getInteractionModel(connection, buildURI(resource));
+ }
+
+ @Override
+ public InteractionModel getInteractionModel(RepositoryConnection connection, URI uri) throws RepositoryException {
+ if (connection.hasStatement(uri, ldpInteractionModelProperty, InteractionModel.LDPC.getUri(), true, ldpContext)) {
+ return InteractionModel.LDPC;
+ } else if (connection.hasStatement(uri, ldpInteractionModelProperty, InteractionModel.LDPR.getUri(), true, ldpContext)) {
+ return InteractionModel.LDPR;
+ }
+
+ log.info("No LDP Interaction Model specified for <{}>, defaulting to {}", uri.stringValue(), InteractionModel.LDPC);
+ // Default Interaction Model is LDPC
+ return InteractionModel.LDPC;
+ }
}
http://git-wip-us.apache.org/repos/asf/marmotta/blob/ca37361e/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 66516fd..81dedf1 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
@@ -22,6 +22,7 @@ import org.apache.marmotta.commons.vocabulary.LDP;
import org.apache.marmotta.platform.core.api.config.ConfigurationService;
import org.apache.marmotta.platform.core.api.triplestore.SesameService;
import org.apache.marmotta.platform.ldp.api.LdpService;
+import org.apache.marmotta.platform.ldp.exceptions.InvalidInteractionModelException;
import org.apache.marmotta.platform.ldp.exceptions.InvalidModificationException;
import org.apache.marmotta.platform.ldp.patch.InvalidPatchDocumentException;
import org.apache.marmotta.platform.ldp.patch.parser.ParseException;
@@ -194,6 +195,7 @@ public class LdpWebService {
*/
@POST
public Response POST(@Context UriInfo uriInfo, @HeaderParam("Slug") String slug,
+ @HeaderParam("Link") List<Link> linkHeaders,
InputStream postBody, @HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType type)
throws RepositoryException {
@@ -204,7 +206,16 @@ public class LdpWebService {
try {
conn.begin();
- // TODO: Check the LDP-Interaction Model (Sec. 5.2.3.4 and Sec. 4.2.1.4)
+ // Check that the target container supports the LDPC Interaction Model
+ final LdpService.InteractionModel containerModel = ldpService.getInteractionModel(conn, container);
+ if (containerModel != LdpService.InteractionModel.LDPC) {
+ final Response.ResponseBuilder response = createResponse(conn, Response.Status.METHOD_NOT_ALLOWED, container);
+ conn.commit();
+ return response.entity(String.format("%s only supports %s Interaction Model", container, containerModel)).build();
+ }
+
+ // Get the LDP-Interaction Model (Sec. 5.2.3.4 and Sec. 4.2.1.4)
+ final LdpService.InteractionModel ldpInteractionModel = ldpService.getInteractionModel(linkHeaders);
final String localName;
if (StringUtils.isBlank(slug)) {
@@ -246,7 +257,7 @@ public class LdpWebService {
final String mimeType = LdpUtils.getMimeType(type);
//checking if resource (container) exists is done later in the service
try {
- String location = ldpService.addResource(conn, container, newResource, mimeType, postBody);
+ String location = ldpService.addResource(conn, container, newResource, ldpInteractionModel, 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. 5.2.3.12, see also http://www.w3.org/2012/ldp/track/issues/15
@@ -262,6 +273,11 @@ public class LdpWebService {
conn.rollback();
return resp.build();
}
+ } catch (InvalidInteractionModelException e) {
+ log.debug("POST with invalid interaction model <{}> to <{}>", e.getHref(), container);
+ final Response.ResponseBuilder response = createResponse(conn, Response.Status.BAD_REQUEST, container);
+ conn.commit();
+ return response.entity(e.getMessage()).build();
} catch (final Throwable t) {
conn.rollback();
throw t;
@@ -438,11 +454,15 @@ public class LdpWebService {
// 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. 5.2.3
- // TODO: LDP Interaction Model!
- builder.header("Accept-Post", LdpUtils.getAcceptPostHeader("*/*"));
+ if (ldpService.getInteractionModel(con, resource) == LdpService.InteractionModel.LDPR) {
+ // Sec. 4.2.8.2
+ builder.allow("GET", "HEAD", "PATCH", "OPTIONS");
+ } else {
+ // Sec. 4.2.8.2
+ builder.allow("GET", "HEAD", "POST", "PATCH", "OPTIONS");
+ // Sec. 4.2.3 / Sec. 5.2.3
+ builder.header("Accept-Post", LdpUtils.getAcceptPostHeader("*/*"));
+ }
// Sec. 4.2.7.1
builder.header("Accept-Patch", RdfPatchParser.MIME_TYPE);
}
http://git-wip-us.apache.org/repos/asf/marmotta/blob/ca37361e/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/webservices/LdpWebServiceTest.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/webservices/LdpWebServiceTest.java b/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/webservices/LdpWebServiceTest.java
index f110946..47ecd8e 100644
--- a/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/webservices/LdpWebServiceTest.java
+++ b/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/webservices/LdpWebServiceTest.java
@@ -37,6 +37,7 @@ import org.openrdf.rio.RDFFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.ws.rs.core.Link;
import javax.ws.rs.core.UriBuilder;
import java.io.IOException;
import java.net.URISyntaxException;
@@ -273,7 +274,42 @@ public class LdpWebServiceTest {
assertEquals("md5sum",expectedMD5, HashUtils.md5sum(data));
}
- @AfterClass
+ @Test
+ public void testInteractionModel() throws Exception {
+ final String container = baseUrl+LdpWebService.PATH + "/iam";
+
+ // Try LDPR
+ final String ldpr = RestAssured
+ .given()
+ .header("Link", Link.fromUri(LDP.Resource.stringValue()).rel("type").build().toString())
+ .body(testResourceTTL.getBytes())
+ .contentType(RDFFormat.TURTLE.getDefaultMIMEType())
+ .expect()
+ .statusCode(201)
+ .post(container)
+ .getHeader("Location");
+
+ // Now POSTing to the ldpr should fail
+ RestAssured
+ .given()
+ .body(testResourceTTL.getBytes())
+ .contentType(RDFFormat.TURTLE.getDefaultMIMEType())
+ .expect()
+ .statusCode(405)
+ .post(ldpr);
+
+ // Try an invalid interaction model
+ RestAssured
+ .given()
+ .header("Link", Link.fromUri(baseUrl).rel("type").build().toString())
+ .body(testResourceTTL.getBytes())
+ .contentType(RDFFormat.TURTLE.getDefaultMIMEType())
+ .expect()
+ .statusCode(400)
+ .post(container);
+ }
+
+ @AfterClass
public static void tearDown() {
marmotta.shutdown();
}