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/09/10 11:39:02 UTC
[11/17] git commit: MARMOTT-514: Only one test is now failing,
and that one is only a SHOULD!
MARMOTT-514: Only one test is now failing, and that one is only a SHOULD!
This solves MARMOTTA-515, MARMOTTA-516, MARMOTTA-517, MARMOTTA-518, MARMOTTA-519, MARMOTTA-520
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/30227f31
Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/30227f31
Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/30227f31
Branch: refs/heads/ldp
Commit: 30227f31c12babe6716bc3e43df68b3aba53a20a
Parents: 13d8e64
Author: Jakob Frank <ja...@apache.org>
Authored: Fri Sep 5 09:55:41 2014 +0200
Committer: Jakob Frank <ja...@apache.org>
Committed: Fri Sep 5 10:22:48 2014 +0200
----------------------------------------------------------------------
.../commons/http/MarmottaHttpUtils.java | 2 +
.../platform/ldp/services/LdpServiceImpl.java | 14 +-
.../marmotta/platform/ldp/util/LdpUtils.java | 24 +-
.../platform/ldp/webservices/LdpWebService.java | 371 +++++++++++++------
4 files changed, 282 insertions(+), 129 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/marmotta/blob/30227f31/commons/marmotta-commons/src/main/java/org/apache/marmotta/commons/http/MarmottaHttpUtils.java
----------------------------------------------------------------------
diff --git a/commons/marmotta-commons/src/main/java/org/apache/marmotta/commons/http/MarmottaHttpUtils.java b/commons/marmotta-commons/src/main/java/org/apache/marmotta/commons/http/MarmottaHttpUtils.java
index 568829d..d1e070c 100644
--- a/commons/marmotta-commons/src/main/java/org/apache/marmotta/commons/http/MarmottaHttpUtils.java
+++ b/commons/marmotta-commons/src/main/java/org/apache/marmotta/commons/http/MarmottaHttpUtils.java
@@ -105,6 +105,8 @@ public class MarmottaHttpUtils {
public static ContentType parseContentType(String c) {
+ if (StringUtils.isBlank(c)) return null;
+
String mt[] = c.split(";");
String[] tst = mt[0].split("/");
http://git-wip-us.apache.org/repos/asf/marmotta/blob/30227f31/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 c592dea..cdb48cc 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
@@ -108,7 +108,7 @@ public class LdpServiceImpl implements LdpService {
uriBuilder.path(uriInfo.getPathParameters().getFirst("local"));
// uriBuilder.path(uriInfo.getPath().replaceFirst("/$", ""));
String uri = uriBuilder.build().toString();
- log.debug("Request URI: {}", uri);
+ log.debug("=== Request URI: {}", uri);
return uri;
}
@@ -197,7 +197,7 @@ public class LdpServiceImpl implements LdpService {
} else {
return null;
}
- }finally {
+ } finally {
it.close();
}
}
@@ -228,7 +228,7 @@ public class LdpServiceImpl implements LdpService {
} else {
return null;
}
- }finally {
+ } finally {
it.close();
}
}
@@ -323,6 +323,7 @@ public class LdpServiceImpl implements LdpService {
connection.add(container, DCTERMS.modified, now, ldpContext);
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);
@@ -331,9 +332,11 @@ public class LdpServiceImpl implements LdpService {
// TODO: find a better way to ingest n-triples (text/plain) while still supporting regular text files
final RDFFormat rdfFormat = ("text/plain".equals(type) ? null : Rio.getParserFormatForMIMEType(type));
if (rdfFormat == null) {
- log.debug("POST creates new LDP-NR, because no suitable RDF parser found for type {}", type);
+ log.debug("Creating new LDP-NR, because no suitable RDF parser found for type {}", type);
final Literal format = valueFactory.createLiteral(type);
final URI binaryResource = valueFactory.createURI(resource.stringValue() + LdpUtils.getExtension(type));
+ log.debug("LDP-NR is <{}>", binaryResource);
+ log.debug("Corresponding LDP-RS is <{}>", resource);
connection.add(container, LDP.contains, binaryResource, ldpContext);
@@ -354,8 +357,7 @@ public class LdpServiceImpl implements LdpService {
return binaryResource.stringValue();
} else {
- log.debug("POST creates new LDP-RS, data provided as {}", rdfFormat.getName());
- connection.add(resource, RDF.TYPE, LDP.RDFSource, ldpContext);
+ log.debug("Creating new LDP-RS, data provided as {}", rdfFormat.getName());
connection.add(container, LDP.contains, resource, ldpContext);
// FIXME: We are (are we?) allowed to filter out server-managed properties here
http://git-wip-us.apache.org/repos/asf/marmotta/blob/30227f31/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 4ec9fe7..9a81086 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
@@ -33,6 +33,7 @@ import org.openrdf.rio.RDFFormat;
import org.openrdf.rio.RDFHandlerException;
import org.openrdf.rio.RDFParserRegistry;
import org.openrdf.rio.RDFWriter;
+import org.slf4j.Logger;
import javax.ws.rs.core.MediaType;
import java.io.File;
@@ -48,6 +49,8 @@ import java.util.Set;
*/
public class LdpUtils {
+ private static Logger log = org.slf4j.LoggerFactory.getLogger(LdpUtils.class);
+
/**
* Urify the Slug: header value, i.e. replace all non-url chars with a single dash.
*
@@ -78,12 +81,19 @@ public class LdpUtils {
* @return file extension (already including '.')
*/
public static String getExtension(String mimeType) {
- MimeTypes allTypes = MimeTypes.getDefaultMimeTypes();
+ final String defaultExt = ".bin";
+ final MimeTypes allTypes = MimeTypes.getDefaultMimeTypes();
try {
- return allTypes.forName(mimeType).getExtension();
+ final String ext = allTypes.forName(mimeType).getExtension();
+ log.trace("Tika's file-extension for {} is '{}'", mimeType, ext);
+ if (StringUtils.isNotBlank(ext)) {
+ return ext;
+ }
} catch (MimeTypeException e) {
- return null; //FIXME
+ log.trace("MimeTypeException: {}. Not critical, recovering...", e.getMessage());
}
+ log.trace("Using fallback file-extension '{}' for {}", defaultExt, mimeType);
+ return defaultExt;
}
/**
@@ -137,14 +147,18 @@ public class LdpUtils {
return sb.toString();
}
- public static URI getContainer(String resource) throws MalformedURLException, URISyntaxException {
+ public static String 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());
+ return parent.toASCIIString();
}
public static URI getContainer(URI resource) throws MalformedURLException, URISyntaxException {
return new URIImpl(resource.getNamespace());
}
+ private LdpUtils() {
+ // Static access only
+ }
+
}
http://git-wip-us.apache.org/repos/asf/marmotta/blob/30227f31/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 7a8e772..29c9ca9 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
@@ -43,7 +43,6 @@ import org.openrdf.repository.RepositoryException;
import org.openrdf.rio.*;
import org.slf4j.Logger;
-import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
@@ -53,6 +52,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.UUID;
@@ -85,6 +86,39 @@ public class LdpWebService {
@Inject
private SesameService sesameService;
+ private final List<ContentType> producedRdfTypes;
+
+ public LdpWebService() {
+ producedRdfTypes = new ArrayList<>();
+
+ for(RDFFormat format : RDFWriterRegistry.getInstance().getKeys()) {
+ final String primaryQ;
+ if (format == RDFFormat.TURTLE) {
+ primaryQ = ";q=1.0";
+ } else if (format == RDFFormat.JSONLD) {
+ primaryQ = ";q=0.9";
+ } else if (format == RDFFormat.RDFXML) {
+ primaryQ = ";q=0.8";
+ } else {
+ primaryQ = ";q=0.5";
+ }
+ final String secondaryQ = ";q=0.3";
+ final List<String> mimeTypes = format.getMIMETypes();
+ for (int i = 0; i < mimeTypes.size(); i++) {
+ final String mime = mimeTypes.get(i);
+ if (i == 0) {
+ // first mimetype is the default
+ producedRdfTypes.add(MarmottaHttpUtils.parseContentType(mime + primaryQ));
+ } else {
+ producedRdfTypes.add(MarmottaHttpUtils.parseContentType(mime + secondaryQ));
+ }
+ }
+ }
+ Collections.sort(producedRdfTypes);
+
+ log.debug("Available RDF Serializer: {}", producedRdfTypes);
+ }
+
protected void initialize(@Observes SesameStartupEvent event) {
log.info("Starting up LDP WebService Endpoint");
String root = UriBuilder.fromUri(configurationService.getBaseUri()).path(LdpWebService.PATH).build().toASCIIString();
@@ -105,23 +139,25 @@ public class LdpWebService {
@GET
public Response GET(@Context final UriInfo uriInfo, @Context Request r,
- @HeaderParam(HttpHeaders.ACCEPT) @DefaultValue(MediaType.WILDCARD) MediaType type)
+ @HeaderParam(HttpHeaders.ACCEPT) @DefaultValue(MediaType.WILDCARD) String type)
throws RepositoryException {
final String resource = ldpService.getResourceUri(uriInfo);
log.debug("GET to LDPR <{}>", resource);
- return buildGetResponse(resource, r, type).build();
+ return buildGetResponse(resource, r, MarmottaHttpUtils.parseAcceptHeader(type)).build();
}
@HEAD
public Response HEAD(@Context final UriInfo uriInfo, @Context Request r,
- @HeaderParam(HttpHeaders.ACCEPT) @DefaultValue(MediaType.WILDCARD) MediaType type)
+ @HeaderParam(HttpHeaders.ACCEPT) @DefaultValue(MediaType.WILDCARD) String type)
throws RepositoryException {
final String resource = ldpService.getResourceUri(uriInfo);
log.debug("HEAD to LDPR <{}>", resource);
- return buildGetResponse(resource, r, type).entity(null).build();
+ return buildGetResponse(resource, r, MarmottaHttpUtils.parseAcceptHeader(type)).entity(null).build();
}
- private Response.ResponseBuilder buildGetResponse(final String resource, Request r, MediaType type) throws RepositoryException {
+ private Response.ResponseBuilder buildGetResponse(final String resource, Request r, List<ContentType> acceptedContentTypes) throws RepositoryException {
+ log.trace("LDPR requested media type {}", acceptedContentTypes);
+ MediaType type = MediaType.valueOf(acceptedContentTypes.get(0).toString());
final RepositoryConnection conn = sesameService.getConnection();
try {
conn.begin();
@@ -136,75 +172,59 @@ public class LdpWebService {
log.trace("{} exists, continuing", resource);
}
- final RDFFormat format;
- final RDFFormat fallback = (ldpService.isNonRdfSourceResource(conn, resource) ? null : RDFFormat.TURTLE);
- final String mimeType = LdpUtils.getMimeType(type);
- if (StringUtils.isBlank(mimeType) || "text/plain".equals(mimeType)) {
- // TODO: find a better way to support n-triples (text/plain)
- // while still supporting regular text files
- format = null;
- } else if (type.isWildcardSubtype() && "text".equals(type.getType())) {
- format = RDFFormat.TURTLE;
- } else {
- ContentType contentType = MarmottaHttpUtils.performContentNegotiation(mimeType, exportService.getProducedTypes());
- format = (contentType != null ? Rio.getWriterFormatForMIMEType(contentType.getMime(), fallback) : fallback);
- }
-
- if (format == null) {
- log.debug("GET to <{}> with non-RDF format {} of a LDP-NR", resource, type);
- final StreamingOutput entity = new StreamingOutput() {
- @Override
- public void write(OutputStream out) throws IOException, WebApplicationException {
- try {
- final RepositoryConnection outputConn = sesameService.getConnection();
- try {
- outputConn.begin();
- ldpService.exportBinaryResource(outputConn, resource, out);
- outputConn.commit();
- } catch (RepositoryException | IOException e) {
- outputConn.rollback();
- throw new WebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e).build());
- } finally {
- outputConn.close();
- }
- } catch (RepositoryException e) {
- throw new WebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e).build());
+ // Content-Neg
+ if (ldpService.isNonRdfSourceResource(conn, resource)) {
+ log.trace("<{}> is marked as LDP-NR", resource);
+ // LDP-NR
+ final ContentType realType = MarmottaHttpUtils.parseContentType(ldpService.getMimeType(conn, resource));
+ if (realType == null) {
+ // Fallback to related LDP-RS
+ log.trace("<{}> does not look like a LDP-NR (no format found), trying the corresponding LDP-RS", resource);
+ final URI ldpRS = ldpService.getRdfSourceForNonRdfSource(conn, resource);
+ if (ldpRS == null) {
+ log.debug("No corresponding LDP-RS found for <{}>, sending 406", resource);
+ final Response.ResponseBuilder resp = build406Response(conn, resource, Collections.<ContentType>emptyList());
+ conn.commit();
+ return resp;
+ } else {
+ // Sending back the corresponding LDP-RS
+ log.trace("Using corresponding LDP-RS <{}> for LDP-NR <{}>", ldpRS, resource);
+ final ContentType bestType = MarmottaHttpUtils.bestContentType(producedRdfTypes, acceptedContentTypes);
+ if (bestType == null) {
+ log.trace("Available formats {} do not match any of the requested formats {} for <{}>, sending 406", producedRdfTypes, acceptedContentTypes, resource);
+ final Response.ResponseBuilder resp = build406Response(conn, resource, producedRdfTypes);
+ conn.commit();
+ return resp;
+ } else {
+ log.trace("Sending corresponding LDP-RS <{}> for requested LDP-NR <{}> using {}", ldpRS, resource, bestType);
+ final Response.ResponseBuilder resp = buildGetResponseSourceResource(conn, ldpRS.stringValue(), Rio.getWriterFormatForMIMEType(bestType.getMime(), RDFFormat.TURTLE));
+ conn.commit();
+ return resp;
}
}
- };
- final String realType = ldpService.getMimeType(conn, resource);
- final Response.ResponseBuilder resp = createResponse(conn, Response.Status.OK, resource).entity(entity).type(realType != null ? MediaType.valueOf(realType) : type);
- conn.commit();
- return resp;
+ } else if (MarmottaHttpUtils.bestContentType(Collections.singletonList(realType), acceptedContentTypes) == null) {
+ log.debug("Can't send <{}> ({}) in any of the accepted formats: {}, sending 406");
+ final Response.ResponseBuilder resp = build406Response(conn, resource, Collections.singletonList(realType));
+ conn.commit();
+ return resp;
+ } else {
+ final Response.ResponseBuilder resp = buildGetResponseBinaryResource(conn, resource);
+ conn.commit();
+ return resp;
+ }
} else {
- // Deliver all triples from the <subject> context.
- log.debug("GET to <{}> with RDF format {} of a LPD-R", resource, format.getDefaultMIMEType());
- final StreamingOutput entity = new StreamingOutput() {
- @Override
- public void write(OutputStream output) throws IOException, WebApplicationException {
- try {
- final RepositoryConnection outputConn = sesameService.getConnection();
- try {
- outputConn.begin();
- ldpService.exportResource(outputConn, resource, output, format);
- outputConn.commit();
- } catch (RDFHandlerException e) {
- outputConn.rollback();
- throw new NoLogWebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e.getMessage()).build());
- } catch (final Throwable t) {
- outputConn.rollback();
- throw t;
- } finally {
- outputConn.close();
- }
- } catch (RepositoryException e) {
- throw new WebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e).build());
- }
- }
- };
- final Response.ResponseBuilder resp = createResponse(conn, Response.Status.OK, resource).entity(entity).type(format.getDefaultMIMEType());
- conn.commit();
- return resp;
+ // Requested Resource is a LDP-RS
+ final ContentType bestType = MarmottaHttpUtils.bestContentType(producedRdfTypes, acceptedContentTypes);
+ if (bestType == null) {
+ log.trace("Available formats {} do not match any of the requested formats {} for <{}>, sending 406", producedRdfTypes, acceptedContentTypes, resource);
+ final Response.ResponseBuilder resp = build406Response(conn, resource, producedRdfTypes);
+ conn.commit();
+ return resp;
+ } else {
+ final Response.ResponseBuilder resp = buildGetResponseSourceResource(conn, resource, Rio.getWriterFormatForMIMEType(bestType.getMime(), RDFFormat.TURTLE));
+ conn.commit();
+ return resp;
+ }
}
} catch (final Throwable t) {
conn.rollback();
@@ -214,6 +234,74 @@ public class LdpWebService {
}
}
+ private Response.ResponseBuilder build406Response(RepositoryConnection connection, String resource, List<ContentType> availableContentTypes) throws RepositoryException {
+ final Response.ResponseBuilder response = createResponse(connection, Response.Status.NOT_ACCEPTABLE, resource);
+ if (availableContentTypes.isEmpty()) {
+ response.entity(String.format("%s is not available in the requested format%n", resource));
+ } else {
+ response.entity(String.format("%s is only available in the following formats: %s%n", resource, availableContentTypes));
+ }
+ // Sec. 4.2.2.2
+ return addOptionsHeader(connection, resource, response);
+ }
+
+ private Response.ResponseBuilder buildGetResponseBinaryResource(RepositoryConnection connection, final String resource) throws RepositoryException {
+ final String realType = ldpService.getMimeType(connection, resource);
+ log.debug("Building response for LDP-NR <{}> with format {}", resource, realType);
+ final StreamingOutput entity = new StreamingOutput() {
+ @Override
+ public void write(OutputStream out) throws IOException, WebApplicationException {
+ try {
+ final RepositoryConnection outputConn = sesameService.getConnection();
+ try {
+ outputConn.begin();
+ ldpService.exportBinaryResource(outputConn, resource, out);
+ outputConn.commit();
+ } catch (RepositoryException | IOException e) {
+ outputConn.rollback();
+ throw new WebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e).build());
+ } finally {
+ outputConn.close();
+ }
+ } catch (RepositoryException e) {
+ throw new WebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e).build());
+ }
+ }
+ };
+ // Sec. 4.2.2.2
+ return addOptionsHeader(connection, resource, createResponse(connection, Response.Status.OK, resource).entity(entity).type(realType));
+ }
+
+ private Response.ResponseBuilder buildGetResponseSourceResource(RepositoryConnection conn, final String resource, final RDFFormat format) throws RepositoryException {
+ // Deliver all triples from the <subject> context.
+ log.debug("Building response for LDP-RS <{}> with RDF format {}", resource, format.getDefaultMIMEType());
+ final StreamingOutput entity = new StreamingOutput() {
+ @Override
+ public void write(OutputStream output) throws IOException, WebApplicationException {
+ try {
+ final RepositoryConnection outputConn = sesameService.getConnection();
+ try {
+ outputConn.begin();
+ ldpService.exportResource(outputConn, resource, output, format);
+ outputConn.commit();
+ } catch (RDFHandlerException e) {
+ outputConn.rollback();
+ throw new NoLogWebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e.getMessage()).build());
+ } catch (final Throwable t) {
+ outputConn.rollback();
+ throw t;
+ } finally {
+ outputConn.close();
+ }
+ } catch (RepositoryException e) {
+ throw new WebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e).build());
+ }
+ }
+ };
+ // Sec. 4.2.2.2
+ return addOptionsHeader(conn, resource, createResponse(conn, Response.Status.OK, resource).entity(entity).type(format.getDefaultMIMEType()));
+ }
+
/**
* LDP Post Request
*
@@ -222,7 +310,7 @@ public class LdpWebService {
*/
@POST
public Response POST(@Context UriInfo uriInfo, @HeaderParam("Slug") String slug,
- @HeaderParam("Link") List<Link> linkHeaders,
+ @HeaderParam(HttpHeaders.LINK) List<Link> linkHeaders,
InputStream postBody, @HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType type)
throws RepositoryException {
@@ -281,25 +369,8 @@ public class LdpWebService {
}
log.debug("POST to <{}> will create new LDP-R <{}>", container, newResource);
- 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, 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
- }
- conn.commit();
- return response.build();
- } catch (IOException | RDFParseException e) {
- 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(conn, Response.Status.UNSUPPORTED_MEDIA_TYPE, container).entity(e);
- conn.rollback();
- return resp.build();
- }
+ // connection is closed by buildPostResponse
+ return buildPostResponse(conn, container, newResource, ldpInteractionModel, postBody, type);
} 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);
@@ -314,10 +385,37 @@ public class LdpWebService {
}
/**
+ * @param connection the RepositoryConnection (with active transaction) to read extra data from. WILL BE COMMITTED OR ROLLBACKED
+ * @throws RepositoryException
+ */
+ private Response buildPostResponse(RepositoryConnection connection, String container, String newResource, LdpService.InteractionModel interactionModel, InputStream requestBody, MediaType type) throws RepositoryException {
+ final String mimeType = LdpUtils.getMimeType(type);
+ //checking if resource (container) exists is done later in the service
+ try {
+ String location = ldpService.addResource(connection, container, newResource, interactionModel, mimeType, requestBody);
+ final Response.ResponseBuilder response = createResponse(connection, 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
+ }
+ connection.commit();
+ return response.build();
+ } catch (IOException | RDFParseException e) {
+ final Response.ResponseBuilder resp = createResponse(connection, Response.Status.BAD_REQUEST, container).entity(e.getClass().getSimpleName() + ": " + e.getMessage());
+ connection.rollback();
+ return resp.build();
+ } catch (UnsupportedRDFormatException e) {
+ final Response.ResponseBuilder resp = createResponse(connection, Response.Status.UNSUPPORTED_MEDIA_TYPE, container).entity(e);
+ connection.rollback();
+ return resp.build();
+ }
+ }
+
+ /**
* Handle PUT (Sec. 4.2.4, Sec. 5.2.4)
*/
@PUT
public Response PUT(@Context UriInfo uriInfo, @Context Request request,
+ @HeaderParam(HttpHeaders.LINK) List<Link> linkHeaders,
@HeaderParam(HttpHeaders.IF_MATCH) EntityTag eTag,
@HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType type, InputStream postBody)
throws RepositoryException, IOException, InvalidModificationException, RDFParseException, IncompatibleResourceTypeException, URISyntaxException {
@@ -332,7 +430,7 @@ public class LdpWebService {
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);
+ log.debug("<{}> exists, so this is an UPDATE", resource);
if (eTag == null) {
// check for If-Match header (ETag) -> 428 Precondition Required (Sec. 4.2.4.5)
@@ -355,17 +453,42 @@ public class LdpWebService {
newResource = ldpService.updateResource(conn, resource, postBody, mimeType);
log.info("PUT update for <{}> successful", newResource);
resp = createResponse(conn, Response.Status.OK, resource);
+ conn.commit();
+ return resp.build();
} else {
- log.debug("creating resource <{}>", resource);
+ log.debug("<{}> does not exist, so this is a CREATE", resource);
//LDP servers may allow resource creation using PUT (Sec. 4.2.4.6)
+
+ final String container = LdpUtils.getContainer(resource);
+ try {
+ // 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);
+
+ // connection is closed by buildPostResponse
+ return buildPostResponse(conn, container, resource, ldpInteractionModel, postBody, type);
+ } catch (InvalidInteractionModelException e) {
+ log.debug("PUT 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();
+ }
+ /*
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));
+ conn.commit();
+ return resp.build();
+ */
}
- conn.commit();
-
- return resp.build();
} catch (IOException | RDFParseException e) {
final Response.ResponseBuilder resp = createResponse(conn, Response.Status.BAD_REQUEST, resource).entity(e.getClass().getSimpleName() + ": " + e.getMessage());
conn.rollback();
@@ -477,6 +600,7 @@ public class LdpWebService {
* Handle OPTIONS (Sec. 4.2.8, Sec. 5.2.8)
*/
@OPTIONS
+ @Produces("text/plain")
public Response OPTIONS(@Context final UriInfo uriInfo) throws RepositoryException {
final String resource = ldpService.getResourceUri(uriInfo);
log.debug("OPTIONS to <{}>", resource);
@@ -489,22 +613,7 @@ public class LdpWebService {
Response.ResponseBuilder builder = createResponse(con, Response.Status.OK, resource);
- if (ldpService.isNonRdfSourceResource(con, resource)) {
- // Sec. 4.2.8.2
- builder.allow("GET", "HEAD", "OPTIONS");
- } else if (ldpService.isRdfSourceResource(con, resource)) {
- 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);
- }
+ addOptionsHeader(con, resource, builder);
con.commit();
return builder.build();
@@ -517,10 +626,35 @@ public class LdpWebService {
}
+ private Response.ResponseBuilder addOptionsHeader(RepositoryConnection connection, String resource, Response.ResponseBuilder builder) throws RepositoryException {
+ log.debug("Adding required LDP Headers (OPTIONS, GET); see Sec. 8.2.8 and Sec. 4.2.2.2");
+ if (ldpService.isNonRdfSourceResource(connection, resource)) {
+ // Sec. 4.2.8.2
+ log.trace("<{}> is an LDP-NR: GET, HEAD, PUT and OPTIONS allowed", resource);
+ builder.allow("GET", "HEAD", "PUT", "OPTIONS");
+ } else if (ldpService.isRdfSourceResource(connection, resource)) {
+ if (ldpService.getInteractionModel(connection, resource) == LdpService.InteractionModel.LDPR) {
+ log.trace("<{}> is a LDP-RS (LDPR interaction model): GET, HEAD, PUT, PATCH and OPTIONS allowed", resource);
+ // Sec. 4.2.8.2
+ builder.allow("GET", "HEAD", "PUT", "PATCH", "OPTIONS");
+ } else {
+ // Sec. 4.2.8.2
+ log.trace("<{}> is a LDP-RS (LDPC interaction model): GET, HEAD, POST, PUT, PATCH and OPTIONS allowed", resource);
+ builder.allow("GET", "HEAD", "POST", "PUT", "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);
+ }
+
+ return builder;
+ }
+
/**
* Add all the default headers specified in LDP to the Response
*
- * @param connection
+ * @param connection the RepositoryConnection (with active transaction) to read extra data from
* @param status the StatusCode
* @param resource the iri/uri/url of the resouce
* @return the provided ResponseBuilder for chaining
@@ -532,7 +666,7 @@ public class LdpWebService {
/**
* Add all the default headers specified in LDP to the Response
*
- * @param connection
+ * @param connection the RepositoryConnection (with active transaction) to read extra data from
* @param status the status code
* @param resource the uri/url of the resouce
* @return the provided ResponseBuilder for chaining
@@ -544,9 +678,9 @@ public class LdpWebService {
/**
* Add all the default headers specified in LDP to the Response
*
- * @param connection
+ * @param connection the RepositoryConnection (with active transaction) to read extra data from
* @param rb the ResponseBuilder
- * @param resource the uri/url of the resouce
+ * @param resource the uri/url of the resource
* @return the provided ResponseBuilder for chaining
*/
protected Response.ResponseBuilder createResponse(RepositoryConnection connection, Response.ResponseBuilder rb, String resource) throws RepositoryException {
@@ -566,8 +700,9 @@ public class LdpWebService {
if (rdfSource != null) {
// Sec. 5.2.8.1 and 5.2.3.12
// 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");
+ // TODO: Propose to LDP-WG?
+ rb.link(rdfSource.stringValue(), "meta");
}
final URI nonRdfSource = ldpService.getNonRdfSourceForRdfSource(connection, resource);
if (nonRdfSource != null) {