marmotta-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ja...@apache.org
Subject [1/6] git commit: MARMOTTA-440: implemented HTTP PATCH operation
Date Tue, 25 Feb 2014 19:50:35 GMT
Repository: marmotta
Updated Branches:
  refs/heads/ldp e66e59d75 -> 370a49b68


MARMOTTA-440: implemented HTTP PATCH operation


Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo
Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/05e03798
Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/05e03798
Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/05e03798

Branch: refs/heads/ldp
Commit: 05e03798f53469d58bc5543de974b4b1a41d4e98
Parents: e66e59d
Author: Jakob Frank <jakob@apache.org>
Authored: Tue Feb 25 17:57:42 2014 +0100
Committer: Jakob Frank <jakob@apache.org>
Committed: Tue Feb 25 18:01:10 2014 +0100

----------------------------------------------------------------------
 .../marmotta/platform/ldp/api/LdpService.java   |  4 ++
 .../InvalidModificationException.java           | 40 +++++++++++++++++
 .../patch/InvalidPatchDocumentException.java    | 40 +++++++++++++++++
 .../platform/ldp/patch/RdfPatchUtil.java        | 25 ++++++++---
 .../platform/ldp/services/LdpServiceImpl.java   | 36 +++++++++++++++
 .../platform/ldp/webservices/LdpWebService.java | 46 +++++++++++++++++---
 .../platform/ldp/patch/RdfPatchUtilTest.java    | 19 ++++++++
 .../ldp/webservices/LdpWebServiceTest.java      |  6 +--
 8 files changed, 199 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/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 887aa8c..3429599 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,9 @@
  */
 package org.apache.marmotta.platform.ldp.api;
 
+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;
 import org.openrdf.model.Statement;
 import org.openrdf.model.URI;
 import org.openrdf.repository.RepositoryException;
