MARMOTTA-440: Refactored LdpWebService and LdpService(Impl) to have only one transaction (RepositoryConnection) per request.
Project: http://git-wip-us.apache.org/repos/asf/marmotta/repo Commit: http://git-wip-us.apache.org/repos/asf/marmotta/commit/370a49b6 Tree: http://git-wip-us.apache.org/repos/asf/marmotta/tree/370a49b6 Diff: http://git-wip-us.apache.org/repos/asf/marmotta/diff/370a49b6 Branch: refs/heads/ldp Commit: 370a49b68e5493e15c375ba11582d6de9f192f6a Parents: 7ce3f83 Author: Jakob Frank <[email protected]> Authored: Tue Feb 25 20:47:42 2014 +0100 Committer: Jakob Frank <[email protected]> Committed: Tue Feb 25 20:47:42 2014 +0100 ---------------------------------------------------------------------- .../marmotta/platform/ldp/api/LdpService.java | 34 +- .../platform/ldp/services/LdpServiceImpl.java | 280 ++++++------- .../platform/ldp/webservices/LdpWebService.java | 407 +++++++++++++------ .../platform/ldp/patch/RdfPatchUtilTest.java | 4 +- .../patch/parser/RdfPatchParserImplTest.java | 91 +++++ .../ldp/patch/parser/RdfPatchParserTest.java | 89 ---- 6 files changed, 522 insertions(+), 383 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/marmotta/blob/370a49b6/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 3429599..bd85565 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 @@ -22,6 +22,7 @@ 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.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.rio.RDFFormat; import org.openrdf.rio.RDFHandlerException; @@ -32,6 +33,7 @@ import javax.ws.rs.core.MediaType; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Date; import java.util.List; /** @@ -41,29 +43,35 @@ import java.util.List; */ public interface LdpService { - boolean exists(String resource) throws RepositoryException; + boolean exists(RepositoryConnection connection, String resource) throws RepositoryException; - boolean exists(URI resource) throws RepositoryException; + boolean exists(RepositoryConnection connection, URI resource) throws RepositoryException; - boolean addResource(InputStream stream, MediaType type, String container, String resource) throws RepositoryException, IOException, RDFParseException; + boolean addResource(RepositoryConnection connection, String container, String resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException; - boolean addResource(InputStream stream, MediaType type, URI container, URI resource) throws RepositoryException, IOException, RDFParseException; + boolean addResource(RepositoryConnection connection, URI container, URI resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException; - List<Statement> getStatements(String resource) throws RepositoryException; + List<Statement> getStatements(RepositoryConnection connection, String resource) throws RepositoryException; - List<Statement> getStatements(URI resource) throws RepositoryException; + List<Statement> getStatements(RepositoryConnection conn1, URI resource) throws RepositoryException; - void exportResource(OutputStream output, String resource, RDFFormat format) throws RepositoryException, RDFHandlerException; + void exportResource(RepositoryConnection connection, String resource, OutputStream output, RDFFormat format) throws RepositoryException, RDFHandlerException; - void exportResource(OutputStream output, URI resouce, RDFFormat format) throws RepositoryException, RDFHandlerException; + void exportResource(RepositoryConnection connection, URI resource, OutputStream output, RDFFormat format) throws RepositoryException, RDFHandlerException; - EntityTag generateETag(String uri) throws RepositoryException; + EntityTag generateETag(RepositoryConnection connection, String uri) throws RepositoryException; - EntityTag generateETag(URI uri) throws RepositoryException; + EntityTag generateETag(RepositoryConnection connection, URI uri) throws RepositoryException; - boolean deleteResource(URI resource) throws RepositoryException; + boolean deleteResource(RepositoryConnection connection, URI resource) throws RepositoryException; - boolean deleteResource(String resource) throws RepositoryException; + void patchResource(RepositoryConnection connection, URI uri, InputStream patchData, boolean strict) throws RepositoryException, ParseException, InvalidModificationException, InvalidPatchDocumentException; - void patchResource(String resource, InputStream patchData) throws RepositoryException, ParseException, InvalidModificationException, InvalidPatchDocumentException; + boolean deleteResource(RepositoryConnection connection, String resource) throws RepositoryException; + + Date getLastModified(RepositoryConnection connection, String resource) throws RepositoryException; + + Date getLastModified(RepositoryConnection connection, URI uri) throws RepositoryException; + + void patchResource(RepositoryConnection connection, String resource, InputStream patchData, boolean strict) throws RepositoryException, ParseException, InvalidModificationException, InvalidPatchDocumentException; } http://git-wip-us.apache.org/repos/asf/marmotta/blob/370a49b6/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 421f088..1d99956 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 @@ -17,6 +17,7 @@ */ package org.apache.marmotta.platform.ldp.services; +import info.aduna.iteration.Iterations; import info.aduna.iteration.UnionIteration; import org.apache.marmotta.commons.vocabulary.DCTERMS; import org.apache.marmotta.commons.vocabulary.LDP; @@ -28,7 +29,6 @@ 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.patch.parser.RdfPatchParserImpl; import org.apache.marmotta.platform.ldp.util.LdpWebServiceUtils; import org.openrdf.model.Literal; @@ -68,158 +68,122 @@ public class LdpServiceImpl implements LdpService { @Inject private ConfigurationService configurationService; - @Inject - private SesameService sesameService; - private URI buildURI(String resource) { return ValueFactoryImpl.getInstance().createURI(resource); } @Override - public boolean exists(String resource) throws RepositoryException { - return exists(buildURI(resource)); + public boolean exists(RepositoryConnection connection, String resource) throws RepositoryException { + return exists(connection, buildURI(resource)); } @Override - public boolean exists(URI resource) throws RepositoryException { - final RepositoryConnection conn = sesameService.getConnection(); - try { - conn.begin(); - final URI ldpContext = conn.getValueFactory().createURI(LDP.NAMESPACE); - return conn.hasStatement(resource, RDF.TYPE, null, true, ldpContext); - } finally { - conn.commit(); - conn.close(); - } + public boolean exists(RepositoryConnection connection, URI resource) throws RepositoryException { + final URI ldpContext = connection.getValueFactory().createURI(LDP.NAMESPACE); + return connection.hasStatement(resource, RDF.TYPE, null, true, ldpContext); } @Override - public List<Statement> getStatements(String resource) throws RepositoryException { - return getStatements(buildURI(resource)); + public List<Statement> getStatements(RepositoryConnection connection, String resource) throws RepositoryException { + return getStatements(connection, buildURI(resource)); } @Override - public List<Statement> getStatements(URI resource) throws RepositoryException { - final RepositoryConnection conn = sesameService.getConnection(); - try { - URI ldp = conn.getValueFactory().createURI(LDP.NAMESPACE); - return conn.getStatements(resource, RDF.TYPE, null, false, ldp).asList(); //FIXME - } finally { - conn.close(); - } + public List<Statement> getStatements(RepositoryConnection connection, URI resource) throws RepositoryException { + final URI ldp = connection.getValueFactory().createURI(LDP.NAMESPACE); + return Iterations.asList(connection.getStatements(resource, RDF.TYPE, null, false, ldp)); //FIXME } @Override - public void exportResource(OutputStream output, String resource, RDFFormat format) throws RepositoryException, RDFHandlerException { - exportResource(output, buildURI(resource), format); + public void exportResource(RepositoryConnection connection, String resource, OutputStream output, RDFFormat format) throws RepositoryException, RDFHandlerException { + exportResource(connection, buildURI(resource), output, format); } @Override - public void exportResource(OutputStream output, URI resource, RDFFormat format) throws RepositoryException, RDFHandlerException { - RepositoryConnection conn = sesameService.getConnection(); + public void exportResource(RepositoryConnection connection, URI resource, OutputStream output, RDFFormat format) throws RepositoryException, RDFHandlerException { + URI ldpContext = connection.getValueFactory().createURI(LDP.NAMESPACE); + // TODO: this should be a little more sophisticated... + // 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) + ); try { - conn.begin(); - URI ldpContext = conn.getValueFactory().createURI(LDP.NAMESPACE); - // TODO: this should be a little more sophisticated... - // TODO: non-membership triples flag / Prefer-header - RDFWriter writer = Rio.createWriter(format, output); - UnionIteration<Statement, RepositoryException> union = new UnionIteration<>( - conn.getStatements(null, null, null, false, resource), - conn.getStatements(resource, null, null, false, ldpContext) - ); - try { - LdpWebServiceUtils.exportIteration(writer, resource, union); - } finally { - union.close(); - } - conn.commit(); + LdpWebServiceUtils.exportIteration(writer, resource, union); } finally { - conn.close(); + union.close(); } } @Override - public boolean addResource(InputStream stream, MediaType type, String container, String resource) throws RepositoryException, IOException, RDFParseException { - return addResource(stream, type, buildURI(container), buildURI(resource)); + public boolean addResource(RepositoryConnection connection, String container, String resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException { + return addResource(connection, buildURI(container), buildURI(resource), type, stream); } @Override - public boolean addResource(InputStream stream, MediaType type, URI container, URI resource) throws RepositoryException, IOException, RDFParseException { - final RepositoryConnection conn = sesameService.getConnection(); - try { - final URI ldpContext = conn.getValueFactory().createURI(LDP.NAMESPACE); - conn.begin(); - - log.trace("Checking possible name clash for new resource <{}>", resource.stringValue()); - if (conn.hasStatement(resource, null, null, false, ldpContext)) { - int i = 0; - final String base = resource.stringValue(); - do { - final String candidate = base + "-" + (++i); - log.trace("<{}> already exists, trying <{}>", resource.stringValue(), candidate); - resource = conn.getValueFactory().createURI(candidate); - } while (conn.hasStatement(resource, null, null, false, ldpContext)); - log.debug("resolved name clash, new resource will be <{}>", resource.stringValue()); - } else { - log.debug("no name clash for <{}>", resource); - } + public boolean addResource(RepositoryConnection connection, URI container, URI resource, MediaType type, InputStream stream) throws RepositoryException, IOException, RDFParseException { + final URI ldpContext = connection.getValueFactory().createURI(LDP.NAMESPACE); + + // FIXME: this should be done in LdpWebService + log.trace("Checking possible name clash for new resource <{}>", resource.stringValue()); + if (connection.hasStatement(resource, null, null, false, ldpContext)) { + int i = 0; + final String base = resource.stringValue(); + do { + final String candidate = base + "-" + (++i); + log.trace("<{}> already exists, trying <{}>", resource.stringValue(), candidate); + resource = connection.getValueFactory().createURI(candidate); + } while (connection.hasStatement(resource, null, null, false, ldpContext)); + log.debug("resolved name clash, new resource will be <{}>", resource.stringValue()); + } else { + log.debug("no name clash for <{}>", resource); + } - log.debug("POST to <{}> will create new LDP-R <{}>", container, resource); + log.debug("POST to <{}> will create new LDP-R <{}>", container, resource); - // Add container triples (Sec. 6.4.3) - // container and meta triples! + // Add container triples (Sec. 6.4.3) + // container and meta triples! - Literal now = conn.getValueFactory().createLiteral(new Date()); + Literal now = connection.getValueFactory().createLiteral(new Date()); - conn.add(container, RDF.TYPE, LDP.BasicContainer, ldpContext); - conn.add(container, LDP.contains, resource, ldpContext); - conn.remove(container, DCTERMS.modified, null, ldpContext); - conn.add(container, DCTERMS.modified, now, ldpContext); + connection.add(container, RDF.TYPE, LDP.BasicContainer, ldpContext); + connection.add(container, LDP.contains, resource, ldpContext); + connection.remove(container, DCTERMS.modified, null, ldpContext); + connection.add(container, DCTERMS.modified, now, ldpContext); - conn.add(resource, RDF.TYPE, LDP.Resource, ldpContext); - conn.add(resource, DCTERMS.created, now, ldpContext); - conn.add(resource, DCTERMS.modified, now, ldpContext); + connection.add(resource, RDF.TYPE, LDP.Resource, ldpContext); + connection.add(resource, DCTERMS.created, now, ldpContext); + connection.add(resource, DCTERMS.modified, now, ldpContext); - // LDP-BC for now! - conn.commit(); + // TODO: No LDP-BC for now! - // Add the bodyContent - log.trace("Content ({}) for new resource <{}>", type, resource); - final RDFFormat rdfFormat = Rio.getParserFormatForMIMEType(type.toString(), RDFFormat.TURTLE); - if (rdfFormat == null) { - log.debug("POST creates new LDP-BR with type {}", type); - log.warn("LDP-BR not (yet) supported!"); - throw new UnsupportedRDFormatException("No available parser for " + type.toString()); - } else { - log.debug("POST creates new LDP-RR, data provided as {}", rdfFormat.getName()); - conn.begin(); + // Add the bodyContent + log.trace("Content ({}) for new resource <{}>", type, resource); + final RDFFormat rdfFormat = Rio.getParserFormatForMIMEType(type.toString(), RDFFormat.TURTLE); + if (rdfFormat == null) { + log.debug("POST creates new LDP-BR with type {}", type); + log.warn("LDP-BR not (yet) supported!"); + throw new UnsupportedRDFormatException("No available parser for " + type.toString()); + } else { + log.debug("POST creates new LDP-RR, data provided as {}", rdfFormat.getName()); - // FIXME: We are (are we?) allowed to filter out server-managed properties here - conn.add(stream, resource.stringValue(), rdfFormat, resource); + // FIXME: We are (are we?) allowed to filter out server-managed properties here + connection.add(stream, resource.stringValue(), rdfFormat, resource); - conn.commit(); - return true; - } - } catch (final Throwable t) { - if (conn.isActive()) { - conn.rollback(); - } - throw t; - } finally { - conn.close(); + return true; } } @Override - public EntityTag generateETag(String resource) throws RepositoryException { - return generateETag(buildURI(resource)); + public EntityTag generateETag(RepositoryConnection connection, String resource) throws RepositoryException { + return generateETag(connection, buildURI(resource)); } @Override - public EntityTag generateETag(URI uri) throws RepositoryException { - final RepositoryConnection conn = sesameService.getConnection(); - final URI ldpContext = conn.getValueFactory().createURI(LDP.NAMESPACE); - final RepositoryResult<Statement> stmts = conn.getStatements(uri, DCTERMS.modified, null, true, ldpContext); + public EntityTag generateETag(RepositoryConnection connection, URI uri) throws RepositoryException { + final URI ldpContext = connection.getValueFactory().createURI(LDP.NAMESPACE); + 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; @@ -243,12 +207,42 @@ 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); + public Date getLastModified(RepositoryConnection connection, String resource) throws RepositoryException { + return getLastModified(connection, buildURI(resource)); + } + + @Override + public Date getLastModified(RepositoryConnection connection, URI uri) throws RepositoryException { + final URI ldpContext = connection.getValueFactory().createURI(LDP.NAMESPACE); + 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; + } + } + } + return latest; + } finally { + stmts.close(); + } + } + + @Override + public void patchResource(RepositoryConnection connection, String resource, InputStream patchData, boolean strict) throws RepositoryException, ParseException, InvalidModificationException, InvalidPatchDocumentException { + patchResource(connection, buildURI(resource), patchData, strict); + } - final Literal now = conn.getValueFactory().createLiteral(new Date()); + @Override + public void patchResource(RepositoryConnection connection, URI uri, InputStream patchData, boolean strict) throws RepositoryException, ParseException, InvalidModificationException, InvalidPatchDocumentException { + final URI ldpContext = connection.getValueFactory().createURI(LDP.NAMESPACE); + + final Literal now = connection.getValueFactory().createLiteral(new Date()); log.trace("parsing patch"); @@ -262,55 +256,45 @@ public class LdpServiceImpl implements LdpService { } } - log.debug("patching <{}> ({} changes)", resource, patch.size()); + log.debug("patching <{}> ({} changes)", uri.stringValue(), patch.size()); - RdfPatchUtil.applyPatch(conn, patch, rUri); + RdfPatchUtil.applyPatch(connection, patch, uri); log.trace("update resource meta"); - conn.remove(rUri, DCTERMS.modified, null, ldpContext); - conn.add(rUri, DCTERMS.modified, now, ldpContext); + connection.remove(uri, DCTERMS.modified, null, ldpContext); + connection.add(uri, DCTERMS.modified, now, ldpContext); } @Override - public boolean deleteResource(String resource) throws RepositoryException { - return deleteResource(buildURI(resource)); + public boolean deleteResource(RepositoryConnection connection, String resource) throws RepositoryException { + return deleteResource(connection, buildURI(resource)); } @Override - public boolean deleteResource(URI resource) throws RepositoryException { - final RepositoryConnection conn = sesameService.getConnection(); - try { - conn.begin(); - - log.warn("NOT CHECKING EXISTENCE OF <{}>", resource); - - URI ldpContext = conn.getValueFactory().createURI(LDP.NAMESPACE); - Literal now = conn.getValueFactory().createLiteral(new Date()); + public boolean deleteResource(RepositoryConnection connection, URI resource) throws RepositoryException { + final URI ldpContext = connection.getValueFactory().createURI(LDP.NAMESPACE); + final Literal now = connection.getValueFactory().createLiteral(new Date()); - // Delete corresponding containment and membership triples (Sec. 6.6.1) - RepositoryResult<Statement> stmts = conn.getStatements(null, LDP.member, resource, false, ldpContext); - try { - while (stmts.hasNext()) { - Statement st = stmts.next(); - conn.remove(st.getSubject(), DCTERMS.modified, null); - conn.add(st.getSubject(), DCTERMS.modified, now); - conn.remove(st); - } - } finally { - stmts.close(); + // Delete corresponding containment and membership triples (Sec. 6.6.1) + RepositoryResult<Statement> stmts = connection.getStatements(null, LDP.member, resource, false, ldpContext); + try { + while (stmts.hasNext()) { + Statement st = stmts.next(); + connection.remove(st.getSubject(), DCTERMS.modified, null); + connection.add(st.getSubject(), DCTERMS.modified, now); + connection.remove(st); } - // Delete the resource meta - conn.remove(resource, null, null, ldpContext); - - // Delete the resource data - conn.clear(resource); - - conn.commit(); - return true; } finally { - conn.close(); + stmts.close(); } + // Delete the resource meta + connection.remove(resource, null, null, ldpContext); + + // Delete the resource data + connection.clear(resource); + + return true; } } http://git-wip-us.apache.org/repos/asf/marmotta/blob/370a49b6/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 9ac0211..25febcb 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 @@ -20,6 +20,7 @@ package org.apache.marmotta.platform.ldp.webservices; 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.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; @@ -27,9 +28,11 @@ import org.apache.marmotta.platform.ldp.patch.parser.ParseException; import org.apache.marmotta.platform.ldp.patch.parser.RdfPatchParser; import org.apache.marmotta.platform.ldp.util.EntityTagUtils; import org.apache.marmotta.platform.ldp.util.LdpWebServiceUtils; +import org.jboss.resteasy.spi.NoLogWebApplicationException; import org.openrdf.model.Statement; import org.openrdf.model.URI; import org.openrdf.model.Value; +import org.openrdf.repository.RepositoryConnection; import org.openrdf.repository.RepositoryException; import org.openrdf.rio.*; import org.slf4j.Logger; @@ -69,6 +72,9 @@ public class LdpWebService { @Inject private LdpService ldpService; + @Inject + private SesameService sesameService; + @PostConstruct protected void initialize() { // TODO: basic initialisation @@ -93,33 +99,59 @@ public class LdpWebService { } private Response.ResponseBuilder buildGetResponse(final String resource, Request r, MediaType type) throws RepositoryException { + final RepositoryConnection con = sesameService.getConnection(); + try { + con.begin(); - if (!ldpService.exists(resource)) { - return Response.status(Response.Status.NOT_FOUND); - } - - // TODO: Maybe this is a LDP-BR? - // TODO: Proper content negotiation + if (!ldpService.exists(con, resource)) { + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NOT_FOUND, resource); + con.rollback(); + return resp; + } - final RDFFormat format = Rio.getWriterFormatForMIMEType(type.toString(), RDFFormat.TURTLE); - if (format == null) { - log.warn("GET to <{}> with unknown accept {}", resource, type); - return createResponse(Response.Status.NOT_IMPLEMENTED, resource); - } else { - // Deliver all triples with <subject> as subject. - final StreamingOutput entity = new StreamingOutput() { - @Override - public void write(OutputStream output) throws IOException, WebApplicationException { - try { - ldpService.exportResource(output, resource, format); - } catch (RDFHandlerException e) { - throw new WebApplicationException(e, createResponse(Response.Status.INTERNAL_SERVER_ERROR, resource).build()); - } catch (RepositoryException e) { - throw new WebApplicationException(e, createResponse(Response.Status.INTERNAL_SERVER_ERROR, resource).build()); + // TODO: Maybe this is a LDP-BR? + // TODO: Proper content negotiation + + final RDFFormat format = Rio.getWriterFormatForMIMEType(type.toString(), RDFFormat.TURTLE); + if (format == null) { + log.warn("GET to <{}> with unknown accept {}", resource, type); + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NOT_IMPLEMENTED, resource); + con.rollback(); + return resp; + } else { + // Deliver all triples with <subject> as subject. + final StreamingOutput entity = new StreamingOutput() { + @Override + public void write(OutputStream output) throws IOException, WebApplicationException { + try { + final RepositoryConnection outputCon = sesameService.getConnection();; + try { + outputCon.begin(); + ldpService.exportResource(outputCon, resource, output, format); + outputCon.commit(); + } catch (RDFHandlerException e) { + outputCon.rollback(); + throw new NoLogWebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e.getMessage()).build()); + } catch (final Throwable t) { + outputCon.rollback(); + throw t; + } finally { + outputCon.close(); + } + } catch (RepositoryException e) { + throw new WebApplicationException(e, createResponse(Response.status(Response.Status.INTERNAL_SERVER_ERROR)).entity(e).build()); + } } - } - }; - return createResponse(Response.Status.OK, resource).entity(entity).type(format.getDefaultMIMEType()); + }; + final Response.ResponseBuilder resp = createResponse(con, Response.Status.OK, resource).entity(entity).type(format.getDefaultMIMEType()); + con.commit(); + return resp; + } + } catch (final Throwable t) { + con.rollback(); + throw t; + } finally { + con.close(); } } @@ -136,82 +168,131 @@ public class LdpWebService { final String container = getResourceUri(uriInfo); log.debug("POST to LDPC <{}>", container); - // TODO: Check if resource (container) exists - log.warn("NOT CHECKING EXISTENCE OF <{}>", container); - - final String localName; - if (StringUtils.isBlank(slug)) { - /* Sec. 6.4.9) */ - localName = UUID.randomUUID().toString(); - } else { - // Honor client wishes from Slug-header (Sec. 6.4.11) - // http://www.ietf.org/rfc/rfc5023.txt - log.trace("Slug-Header is '{}'", slug); - localName = LdpWebServiceUtils.urify(slug); - log.trace("Slug-Header urified: {}", localName); - } - - String newResource = uriInfo.getRequestUriBuilder().path(localName).build().toString(); + final RepositoryConnection con = sesameService.getConnection(); try { - ldpService.addResource(postBody, type, container, newResource); - return createResponse(Response.Status.CREATED, container).location(java.net.URI.create(newResource)).build(); - } catch (IOException | RDFParseException e) { - return createResponse(Response.Status.BAD_REQUEST, container).entity(e.getClass().getSimpleName() + ": "+ e.getMessage()).build(); - } catch (UnsupportedRDFormatException e) { - return createResponse(Response.Status.UNSUPPORTED_MEDIA_TYPE, container).entity(e).build(); + con.begin(); + // TODO: Check if resource (container) exists + log.warn("NOT CHECKING EXISTENCE OF <{}>", container); + + final String localName; + if (StringUtils.isBlank(slug)) { + /* Sec. 6.4.9) */ + localName = UUID.randomUUID().toString(); + } else { + // Honor client wishes from Slug-header (Sec. 6.4.11) + // http://www.ietf.org/rfc/rfc5023.txt + log.trace("Slug-Header is '{}'", slug); + localName = LdpWebServiceUtils.urify(slug); + log.trace("Slug-Header urified: {}", localName); + } + + String newResource = uriInfo.getRequestUriBuilder().path(localName).build().toString(); + try { + ldpService.addResource(con, container, newResource, type, postBody); + final Response.ResponseBuilder resp = createResponse(con, Response.Status.CREATED, container).location(java.net.URI.create(newResource)); + con.commit(); + return resp.build(); + } catch (IOException | RDFParseException e) { + final Response.ResponseBuilder resp = createResponse(con, Response.Status.BAD_REQUEST, container).entity(e.getClass().getSimpleName() + ": " + e.getMessage()); + con.rollback(); + return resp.build(); + } catch (UnsupportedRDFormatException e) { + final Response.ResponseBuilder resp = createResponse(con, Response.Status.UNSUPPORTED_MEDIA_TYPE, container).entity(e); + con.rollback(); + return resp.build(); + } + } catch (final Throwable t) { + con.rollback(); + throw t; + } finally { + con.close(); } } + /** + * Handle PUT (Sec. 5.5, Sec. 6.5) + */ @PUT public Response PUT(@Context UriInfo uriInfo, @Context Request request, @HeaderParam(HttpHeaders.IF_MATCH) EntityTag eTag, @HeaderParam(HttpHeaders.CONTENT_TYPE) MediaType type, InputStream postBody) throws RepositoryException { - /* - * Handle PUT (Sec. 5.5, Sec. 6.5) - */ final String resource = getResourceUri(uriInfo); log.debug("PUT to <{}>", resource); - log.warn("NOT CHECKING EXISTENCE OF <{}>", resource); + final RepositoryConnection con = sesameService.getConnection(); + try { + con.begin(); - if (eTag == null) { - // check for If-Match header (ETag) -> 428 Precondition Required (Sec. 5.5.3) - log.trace("No If-Match header, but that's a MUST"); - return createResponse(428, resource).build(); - } else { - // check ETag -> 412 Precondition Failed (Sec. 5.5.3) - 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, resource).build(); + if (!ldpService.exists(con, resource)) { + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NOT_FOUND, resource); + con.rollback(); + return resp.build(); } - } - /* - * TODO: PUT implementation - * - * clients should not be allowed to update LDPC-membership triples -> 409 Conflict (Sec. 6.5.1) - * - * if the target resource exists, replace ALL data of the target. - */ - return createResponse(Response.Status.NOT_IMPLEMENTED, resource).build(); - } + if (eTag == null) { + // check for If-Match header (ETag) -> 428 Precondition Required (Sec. 5.5.3) + log.trace("No If-Match header, but that's a MUST"); + final Response.ResponseBuilder resp = createResponse(con, 428, resource); + con.rollback(); + return resp.build(); + } else { + // check ETag -> 412 Precondition Failed (Sec. 5.5.3) + log.trace("Checking If-Match: {}", eTag); + EntityTag hasTag = ldpService.generateETag(con, resource); + if (!EntityTagUtils.equals(eTag, hasTag)) { + log.trace("If-Match header did not match, expected {}", hasTag); + final Response.ResponseBuilder resp = createResponse(con, Response.Status.PRECONDITION_FAILED, resource); + con.rollback(); + return resp.build(); + } + } + /* + * TODO: PUT implementation + * + * clients should not be allowed to update LDPC-membership triples -> 409 Conflict (Sec. 6.5.1) + * + * if the target resource exists, replace ALL data of the target. + */ + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NOT_IMPLEMENTED, resource); + con.rollback(); + return resp.build(); + } catch (final Throwable t) { + con.rollback(); + throw t; + } finally { + con.close(); + } + } + /** + * Handle delete (Sec. 5.6, Sec. 6.6) + */ @DELETE - public Response DELETE(@Context UriInfo uriInfo) { - /* - * Handle delete (Sec. 5.6, Sec. 6.6) - */ + public Response DELETE(@Context UriInfo uriInfo) throws RepositoryException { final String resource = getResourceUri(uriInfo); log.debug("DELETE to <{}>", resource); + + final RepositoryConnection con = sesameService.getConnection(); try { - ldpService.deleteResource(resource); - return createResponse(Response.Status.NO_CONTENT, resource).build(); - } catch (RepositoryException e) { + con.begin(); + + if (!ldpService.exists(con, resource)) { + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NOT_FOUND, resource); + con.rollback(); + return resp.build(); + } + + ldpService.deleteResource(con, resource); + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NO_CONTENT, resource); + con.commit(); + return resp.build(); + } catch (final Throwable e) { log.error("Error deleting LDP-R: {}: {}", resource, e.getMessage()); - return createResponse(Response.Status.INTERNAL_SERVER_ERROR, resource).entity("Error deleting LDP-R: " + e.getMessage()).build(); + con.rollback(); + throw e; + } finally { + con.close(); } } @@ -223,85 +304,126 @@ public class LdpWebService { final String resource = getResourceUri(uriInfo); log.debug("PATCH to <{}>", resource); - if (!ldpService.exists(resource)) { - return createResponse(Response.Status.NOT_FOUND, resource).build(); - } + final RepositoryConnection con = sesameService.getConnection(); + try { + con.begin(); - 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, resource).build(); + if (!ldpService.exists(con, resource)) { + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NOT_FOUND, resource); + con.rollback(); + return resp.build(); } - } - // Check for the supported mime-type - if (!type.toString().equals(RdfPatchParser.MIME_TYPE)) { - log.trace("Incompatible Content-Type for PATCH: {}", type); - return createResponse(Response.Status.UNSUPPORTED_MEDIA_TYPE, resource).entity("Unknown Content-Type: " + type + "\n").build(); - } + if (eTag != null) { + // check ETag if present + log.trace("Checking If-Match: {}", eTag); + EntityTag hasTag = ldpService.generateETag(con, resource); + if (!EntityTagUtils.equals(eTag, hasTag)) { + log.trace("If-Match header did not match, expected {}", hasTag); + final Response.ResponseBuilder resp = createResponse(con, Response.Status.PRECONDITION_FAILED, resource); + con.rollback(); + return resp.build(); + } + } - try { - ldpService.patchResource(resource, postBody); - } catch (ParseException | InvalidPatchDocumentException e) { - return createResponse(Response.Status.BAD_REQUEST, resource).entity(e.getMessage() + "\n").build(); - } catch (InvalidModificationException e) { - return createResponse(422, resource).entity(e.getMessage() + "\n").build(); - } + // Check for the supported mime-type + if (!type.toString().equals(RdfPatchParser.MIME_TYPE)) { + log.trace("Incompatible Content-Type for PATCH: {}", type); + final Response.ResponseBuilder resp = createResponse(con, Response.Status.UNSUPPORTED_MEDIA_TYPE, resource).entity("Unknown Content-Type: " + type + "\n"); + con.rollback(); + return resp.build(); + } - return createResponse(Response.Status.NO_CONTENT, resource).build(); + try { + ldpService.patchResource(con, resource, postBody, false); + final Response.ResponseBuilder resp = createResponse(con, Response.Status.NO_CONTENT, resource); + con.commit(); + return resp.build(); + } catch (ParseException | InvalidPatchDocumentException e) { + final Response.ResponseBuilder resp = createResponse(con, Response.Status.BAD_REQUEST, resource).entity(e.getMessage() + "\n"); + con.rollback(); + return resp.build(); + } catch (InvalidModificationException e) { + final Response.ResponseBuilder resp = createResponse(con, 422, resource).entity(e.getMessage() + "\n"); + con.rollback(); + return resp.build(); + } + } catch (final Throwable t) { + con.rollback(); + throw t; + } finally { + con.close(); + } } + /** + * Handle OPTIONS (Sec. 5.9, Sec. 6.9) + */ @OPTIONS - public Response OPTIONS(@Context final UriInfo uriInfo) { - /* - * Handle OPTIONS (Sec. 5.9, Sec. 6.9) - */ + public Response OPTIONS(@Context final UriInfo uriInfo) throws RepositoryException { final String resource = getResourceUri(uriInfo); log.debug("OPTIONS to <{}>", resource); - log.warn("NOT CHECKING EXISTENCE OF <{}>", resource); + final RepositoryConnection con = sesameService.getConnection(); + try { + con.begin(); + + log.warn("NOT CHECKING EXISTENCE OF <{}>", resource); - Response.ResponseBuilder builder = createResponse(Response.Status.OK, resource); + Response.ResponseBuilder builder = createResponse(con, Response.Status.OK, resource); - // Sec. 5.9.2 - builder.allow("GET", "HEAD", "POST", "PATCH", "OPTIONS"); + // Sec. 5.9.2 + 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"); + // Sec. 6.4.14 / Sec. 8.1 + // builder.header("Accept-Post", "text/turtle, */*"); + builder.header("Accept-Post", "text/turtle"); - // Sec. 5.8.2 - builder.header("Accept-Patch", RdfPatchParser.MIME_TYPE); + // Sec. 5.8.2 + builder.header("Accept-Patch", RdfPatchParser.MIME_TYPE); - // TODO: Sec. 6.9.1 - //builder.link(resource, "meta"); + // TODO: Sec. 6.9.1 + //builder.link(resource, "meta"); + con.commit(); + return builder.build(); + } catch (final Throwable t) { + con.rollback(); + throw t; + } finally { + con.close(); + } - return builder.build(); } - protected Response.ResponseBuilder createResponse(int status, String resource) { - return createResponse(Response.status(status), resource); + /** + * Add all the default headers specified in LDP to the Response + * + * @param connection + * @param status the status code + * @param resource the uri/url of the resouce + * @return the provided ResponseBuilder for chaining + */ + protected Response.ResponseBuilder createResponse(RepositoryConnection connection, int status, String resource) throws RepositoryException { + return createResponse(connection, Response.status(status), resource); } /** * Add all the default headers specified in LDP to the Response + * + * @param connection * @param rb the ResponseBuilder * @param resource the uri/url of the resouce * @return the provided ResponseBuilder for chaining */ - protected Response.ResponseBuilder createResponse(Response.ResponseBuilder rb, String resource) { + protected Response.ResponseBuilder createResponse(RepositoryConnection connection, Response.ResponseBuilder rb, String resource) throws RepositoryException { + createResponse(rb); - // Link rel='describedby' (Sec. 5.2.11) - rb.link("http://wiki.apache.org/marmotta/LDPImplementationReport", "describedby"); - - try { - List<Statement> statements = ldpService.getStatements(resource); + if (ldpService.exists(connection, resource)) { + // Link rel='type' (Sec. 5.2.8, 6.2.8) + List<Statement> statements = ldpService.getStatements(connection, resource); for (Statement stmt : statements) { Value o = stmt.getObject(); if (o instanceof URI && o.stringValue().startsWith(LDP.NAMESPACE)) { @@ -310,16 +432,37 @@ public class LdpWebService { } // ETag (Sec. 5.2.7) - rb.tag(ldpService.generateETag(resource)); - } catch (RepositoryException e) { - log.error("Could not set ldp-response headers", e); + rb.tag(ldpService.generateETag(connection, resource)); + + // Last modified date + rb.lastModified(ldpService.getLastModified(connection, resource)); } return new RB(rb); } - protected Response.ResponseBuilder createResponse(Response.Status status, String resource) { - return createResponse(status.getStatusCode(), resource); + /** + * Add the non-resource related headers specified in LDP to the provided ResponseBuilder + * @param rb the ResponseBuilder to decorate + * @return the updated ResponseBuilder for chaining + */ + private Response.ResponseBuilder createResponse(Response.ResponseBuilder rb) { + // Link rel='describedby' (Sec. 5.2.11) + rb.link("http://wiki.apache.org/marmotta/LDPImplementationReport", "describedby"); + + return rb; + } + + /** + * Add all the default headers specified in LDP to the Response + * + * @param connection + * @param status the StatusCode + * @param resource the uri/url of the resouce + * @return the provided ResponseBuilder for chaining + */ + protected Response.ResponseBuilder createResponse(RepositoryConnection connection, Response.Status status, String resource) throws RepositoryException { + return createResponse(connection, status.getStatusCode(), resource); } protected String getResourceUri(UriInfo uriInfo) { http://git-wip-us.apache.org/repos/asf/marmotta/blob/370a49b6/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 6094968..80a42c3 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 @@ -35,7 +35,9 @@ import org.openrdf.rio.RDFFormat; import org.openrdf.sail.memory.MemoryStore; /** - * Created by jakob on 2/25/14. + * Testing RdfPatchUtil + * + * @author Jakob Frank */ public class RdfPatchUtilTest { http://git-wip-us.apache.org/repos/asf/marmotta/blob/370a49b6/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserImplTest.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserImplTest.java b/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserImplTest.java new file mode 100644 index 0000000..bb3ad45 --- /dev/null +++ b/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserImplTest.java @@ -0,0 +1,91 @@ +/* + * 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.parser; + +import org.apache.marmotta.commons.vocabulary.FOAF; +import org.apache.marmotta.platform.ldp.patch.model.PatchLine; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openrdf.model.*; +import org.openrdf.model.impl.LiteralImpl; +import org.openrdf.model.impl.URIImpl; + +import java.util.Iterator; +import java.util.List; + +/** + * Testing the RdfPatchParserImpl + * + * @author Jakob Frank + */ +public class RdfPatchParserImplTest { + + + private RdfPatchParserImpl parser; + private URI alice, bob, charlie; + private Literal lcBob, ucBob; + + @Before + public void setUp() { + parser = new RdfPatchParserImpl(this.getClass().getResourceAsStream("/illustrative.rdfp")); + + alice = new URIImpl("http://example/alice"); + bob = new URIImpl("http://example/bob"); + charlie = new URIImpl("http://example/charlie"); + + lcBob = new LiteralImpl("bob"); + ucBob = new LiteralImpl("Bob"); + } + + @After + public void tearDown() { + parser = null; + } + + + @Test + public void testParsing() throws ParseException { + List<PatchLine> patchLines = parser.parsePatch(); + + Iterator<PatchLine> it = patchLines.iterator(); + + Assert.assertTrue(it.hasNext()); + checkPatchLine(it.next(), PatchLine.Operator.DELETE, bob, FOAF.name, lcBob); + + Assert.assertTrue(it.hasNext()); + checkPatchLine(it.next(), PatchLine.Operator.ADD, bob, FOAF.name, ucBob); + + Assert.assertTrue(it.hasNext()); + checkPatchLine(it.next(), PatchLine.Operator.ADD, null, FOAF.knows, alice); + + Assert.assertTrue(it.hasNext()); + checkPatchLine(it.next(), PatchLine.Operator.DELETE, null, null, charlie); + } + + private void checkPatchLine(PatchLine line, PatchLine.Operator operator, Resource subejct, URI predicate, Value object) { + Assert.assertEquals("Wrong patch operation", operator, line.getOperator()); + + Statement statement = line.getStatement(); + Assert.assertEquals("Wrong subject", subejct, statement.getSubject()); + Assert.assertEquals("Wrong predicate", predicate, statement.getPredicate()); + Assert.assertEquals("Wrong object", object, statement.getObject()); + } + +} http://git-wip-us.apache.org/repos/asf/marmotta/blob/370a49b6/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserTest.java ---------------------------------------------------------------------- diff --git a/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserTest.java b/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserTest.java deleted file mode 100644 index b543061..0000000 --- a/platform/marmotta-ldp/src/test/java/org/apache/marmotta/platform/ldp/patch/parser/RdfPatchParserTest.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.marmotta.platform.ldp.patch.parser; - -import org.apache.marmotta.commons.vocabulary.FOAF; -import org.apache.marmotta.platform.ldp.patch.model.PatchLine; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.openrdf.model.*; -import org.openrdf.model.impl.LiteralImpl; -import org.openrdf.model.impl.URIImpl; - -import java.util.Iterator; -import java.util.List; - -/** - * Created by jakob on 2/24/14. - */ -public class RdfPatchParserTest { - - - private RdfPatchParser parser; - private URI alice, bob, charlie; - private Literal lcBob, ucBob; - - @Before - public void setUp() { - parser = new RdfPatchParser(this.getClass().getResourceAsStream("/illustrative.rdfp")); - - alice = new URIImpl("http://example/alice"); - bob = new URIImpl("http://example/bob"); - charlie = new URIImpl("http://example/charlie"); - - lcBob = new LiteralImpl("bob"); - ucBob = new LiteralImpl("Bob"); - } - - @After - public void tearDown() { - parser = null; - } - - - @Test - public void testParsing() throws ParseException { - List<PatchLine> patchLines = parser.parsePatch(); - - Iterator<PatchLine> it = patchLines.iterator(); - - Assert.assertTrue(it.hasNext()); - checkPatchLine(it.next(), PatchLine.Operator.DELETE, bob, FOAF.name, lcBob); - - Assert.assertTrue(it.hasNext()); - checkPatchLine(it.next(), PatchLine.Operator.ADD, bob, FOAF.name, ucBob); - - Assert.assertTrue(it.hasNext()); - checkPatchLine(it.next(), PatchLine.Operator.ADD, null, FOAF.knows, alice); - - Assert.assertTrue(it.hasNext()); - checkPatchLine(it.next(), PatchLine.Operator.DELETE, null, null, charlie); - } - - private void checkPatchLine(PatchLine line, PatchLine.Operator operator, Resource subejct, URI predicate, Value object) { - Assert.assertEquals("Wrong patch operation", operator, line.getOperator()); - - Statement statement = line.getStatement(); - Assert.assertEquals("Wrong subject", subejct, statement.getSubject()); - Assert.assertEquals("Wrong predicate", predicate, statement.getPredicate()); - Assert.assertEquals("Wrong object", object, statement.getObject()); - } - -}
