/** * Create a triplestore-backed resource service. */ @Inject public TriplestoreResourceService() { this(buildRDFConnection(getConfiguration().get(CONFIG_TRIPLESTORE_RDF_LOCATION))); }
private static TriplestoreResourceService buildResourceService(final AppConfiguration config, final Environment environment) { final RDFConnection rdfConnection = TriplestoreResourceService.buildRDFConnection(config.getResources()); // Health checks environment.healthChecks().register("rdfconnection", new RDFConnectionHealthCheck(rdfConnection)); return new TriplestoreResourceService(rdfConnection); }
@Override public CompletionStage<Void> touch(final IRI identifier) { final Literal time = rdf.createLiteral(now().toString(), XSD.dateTime); return runAsync(() -> { try { rdfConnection.update(buildUpdateModificationRequest(identifier, time)); } catch (final Exception ex) { throw new RuntimeTrellisException("Could not update data for " + identifier, ex); } }); }
@Test public void testInitializeRoot() { final Instant early = now(); final TriplestoreResourceService svc = new TriplestoreResourceService(); svc.initialize(); final Resource res = svc.get(root).toCompletableFuture().join(); assertAll("Check resource", checkResource(res, root, LDP.BasicContainer, early)); assertAll("Check resource stream", checkResourceStream(res, 0L, 5L, 0L, 0L, 0L)); }
@Test public void testPutLdpRsWithoutBaseUrl() throws Exception { final TriplestoreResourceService svc = new TriplestoreResourceService( connect(wrap(rdf.createDataset().asJenaDatasetGraph()))); svc.initialize(); final Dataset dataset = rdf.createDataset(); dataset.add(Trellis.PreferUserManaged, resource, DC.title, rdf.createLiteral("title")); dataset.add(Trellis.PreferAudit, rdf.createBlankNode(), RDF.type, AS.Create); final Instant later = meanwhile(); assertDoesNotThrow(() -> allOf( svc.create(builder(resource).interactionModel(LDP.RDFSource).container(root).build(), dataset) .toCompletableFuture(), svc.touch(root).toCompletableFuture()).join(), "Unsuccessful create operation!"); allOf( svc.get(resource).thenAccept(checkResource(later, LDP.RDFSource, 1L, 1L, 0L)).toCompletableFuture(), svc.get(root).thenAccept(checkRoot(later, 1L)).toCompletableFuture()).join(); }
@Test public void testRDFConnectionError() throws Exception { final TriplestoreResourceService svc = new TriplestoreResourceService(mockRdfConnection); svc.initialize(); doThrow(new RuntimeException("Expected exception")).when(mockRdfConnection).update(any(UpdateRequest.class)); doThrow(new RuntimeException("Expected exception")).when(mockRdfConnection) .loadDataset(any(org.apache.jena.query.Dataset.class)); assertThrows(ExecutionException.class, () -> svc.create(builder(resource).interactionModel(LDP.RDFSource).container(root).build(), rdf.createDataset()).toCompletableFuture().get(), "No (create) exception with dropped backend connection!"); assertThrows(ExecutionException.class, () -> svc.add(resource, rdf.createDataset()).toCompletableFuture().get(), "No (add) exception with dropped backend connection!"); assertThrows(ExecutionException.class, () -> svc.delete(builder(resource).interactionModel(LDP.RDFSource).container(root).build()) .toCompletableFuture().get(), "No (delete) exception with dropped backend connection!"); assertThrows(ExecutionException.class, () -> svc.touch(resource).toCompletableFuture().get(), "No (touch) exception with dropped backend connection!"); }
@Test public void testUpdateRoot() throws Exception { final Instant early = now(); final TriplestoreResourceService svc = new TriplestoreResourceService( connect(wrap(rdf.createDataset().asJenaDatasetGraph()))); svc.initialize(); final Resource res1 = svc.get(root).toCompletableFuture().join(); assertAll("Check resource", checkResource(res1, root, LDP.BasicContainer, early)); assertAll("Check resource stream", checkResourceStream(res1, 0L, 5L, 0L, 0L, 0L)); final Dataset data = rdf.createDataset(); svc.get(root).thenAccept(res -> res.stream().filter(q -> !q.getGraphName().filter(Trellis.PreferServerManaged::equals).isPresent()) .forEach(data::add)).toCompletableFuture().join(); data.add(Trellis.PreferUserManaged, root, RDFS.label, rdf.createLiteral("Resource Label")); data.add(Trellis.PreferUserManaged, root, RDFS.seeAlso, rdf.createIRI("http://example.com")); data.add(Trellis.PreferUserManaged, root, LDP.inbox, rdf.createIRI("http://ldn.example.com/")); data.add(Trellis.PreferUserManaged, root, RDF.type, rdf.createLiteral("Some weird type")); data.add(Trellis.PreferAudit, rdf.createBlankNode(), RDF.type, AS.Update); final Instant later = meanwhile(); assertDoesNotThrow(() -> svc.replace(builder(root).interactionModel(LDP.BasicContainer).build(), data) .toCompletableFuture().join(), "Unsuccessful replace operation!"); final Resource res2 = svc.get(root).toCompletableFuture().join(); assertAll("Check resource", checkResource(res2, root, LDP.BasicContainer, later)); assertAll("Check resource stream", checkResourceStream(res2, 4L, 5L, 1L, 0L, 0L)); }
SUBJECT, PREDICATE, OBJECT))))); req.add(new UpdateDeleteWhere(new QuadAcc(singletonList(new Quad( getAclIRI(identifier), SUBJECT, PREDICATE, OBJECT))))); req.add(new UpdateDeleteWhere(new QuadAcc(asList( new Quad(rdf.asJenaNode(PreferServerManaged), rdf.asJenaNode(identifier), .map(t -> new Quad(rdf.asJenaNode(identifier), rdf.asJenaTriple(t))).forEach(sink::addQuad)); dataset.getGraph(PreferAccessControl).ifPresent(g -> g.stream() .map(t -> new Quad(getAclIRI(identifier), rdf.asJenaTriple(t))).forEach(sink::addQuad)); dataset.getGraph(PreferAudit).ifPresent(g -> g.stream() .map(t -> new Quad(getAuditIRI(identifier), rdf.asJenaTriple(t))).forEach(sink::addQuad));
epb.addTriple(triple(rdf.asJenaNode(root), rdf.asJenaNode(RDF.type), OBJECT)); sink.addQuad(new Quad(rdf.asJenaNode(PreferServerManaged), triple(rdf.asJenaNode(root), rdf.asJenaNode(RDF.type), rdf.asJenaNode(LDP.BasicContainer)))); sink.addQuad(new Quad(rdf.asJenaNode(PreferServerManaged), triple(rdf.asJenaNode(root), rdf.asJenaNode(DC.modified), rdf.asJenaNode(time)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.mode), rdf.asJenaNode(ACL.Read)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.mode), rdf.asJenaNode(ACL.Write)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.mode), rdf.asJenaNode(ACL.Control)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.agentClass), rdf.asJenaNode(FOAF.Agent)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.accessTo), rdf.asJenaNode(root))));
@Override public CompletionStage<Void> delete(final Metadata metadata) { LOGGER.debug("Deleting: {}", metadata.getIdentifier()); return runAsync(() -> { try (final Dataset dataset = rdf.createDataset()) { final Instant eventTime = now(); dataset.add(PreferServerManaged, metadata.getIdentifier(), DC.type, DeletedResource); dataset.add(PreferServerManaged, metadata.getIdentifier(), RDF.type, LDP.Resource); storeResource(metadata.getIdentifier(), dataset, eventTime, OperationType.DELETE); } catch (final Exception ex) { throw new RuntimeTrellisException("Error deleting resource: " + metadata.getIdentifier(), ex); } }); }
@Test public void testNoRoot() { final ResourceService svc = new TriplestoreResourceService(); assertEquals(MISSING_RESOURCE, svc.get(rdf.createIRI(TRELLIS_DATA_PREFIX)).toCompletableFuture().join(), "Not a missing resource!"); }
private void storeResource(final IRI identifier, final Dataset dataset, final Instant eventTime, final OperationType type) { final Literal time = rdf.createLiteral(eventTime.toString(), XSD.dateTime); try { rdfConnection.update(buildUpdateRequest(identifier, time, dataset, type)); } catch (final Exception ex) { throw new RuntimeTrellisException("Could not update data for " + identifier, ex); } }
@Override public CompletionStage<Void> replace(final Metadata metadata, final Dataset dataset) { LOGGER.debug("Persisting: {}", metadata.getIdentifier()); return runAsync(() -> createOrReplace(metadata, dataset, OperationType.REPLACE)); }
/** * This code is equivalent to the SPARQL query below. * * <p><pre><code> * WITH trellis:PreferServerManaged * DELETE { IDENTIFIER dc:modified ?time } * INSERT { IDENTIFIER dc:modified TIME } * WHERE { IDENTIFIER dc:modified ?time } . * </code></pre></p> */ private UpdateRequest buildUpdateModificationRequest(final IRI identifier, final Literal time) { final UpdateRequest req = new UpdateRequest(); final Var modified = Var.alloc(MODIFIED); final UpdateDeleteInsert modify = new UpdateDeleteInsert(); modify.setWithIRI(rdf.asJenaNode(PreferServerManaged)); modify.getDeleteAcc().addTriple(triple(rdf.asJenaNode(identifier), rdf.asJenaNode(DC.modified), modified)); modify.getInsertAcc().addTriple(triple(rdf.asJenaNode(identifier), rdf.asJenaNode(DC.modified), rdf.asJenaNode(time))); final ElementGroup eg = new ElementGroup(); final ElementPathBlock epb = new ElementPathBlock(); epb.addTriple(triple(rdf.asJenaNode(identifier), rdf.asJenaNode(DC.modified), modified)); eg.addElement(epb); modify.setElement(eg); req.add(modify); return req; }
public SimpleServiceBundler() { triplestoreService.initialize(); }
@Test public void testAddAuditTriples() throws Exception { final TriplestoreResourceService svc = new TriplestoreResourceService( connect(wrap(rdf.createDataset().asJenaDatasetGraph()))); svc.initialize(); final Dataset dataset1 = rdf.createDataset(); final Dataset dataset2 = rdf.createDataset(); final BlankNode bnode = rdf.createBlankNode(); dataset1.add(Trellis.PreferUserManaged, resource, DC.title, rdf.createLiteral("resource")); dataset1.add(Trellis.PreferUserManaged, resource, DC.alternative, rdf.createLiteral("alt title")); dataset2.add(Trellis.PreferAudit, resource, PROV.wasGeneratedBy, bnode); dataset2.add(Trellis.PreferAudit, bnode, RDF.type, AS.Create); final Instant later = meanwhile(); assertDoesNotThrow(() -> allOf( svc.create(builder(resource).interactionModel(LDP.Container).container(root).build(), dataset1) .toCompletableFuture(), svc.add(resource, dataset2).toCompletableFuture(), svc.touch(root).toCompletableFuture()).join(), "Unsuccessful create operation!"); allOf( svc.get(resource).thenAccept(checkResource(later, LDP.Container, 2L, 2L, 0L)).toCompletableFuture(), svc.get(root).thenAccept(checkRoot(later, 1L)).toCompletableFuture()).join(); }
@Test public void testInitializeRoot2() { final Instant early = now(); final JenaDataset dataset = rdf.createDataset(); dataset.add(Trellis.PreferServerManaged, root, RDF.type, LDP.BasicContainer); dataset.add(Trellis.PreferServerManaged, root, DC.modified, rdf.createLiteral(early.toString(), XSD.dateTime)); final RDFConnection rdfConnection = connect(wrap(dataset.asJenaDatasetGraph())); final TriplestoreResourceService svc = new TriplestoreResourceService(rdfConnection); svc.initialize(); final Resource res = svc.get(root).toCompletableFuture().join(); assertAll("Check resource", checkResource(res, root, LDP.BasicContainer, early)); assertAll("Check resource stream", checkResourceStream(res, 0L, 0L, 0L, 0L, 0L)); }
SUBJECT, PREDICATE, OBJECT))))); req.add(new UpdateDeleteWhere(new QuadAcc(singletonList(new Quad( getAclIRI(identifier), SUBJECT, PREDICATE, OBJECT))))); req.add(new UpdateDeleteWhere(new QuadAcc(asList( new Quad(rdf.asJenaNode(PreferServerManaged), rdf.asJenaNode(identifier), .map(t -> new Quad(rdf.asJenaNode(identifier), rdf.asJenaTriple(t))).forEach(sink::addQuad)); dataset.getGraph(PreferAccessControl).ifPresent(g -> g.stream() .map(t -> new Quad(getAclIRI(identifier), rdf.asJenaTriple(t))).forEach(sink::addQuad)); dataset.getGraph(PreferAudit).ifPresent(g -> g.stream() .map(t -> new Quad(getAuditIRI(identifier), rdf.asJenaTriple(t))).forEach(sink::addQuad));
epb.addTriple(triple(rdf.asJenaNode(root), rdf.asJenaNode(RDF.type), OBJECT)); sink.addQuad(new Quad(rdf.asJenaNode(PreferServerManaged), triple(rdf.asJenaNode(root), rdf.asJenaNode(RDF.type), rdf.asJenaNode(LDP.BasicContainer)))); sink.addQuad(new Quad(rdf.asJenaNode(PreferServerManaged), triple(rdf.asJenaNode(root), rdf.asJenaNode(DC.modified), rdf.asJenaNode(time)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.mode), rdf.asJenaNode(ACL.Read)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.mode), rdf.asJenaNode(ACL.Write)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.mode), rdf.asJenaNode(ACL.Control)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.agentClass), rdf.asJenaNode(FOAF.Agent)))); sink.addQuad(new Quad(getAclIRI(root), triple(rdf.asJenaNode(auth), rdf.asJenaNode(ACL.accessTo), rdf.asJenaNode(root))));
@Override public CompletionStage<Void> delete(final Metadata metadata) { LOGGER.debug("Deleting: {}", metadata.getIdentifier()); return runAsync(() -> { try (final Dataset dataset = rdf.createDataset()) { final Instant eventTime = now(); dataset.add(PreferServerManaged, metadata.getIdentifier(), DC.type, DeletedResource); dataset.add(PreferServerManaged, metadata.getIdentifier(), RDF.type, LDP.Resource); storeResource(metadata.getIdentifier(), dataset, eventTime, OperationType.DELETE); } catch (final Exception ex) { throw new RuntimeTrellisException("Error deleting resource: " + metadata.getIdentifier(), ex); } }); }