@@ -62,4 +65,5 @@ public interface LdpService {
 
     boolean deleteResource(String resource) throws RepositoryException;
 
+    void patchResource(String resource, InputStream patchData) throws RepositoryException,
ParseException, InvalidModificationException, InvalidPatchDocumentException;
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidModificationException.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidModificationException.java
b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidModificationException.java
new file mode 100644
index 0000000..32e1bde
--- /dev/null
+++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/exceptions/InvalidModificationException.java
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+/**
+ * Created by jakob on 2/25/14.
+ */
+public class InvalidModificationException extends Exception {
+    public InvalidModificationException() {
+        super();
+    }
+
+    public InvalidModificationException(String message) {
+        super(message);
+    }
+
+    public InvalidModificationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public InvalidModificationException(Throwable cause) {
+        super(cause);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/InvalidPatchDocumentException.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/InvalidPatchDocumentException.java
b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/InvalidPatchDocumentException.java
new file mode 100644
index 0000000..7e5a873
--- /dev/null
+++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/InvalidPatchDocumentException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.patch;
+
+/**
+ * Created by jakob on 2/25/14.
+ */
+public class InvalidPatchDocumentException extends Exception {
+
+    public InvalidPatchDocumentException() {
+        super();
+    }
+
+    public InvalidPatchDocumentException(String message) {
+        super(message);
+    }
+
+    public InvalidPatchDocumentException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public InvalidPatchDocumentException(Throwable cause) {
+        super(cause);
+    }
+}

http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtil.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtil.java
b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtil.java
index be43762..8be7652 100644
--- a/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtil.java
+++ b/platform/marmotta-ldp/src/main/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtil.java
@@ -18,6 +18,7 @@
 package org.apache.marmotta.platform.ldp.patch;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.marmotta.platform.ldp.patch.model.PatchLine;
 import org.apache.marmotta.platform.ldp.patch.parser.ParseException;
@@ -39,21 +40,21 @@ import java.util.List;
  */
 public class RdfPatchUtil {
 
-    public static void applyPatch(Repository repository, String patch, Resource... contexts)
throws RepositoryException, ParseException {
+    public static void applyPatch(Repository repository, String patch, Resource... contexts)
throws RepositoryException, ParseException, InvalidPatchDocumentException {
         applyPatch(repository, getPatch(patch), contexts);
     }
 
-    public static void applyPatch(Repository repository, InputStream patchSource, Resource...
contexts) throws RepositoryException, ParseException {
+    public static void applyPatch(Repository repository, InputStream patchSource, Resource...
contexts) throws RepositoryException, ParseException, InvalidPatchDocumentException {
         applyPatch(repository, getPatch(patchSource), contexts);
     }
-    public static void applyPatch(RepositoryConnection con, String patch, Resource... contexts)
throws RepositoryException, ParseException {
+    public static void applyPatch(RepositoryConnection con, String patch, Resource... contexts)
throws RepositoryException, ParseException, InvalidPatchDocumentException {
         applyPatch(con, getPatch(patch), contexts);
     }
-    public static void applyPatch(RepositoryConnection con, InputStream patchSource, Resource...
contexts) throws RepositoryException, ParseException {
+    public static void applyPatch(RepositoryConnection con, InputStream patchSource, Resource...
contexts) throws RepositoryException, ParseException, InvalidPatchDocumentException {
         applyPatch(con, getPatch(patchSource), contexts);
     }
 
-    public static void applyPatch(Repository repository, List<PatchLine> patch, Resource...
contexts) throws RepositoryException {
+    public static void applyPatch(Repository repository, List<PatchLine> patch, Resource...
contexts) throws RepositoryException, InvalidPatchDocumentException {
         RepositoryConnection con = repository.getConnection();
         try {
             con.begin();
@@ -67,7 +68,7 @@ public class RdfPatchUtil {
         }
     }
 
-    public static void applyPatch(RepositoryConnection con, List<PatchLine> patch,
Resource... contexts) throws RepositoryException {
+    public static void applyPatch(RepositoryConnection con, List<PatchLine> patch,
Resource... contexts) throws RepositoryException, InvalidPatchDocumentException {
         Resource subject = null;
         URI predicate = null;
         Value object = null;
@@ -78,6 +79,18 @@ public class RdfPatchUtil {
             predicate = statement.getPredicate()!=null?statement.getPredicate():predicate;
             object = statement.getObject()!=null?statement.getObject():object;
 
+            if (subject == null || predicate == null || object == null) {
+                if (subject == null) {
+                    throw new InvalidPatchDocumentException("Cannot resolve 'R' - subject
was never set");
+                }
+                if (predicate == null) {
+                    throw new InvalidPatchDocumentException("Cannot resolve 'R' - predicate
was never set");
+                }
+                if (object == null) {
+                    throw new InvalidPatchDocumentException("Cannot resolve 'R' - object
was never set");
+                }
+            }
+
             switch (patchLine.getOperator()) {
                 case ADD:
                     con.add(subject, predicate, object, contexts);

http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/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 b49f9d2..030d930 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
@@ -23,6 +23,12 @@ 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.InvalidModificationException;
+import org.apache.marmotta.platform.ldp.patch.InvalidPatchDocumentException;
+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.RdfPatchParser;
 import org.apache.marmotta.platform.ldp.util.LdpWebServiceUtils;
 import org.openrdf.model.Literal;
 import org.openrdf.model.Statement;
@@ -235,6 +241,36 @@ public class LdpServiceImpl implements LdpService {
     }
 
     @Override
+    public void patchResource(String resource, InputStream patchData) throws RepositoryException,
ParseException, InvalidModificationException, InvalidPatchDocumentException {
+        final RepositoryConnection conn = sesameService.getConnection();
+        final URI rUri = conn.getValueFactory().createURI(resource);
+        final URI ldpContext = conn.getValueFactory().createURI(LDP.NAMESPACE);
+
+        final Literal now = conn.getValueFactory().createLiteral(new Date());
+
+
+        log.trace("parsing patch");
+        List<PatchLine> patch = new RdfPatchParser(patchData).parsePatch();
+
+        // we are allowed to restrict the patch contents (Sec. ???)
+        log.trace("checking for invalid patch statements");
+        for (PatchLine patchLine : patch) {
+            if (LDP.contains.equals(patchLine.getStatement().getPredicate())) {
+                throw new InvalidModificationException("must not change <" + LDP.contains.stringValue()
+ "> via PATCH");
+            }
+        }
+
+        log.debug("patching <{}> ({} changes)", resource, patch.size());
+
+        RdfPatchUtil.applyPatch(conn, patch, rUri);
+
+        log.trace("update resource meta");
+        conn.remove(rUri, DCTERMS.modified, null, ldpContext);
+        conn.add(rUri, DCTERMS.modified, now, ldpContext);
+
+    }
+
+    @Override
     public boolean deleteResource(String resource) throws RepositoryException {
         return deleteResource(buildURI(resource));
     }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/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 9fe07e6..9163b67 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
@@ -21,6 +21,9 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.marmotta.commons.vocabulary.LDP;
 import org.apache.marmotta.platform.core.api.config.ConfigurationService;
 import org.apache.marmotta.platform.ldp.api.LdpService;
+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;
 import org.apache.marmotta.platform.ldp.util.EntityTagUtils;
 import org.apache.marmotta.platform.ldp.util.LdpWebServiceUtils;
 import org.openrdf.model.Statement;
@@ -163,7 +166,7 @@ public class LdpWebService {
     @PUT
     public Response PUT(@Context UriInfo uriInfo, @Context Request request,
                         @HeaderParam(HttpHeaders.IF_MATCH) EntityTag eTag,
-                        InputStream postBody, @HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType
type)
+                        @HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType type, InputStream
postBody)
             throws RepositoryException {
         /*
          * Handle PUT (Sec. 5.5, Sec. 6.5)
@@ -215,13 +218,42 @@ public class LdpWebService {
     }
 
     @PATCH
-    public Response PATCH(@Context UriInfo uriInfo, InputStream postBody, @HeaderParam(HttpHeaders.CONTENT_TYPE)
MediaType type) {
+    public Response PATCH(@Context UriInfo uriInfo,
+                          @HeaderParam(HttpHeaders.IF_MATCH) EntityTag eTag,
+                          @HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType type, InputStream
postBody) throws RepositoryException {
+        final String resource = getResourceUri(uriInfo);
+        log.debug("PATCH to <{}>", resource);
+
+        if (!ldpService.exists(resource)) {
+            return createResponse(Response.Status.NOT_FOUND, uriInfo).build();
+        }
+
+        if (eTag != null) {
+            // check ETag if present
+            log.trace("Checking If-Match: {}", eTag);
+            EntityTag hasTag = ldpService.generateETag(resource);
+            if (!EntityTagUtils.equals(eTag, hasTag)) {
+                log.trace("If-Match header did not match, expected {}", hasTag);
+                return createResponse(Response.Status.PRECONDITION_FAILED, uriInfo).build();
+            }
+        }
+
         // Check for the supported mime-type
         if (!type.toString().equals(APPLICATION_RDF_PATCH)) {
-            return createResponse(Response.Status.BAD_REQUEST, uriInfo).entity("Unknown Content-Type:
" + type + "\n").build();
-        };
+            log.trace("Incompatible Content-Type for PATCH: {}", type);
+            return createResponse(Response.Status.UNSUPPORTED_MEDIA_TYPE, uriInfo).entity("Unknown
Content-Type: " + type + "\n").build();
+        }
+
+        try {
+            ldpService.patchResource(resource, postBody);
+        } catch (ParseException | InvalidPatchDocumentException e) {
+            return createResponse(Response.Status.BAD_REQUEST, uriInfo).entity(e.getMessage()
+ "\n").build();
+        } catch (InvalidModificationException e) {
+            return createResponse(422, uriInfo).entity(e.getMessage() + "\n").build();
+        }
+
+        return createResponse(Response.Status.NO_CONTENT, uriInfo).build();
 
-        return Response.status(Response.Status.NOT_IMPLEMENTED).build();
     }
 
     @OPTIONS
@@ -237,13 +269,13 @@ public class LdpWebService {
         Response.ResponseBuilder builder = createResponse(Response.Status.OK, uriInfo);
 
         // Sec. 5.9.2
-        builder.allow("GET", "HEAD", "POST", "OPTIONS");
+        builder.allow("GET", "HEAD", "POST", "PATCH", "OPTIONS");
 
         // Sec. 6.4.14 / Sec. 8.1
         // builder.header("Accept-Post", "text/turtle, */*");
         builder.header("Accept-Post", "text/turtle");
 
-        // TODO: Sec. 5.8.2
+        // Sec. 5.8.2
         builder.header("Accept-Patch", APPLICATION_RDF_PATCH);
 
 

http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtilTest.java
----------------------------------------------------------------------
diff --git a/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtilTest.java
b/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtilTest.java
index ba55337..6094968 100644
--- a/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtilTest.java
+++ b/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/RdfPatchUtilTest.java
@@ -18,6 +18,7 @@
 package org.apache.marmotta.platform.ldp.patch;
 
 import org.apache.marmotta.commons.vocabulary.FOAF;
+import org.apache.marmotta.platform.ldp.patch.parser.ParseException;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -28,6 +29,7 @@ import org.openrdf.model.impl.LiteralImpl;
 import org.openrdf.model.impl.URIImpl;
 import org.openrdf.repository.Repository;
 import org.openrdf.repository.RepositoryConnection;
+import org.openrdf.repository.RepositoryException;
 import org.openrdf.repository.sail.SailRepository;
 import org.openrdf.rio.RDFFormat;
 import org.openrdf.sail.memory.MemoryStore;
@@ -95,4 +97,21 @@ public class RdfPatchUtilTest {
             con.close();
         }
     }
+
+    @Test(expected = InvalidPatchDocumentException.class)
+    public void testInvalidPatchDocumentException() throws RepositoryException, ParseException,
InvalidPatchDocumentException {
+        RepositoryConnection con = repository.getConnection();
+        try {
+            final String invalidPatch = "A <http://example/foo> R <http://example/bar>
.";
+
+            RdfPatchUtil.applyPatch(con, invalidPatch);
+
+            Assert.fail("applyPatch should throw an InvalidPatchDocumentException");
+        } catch (final Throwable t) {
+            con.rollback();
+            throw t;
+        } finally {
+            con.close();
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/marmotta/blob/05e03798/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 947d594..3e0a3d6 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
@@ -60,10 +60,8 @@ public class LdpWebServiceTest {
     }
 
     @Test
-    public void test() {
-	// Nothing is implemented so far...
-        Assert.assertEquals(501, RestAssured.get("/ldp").statusCode());
-        Assert.assertEquals(501, RestAssured.get("/ldp/foo/bar/and/some/more").statusCode());
+    public void testCRUD() {
+        
     }
 
     @AfterClass


Mime
View raw message