diff --git a/build.gradle b/build.gradle index a2d291b4f..6e32d74e5 100644 --- a/build.gradle +++ b/build.gradle @@ -85,12 +85,13 @@ subprojects { jaxbVersion = '2.3.0' /* Transitive dependencies (for Karaf provisioning) */ - guavaVersion = '20.0' // via mustache 0.9.5 + transitiveGuavaVersion = '20.0' // via mustache 0.9.5 /* Testing */ apiguardianVersion = '1.0.0' awaitilityVersion = '3.1.0' commonsTextVersion = '1.3' + guavaVersion = '24.1-jre' jerseyVersion = '2.25.1' jsonVersion = '1.1.2' junitVersion = '5.1.0' diff --git a/trellis-api/src/main/java/org/trellisldp/api/ResourceService.java b/trellis-api/src/main/java/org/trellisldp/api/ResourceService.java index 923e5e2c1..90736f109 100644 --- a/trellis-api/src/main/java/org/trellisldp/api/ResourceService.java +++ b/trellis-api/src/main/java/org/trellisldp/api/ResourceService.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.Future; import java.util.stream.Stream; @@ -235,6 +236,13 @@ default Stream export(final Collection graphNames) { getInstance().createQuad(resource.getIdentifier(), q.getSubject(), q.getPredicate(), q.getObject()))); } + /** + * Return a collection of interaction models supported by this Resource Service. + * + * @return a set of supported interaction models + */ + Set supportedInteractionModels(); + /** * An identifier generator. * diff --git a/trellis-api/src/test/java/org/trellisldp/api/JoiningResourceServiceTest.java b/trellis-api/src/test/java/org/trellisldp/api/JoiningResourceServiceTest.java index 90c14be86..b95f57683 100644 --- a/trellis-api/src/test/java/org/trellisldp/api/JoiningResourceServiceTest.java +++ b/trellis-api/src/test/java/org/trellisldp/api/JoiningResourceServiceTest.java @@ -15,6 +15,7 @@ package org.trellisldp.api; import static java.time.Instant.now; +import static java.util.Collections.emptySet; import static java.util.Collections.synchronizedMap; import static java.util.concurrent.CompletableFuture.completedFuture; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,6 +31,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -149,6 +151,10 @@ public String generateIdentifier() { return "new-identifier"; } + @Override + public Set supportedInteractionModels() { + return emptySet(); + } } private final ResourceService testable = new TestableJoiningResourceService(testImmutableService, diff --git a/trellis-http/build.gradle b/trellis-http/build.gradle index 6b8f812c7..23ba9cc2a 100644 --- a/trellis-http/build.gradle +++ b/trellis-http/build.gradle @@ -23,6 +23,7 @@ dependencies { testImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion testImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion + testImplementation group: 'com.google.guava', name: 'guava', version: guavaVersion testImplementation group: 'org.apache.commons', name: 'commons-text', version: commonsTextVersion testImplementation group: 'org.apache.commons', name: 'commons-rdf-simple', version: commonsRdfVersion testImplementation group: 'org.apache.tamaya', name: 'tamaya-core', version: tamayaVersion diff --git a/trellis-http/src/main/java/org/trellisldp/http/impl/BaseLdpHandler.java b/trellis-http/src/main/java/org/trellisldp/http/impl/BaseLdpHandler.java index 523f25449..1567e803b 100644 --- a/trellis-http/src/main/java/org/trellisldp/http/impl/BaseLdpHandler.java +++ b/trellis-http/src/main/java/org/trellisldp/http/impl/BaseLdpHandler.java @@ -17,11 +17,14 @@ import static java.util.Date.from; import static java.util.Objects.nonNull; import static java.util.Optional.ofNullable; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; +import static javax.ws.rs.core.Response.Status.BAD_REQUEST; import static javax.ws.rs.core.Response.Status.GONE; import static javax.ws.rs.core.Response.status; import static org.apache.commons.rdf.api.RDFSyntax.JSONLD; import static org.apache.commons.rdf.api.RDFSyntax.NTRIPLES; import static org.apache.commons.rdf.api.RDFSyntax.TURTLE; +import static org.slf4j.LoggerFactory.getLogger; import static org.trellisldp.api.RDFUtils.getInstance; import java.time.Instant; @@ -29,25 +32,32 @@ import java.util.List; import java.util.ServiceLoader; +import javax.ws.rs.BadRequestException; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.Link; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response.ResponseBuilder; +import org.apache.commons.rdf.api.IRI; import org.apache.commons.rdf.api.RDF; import org.apache.commons.rdf.api.RDFSyntax; +import org.slf4j.Logger; import org.trellisldp.api.AuditService; import org.trellisldp.api.ConstraintService; import org.trellisldp.api.Resource; import org.trellisldp.api.ResourceService; import org.trellisldp.http.domain.LdpRequest; +import org.trellisldp.vocabulary.LDP; +import org.trellisldp.vocabulary.Trellis; /** * @author acoburn */ public class BaseLdpHandler { + private static final Logger LOGGER = getLogger(BaseLdpHandler.class); + protected static final RDF rdf = getInstance(); protected AuditService audit; @@ -122,4 +132,22 @@ protected static void checkCache(final Request request, final Instant modified, throw new WebApplicationException(builder.build()); } } + + /** + * Check that the given interaction model is supported by the + * underlying persistence layer. + * + * @param interactionModel the interaction model + * @throws BadRequestException if the interaction model is not supported + */ + protected void checkInteractionModel(final IRI interactionModel) { + if (!resourceService.supportedInteractionModels().contains(interactionModel)) { + LOGGER.error("Interaction model not supported: ", interactionModel); + throw new BadRequestException("Unsupported interaction model provided: " + interactionModel, + status(BAD_REQUEST) + .link(Trellis.UnsupportedInteractionModel.getIRIString(), LDP.constrainedBy.getIRIString()) + .entity("Unsupported interaction model provided").type(TEXT_PLAIN_TYPE).build()); + } + } + } diff --git a/trellis-http/src/main/java/org/trellisldp/http/impl/DeleteHandler.java b/trellis-http/src/main/java/org/trellisldp/http/impl/DeleteHandler.java index 811ce61a9..870524859 100644 --- a/trellis-http/src/main/java/org/trellisldp/http/impl/DeleteHandler.java +++ b/trellis-http/src/main/java/org/trellisldp/http/impl/DeleteHandler.java @@ -84,6 +84,9 @@ public ResponseBuilder deleteResource(final Resource res) { final EntityTag etag = new EntityTag(buildEtagHash(identifier, res.getModified())); checkCache(req.getRequest(), res.getModified(), etag); + // Check that the persistence layer supports LDP-R + checkInteractionModel(LDP.Resource); + LOGGER.debug("Deleting {}", identifier); try (final TrellisDataset dataset = TrellisDataset.createDataset()) { diff --git a/trellis-http/src/main/java/org/trellisldp/http/impl/PatchHandler.java b/trellis-http/src/main/java/org/trellisldp/http/impl/PatchHandler.java index 07fc6484d..4118cd87e 100644 --- a/trellis-http/src/main/java/org/trellisldp/http/impl/PatchHandler.java +++ b/trellis-http/src/main/java/org/trellisldp/http/impl/PatchHandler.java @@ -145,6 +145,9 @@ public ResponseBuilder updateResource(final Resource res) { final EntityTag etag = new EntityTag(buildEtagHash(identifier, res.getModified())); checkCache(req.getRequest(), res.getModified(), etag); + // Check that the persistence layer supports LDP-RS + checkInteractionModel(LDP.RDFSource); + LOGGER.debug("Updating {} via PATCH", identifier); final IRI graphName = ACL.equals(req.getExt()) ? PreferAccessControl : PreferUserManaged; diff --git a/trellis-http/src/main/java/org/trellisldp/http/impl/PostHandler.java b/trellis-http/src/main/java/org/trellisldp/http/impl/PostHandler.java index e363bf80d..2ea0f98d4 100644 --- a/trellis-http/src/main/java/org/trellisldp/http/impl/PostHandler.java +++ b/trellis-http/src/main/java/org/trellisldp/http/impl/PostHandler.java @@ -116,6 +116,9 @@ public ResponseBuilder createResource() { .filter(l -> l.startsWith(LDP.URI)).map(rdf::createIRI) .filter(l -> !LDP.Resource.equals(l)).orElse(defaultType); + // Verify that the persistence layer supports the specified IXN model + checkInteractionModel(ldpType); + if (ldpType.equals(LDP.NonRDFSource) && rdfSyntax.isPresent()) { throw new BadRequestException("Cannot save a NonRDFSource with RDF syntax"); } diff --git a/trellis-http/src/main/java/org/trellisldp/http/impl/PutHandler.java b/trellis-http/src/main/java/org/trellisldp/http/impl/PutHandler.java index 725d2217f..add252b04 100644 --- a/trellis-http/src/main/java/org/trellisldp/http/impl/PutHandler.java +++ b/trellis-http/src/main/java/org/trellisldp/http/impl/PutHandler.java @@ -176,6 +176,9 @@ public ResponseBuilder setResource(final Resource res) { .filter(l -> l.startsWith(LDP.URI)).map(rdf::createIRI).filter(l -> !LDP.Resource.equals(l)) .orElse(defaultType); + // Verify that the persistence layer supports the given interaction model + checkInteractionModel(ldpType); + LOGGER.debug("Using LDP Type: {}", ldpType); // It is not possible to change the LDP type to a type that is not a subclass checkInteractionModelChange(res, ldpType, isBinaryDescription); diff --git a/trellis-http/src/test/java/org/trellisldp/http/AbstractLdpResourceTest.java b/trellis-http/src/test/java/org/trellisldp/http/AbstractLdpResourceTest.java index 8aa092b98..83023d4e3 100644 --- a/trellis-http/src/test/java/org/trellisldp/http/AbstractLdpResourceTest.java +++ b/trellis-http/src/test/java/org/trellisldp/http/AbstractLdpResourceTest.java @@ -13,6 +13,7 @@ */ package org.trellisldp.http; +import static com.google.common.collect.Sets.newHashSet; import static java.nio.charset.StandardCharsets.UTF_8; import static java.time.Instant.MAX; import static java.time.Instant.ofEpochSecond; @@ -190,17 +191,12 @@ abstract class AbstractLdpResourceTest extends JerseyTest { private static final IRI childIdentifier = rdf.createIRI(TRELLIS_DATA_PREFIX + CHILD_PATH); private static final IRI deletedIdentifier = rdf.createIRI(TRELLIS_DATA_PREFIX + DELETED_PATH); private static final IRI userDeletedIdentifier = rdf.createIRI(TRELLIS_DATA_PREFIX + USER_DELETED_PATH); + private static final Set allInteractionModels = newHashSet(LDP.Resource, LDP.RDFSource, LDP.NonRDFSource, + LDP.Container, LDP.BasicContainer, LDP.DirectContainer, LDP.IndirectContainer); protected static final String BASE_URL = "http://example.org/"; - protected static final Set allModes = new HashSet<>(); - - static { - allModes.add(ACL.Append); - allModes.add(ACL.Control); - allModes.add(ACL.Read); - allModes.add(ACL.Write); - } + protected static final Set allModes = newHashSet(ACL.Append, ACL.Control, ACL.Read, ACL.Write); @Mock protected ResourceService mockResourceService; @@ -255,6 +251,7 @@ public void setUpMocks() { whenResource(mockResourceService.get(eq(root))).thenReturn(of(mockResource)); when(mockResourceService.get(eq(childIdentifier))).thenReturn(empty()); when(mockResourceService.get(eq(childIdentifier), any(Instant.class))).thenReturn(empty()); + when(mockResourceService.supportedInteractionModels()).thenReturn(allInteractionModels); whenResource(mockResourceService.get(eq(binaryIdentifier))).thenReturn(of(mockBinaryResource)); whenResource(mockResourceService.get(eq(binaryIdentifier), any(Instant.class))) .thenReturn(of(mockBinaryVersionedResource)); diff --git a/trellis-http/src/test/java/org/trellisldp/http/impl/DeleteHandlerTest.java b/trellis-http/src/test/java/org/trellisldp/http/impl/DeleteHandlerTest.java index 347e4916a..7647dae89 100644 --- a/trellis-http/src/test/java/org/trellisldp/http/impl/DeleteHandlerTest.java +++ b/trellis-http/src/test/java/org/trellisldp/http/impl/DeleteHandlerTest.java @@ -15,16 +15,20 @@ import static java.time.Instant.ofEpochSecond; import static java.util.Collections.emptyList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; import static java.util.Date.from; import static java.util.Optional.empty; import static java.util.concurrent.CompletableFuture.completedFuture; import static javax.ws.rs.core.Link.fromUri; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static javax.ws.rs.core.Response.Status.NO_CONTENT; import static javax.ws.rs.core.Response.Status.PRECONDITION_FAILED; import static javax.ws.rs.core.Response.status; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @@ -106,6 +110,7 @@ public void setUp() { final IRI iri = rdf.createIRI("trellis:repo"); when(mockResource.getModified()).thenReturn(time); when(mockResource.getIdentifier()).thenReturn(iri); + when(mockResourceService.supportedInteractionModels()).thenReturn(singleton(LDP.Resource)); when(mockResourceService.getMementos(any())).thenReturn(emptyList()); when(mockResource.getExtraLinkRelations()).thenAnswer(inv -> Stream.empty()); @@ -184,6 +189,18 @@ public void testDeleteException() throws Exception { assertEquals(INTERNAL_SERVER_ERROR, res.getStatusInfo()); } + @Test + public void testDeletePersistenceSupport() { + when(mockResourceService.supportedInteractionModels()).thenReturn(emptySet()); + final DeleteHandler handler = new DeleteHandler(mockLdpRequest, mockResourceService, mockAuditService, baseUrl); + + final BadRequestException ex = assertThrows(BadRequestException.class, () -> + handler.deleteResource(mockResource)); + assertTrue(ex.getResponse().getLinks().stream().anyMatch(link -> + link.getUri().toString().equals(Trellis.UnsupportedInteractionModel.getIRIString()) && + link.getRel().equals(LDP.constrainedBy.getIRIString()))); + assertEquals(TEXT_PLAIN_TYPE, ex.getResponse().getMediaType()); + } @Test public void testDeleteACLError() { diff --git a/trellis-http/src/test/java/org/trellisldp/http/impl/PatchHandlerTest.java b/trellis-http/src/test/java/org/trellisldp/http/impl/PatchHandlerTest.java index 3c865e335..d243ec94b 100644 --- a/trellis-http/src/test/java/org/trellisldp/http/impl/PatchHandlerTest.java +++ b/trellis-http/src/test/java/org/trellisldp/http/impl/PatchHandlerTest.java @@ -14,11 +14,14 @@ package org.trellisldp.http.impl; import static java.time.Instant.ofEpochSecond; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.stream.Stream.of; import static javax.ws.rs.core.Link.fromUri; import static javax.ws.rs.core.MediaType.TEXT_HTML_TYPE; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN_TYPE; import static javax.ws.rs.core.Response.Status.CONFLICT; import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR; import static javax.ws.rs.core.Response.Status.NO_CONTENT; @@ -128,6 +131,7 @@ public void setUp() { when(mockResource.getModified()).thenReturn(time); when(mockResource.getInteractionModel()).thenReturn(LDP.RDFSource); when(mockResource.getIdentifier()).thenReturn(identifier); + when(mockResourceService.supportedInteractionModels()).thenReturn(singleton(LDP.RDFSource)); when(mockResourceService.add(any(IRI.class), any(Session.class), any(Dataset.class))) .thenReturn(completedFuture(true)); when(mockResourceService.replace(any(IRI.class), any(Session.class), any(IRI.class), any(Dataset.class))) @@ -281,6 +285,21 @@ public void testError() { assertThrows(BadRequestException.class, () -> patchHandler.updateResource(mockResource)); } + @Test + public void testNoLdpRsSupport() { + when(mockResourceService.supportedInteractionModels()).thenReturn(emptySet()); + + final PatchHandler patchHandler = new PatchHandler(mockLdpRequest, insert, mockAuditService, + mockResourceService, mockIoService, null); + + final BadRequestException ex = assertThrows(BadRequestException.class, () -> + patchHandler.updateResource(mockResource)); + assertTrue(ex.getResponse().getLinks().stream().anyMatch(link -> + link.getUri().toString().equals(Trellis.UnsupportedInteractionModel.getIRIString()) && + link.getRel().equals(LDP.constrainedBy.getIRIString()))); + assertEquals(TEXT_PLAIN_TYPE, ex.getResponse().getMediaType()); + } + @Test public void testException() throws Exception { when(mockFuture.get()).thenThrow(new InterruptedException("Expected")); diff --git a/trellis-http/src/test/java/org/trellisldp/http/impl/PostHandlerTest.java b/trellis-http/src/test/java/org/trellisldp/http/impl/PostHandlerTest.java index 150aa1cde..13390d9fb 100644 --- a/trellis-http/src/test/java/org/trellisldp/http/impl/PostHandlerTest.java +++ b/trellis-http/src/test/java/org/trellisldp/http/impl/PostHandlerTest.java @@ -13,8 +13,10 @@ */ package org.trellisldp.http.impl; +import static com.google.common.collect.Sets.newHashSet; import static java.net.URI.create; import static java.time.Instant.ofEpochSecond; +import static java.util.Collections.emptySet; import static java.util.UUID.randomUUID; import static java.util.concurrent.CompletableFuture.completedFuture; import static javax.ws.rs.core.HttpHeaders.CONTENT_TYPE; @@ -44,6 +46,7 @@ import java.io.InputStream; import java.time.Instant; import java.util.Map; +import java.util.Set; import java.util.concurrent.Future; import java.util.function.Predicate; import java.util.stream.Stream; @@ -78,6 +81,7 @@ import org.trellisldp.http.domain.LdpRequest; import org.trellisldp.vocabulary.DC; import org.trellisldp.vocabulary.LDP; +import org.trellisldp.vocabulary.Trellis; /** * @author acoburn @@ -88,6 +92,9 @@ public class PostHandlerTest { private static final Instant time = ofEpochSecond(1496262729); private static final String baseUrl = "http://example.org/repo/"; private static final RDF rdf = getInstance(); + private static final Set allInteractionModels = newHashSet(LDP.Resource, LDP.RDFSource, + LDP.NonRDFSource, LDP.Container, LDP.BasicContainer, LDP.DirectContainer, LDP.IndirectContainer); + private File entity; @Mock @@ -120,6 +127,7 @@ public class PostHandlerTest { public void setUp() { initMocks(this); when(mockBinaryService.generateIdentifier()).thenReturn("file:" + randomUUID()); + when(mockResourceService.supportedInteractionModels()).thenReturn(allInteractionModels); when(mockResourceService.add(any(IRI.class), any(Session.class), any(Dataset.class))) .thenReturn(completedFuture(true)); when(mockResourceService.create(any(IRI.class), any(Session.class), any(IRI.class), any(Dataset.class))) @@ -261,6 +269,21 @@ public void testDefaultType5() throws IOException { assertEquals(create(baseUrl + "newresource"), res.getLocation()); } + @Test + public void testUnsupportedType() { + when(mockResourceService.supportedInteractionModels()).thenReturn(emptySet()); + when(mockRequest.getLink()).thenReturn(fromUri(LDP.Container.getIRIString()).rel("type").build()); + + final File entity = new File(getClass().getResource("/emptyData.txt").getFile()); + final PostHandler postHandler = new PostHandler(mockRequest, "newresource", entity, mockResourceService, + mockIoService, mockBinaryService, null, mockAuditService); + + final BadRequestException ex = assertThrows(BadRequestException.class, postHandler::createResource); + assertTrue(ex.getResponse().getLinks().stream().anyMatch(link -> + link.getUri().toString().equals(Trellis.UnsupportedInteractionModel.getIRIString()) && + link.getRel().equals(LDP.constrainedBy.getIRIString()))); + } + @Test public void testEntity() throws IOException { final String path = "newresource"; diff --git a/trellis-http/src/test/java/org/trellisldp/http/impl/PutHandlerTest.java b/trellis-http/src/test/java/org/trellisldp/http/impl/PutHandlerTest.java index 91c25d546..c99ac2547 100644 --- a/trellis-http/src/test/java/org/trellisldp/http/impl/PutHandlerTest.java +++ b/trellis-http/src/test/java/org/trellisldp/http/impl/PutHandlerTest.java @@ -13,7 +13,9 @@ */ package org.trellisldp.http.impl; +import static com.google.common.collect.Sets.newHashSet; import static java.time.Instant.ofEpochSecond; +import static java.util.Collections.emptySet; import static java.util.Date.from; import static java.util.Optional.empty; import static java.util.Optional.of; @@ -47,6 +49,7 @@ import java.io.File; import java.io.InputStream; import java.time.Instant; +import java.util.Set; import java.util.concurrent.Future; import java.util.function.Predicate; @@ -81,6 +84,7 @@ import org.trellisldp.audit.DefaultAuditService; import org.trellisldp.http.domain.LdpRequest; import org.trellisldp.vocabulary.LDP; +import org.trellisldp.vocabulary.Trellis; /** * @author acoburn @@ -94,6 +98,9 @@ public class PutHandlerTest { private static final String baseUrl = "http://localhost:8080/repo/"; private static final RDF rdf = getInstance(); private static final IRI identifier = rdf.createIRI("trellis:data/resource"); + private static final Set allInteractionModels = newHashSet(LDP.Resource, LDP.RDFSource, LDP.NonRDFSource, + LDP.Container, LDP.BasicContainer, LDP.DirectContainer, LDP.IndirectContainer); + private final Binary testBinary = new Binary(rdf.createIRI("file:binary.txt"), binaryTime, "text/plain", null); @Mock @@ -131,6 +138,7 @@ public void setUp() { when(mockResource.getModified()).thenReturn(time); when(mockBinaryService.generateIdentifier()).thenReturn("file:" + randomUUID()); + when(mockResourceService.supportedInteractionModels()).thenReturn(allInteractionModels); when(mockResourceService.add(any(IRI.class), any(Session.class), any(Dataset.class))) .thenReturn(completedFuture(true)); when(mockResourceService.replace(any(IRI.class), any(Session.class), any(IRI.class), any(Dataset.class))) @@ -373,6 +381,23 @@ public void testRdfToNonRDFSource() { assertFalse(res.getLinks().stream().anyMatch(hasType(LDP.NonRDFSource))); } + @Test + public void testUnsupportedType() { + when(mockResourceService.supportedInteractionModels()).thenReturn(emptySet()); + when(mockLdpRequest.getLink()).thenReturn(fromUri(LDP.Resource.getIRIString()).rel("type").build()); + when(mockLdpRequest.getContentType()).thenReturn(TEXT_TURTLE); + + final File entity = new File(getClass().getResource("/simpleTriple.ttl").getFile()); + final PutHandler putHandler = new PutHandler(mockLdpRequest, entity, mockResourceService, mockAuditService, + mockIoService, mockBinaryService, null); + + final BadRequestException ex = assertThrows(BadRequestException.class, () -> + putHandler.setResource(mockResource)); + assertTrue(ex.getResponse().getLinks().stream().anyMatch(link -> + link.getUri().toString().equals(Trellis.UnsupportedInteractionModel.getIRIString()) && + link.getRel().equals(LDP.constrainedBy.getIRIString()))); + } + @Test public void testError() { when(mockResource.getInteractionModel()).thenReturn(LDP.NonRDFSource); diff --git a/trellis-karaf/src/main/resources/features.xml b/trellis-karaf/src/main/resources/features.xml index beebff178..596ee8531 100644 --- a/trellis-karaf/src/main/resources/features.xml +++ b/trellis-karaf/src/main/resources/features.xml @@ -109,7 +109,7 @@ trellis-api trellis-vocabulary - mvn:com.google.guava/guava/${guavaVersion} + mvn:com.google.guava/guava/${transitiveGuavaVersion} mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.mustache-compiler/${mustacheVersion} mvn:org.apache.tamaya/tamaya-api/${tamayaVersion} diff --git a/trellis-triplestore/src/main/java/org/trellisldp/triplestore/TriplestoreResourceService.java b/trellis-triplestore/src/main/java/org/trellisldp/triplestore/TriplestoreResourceService.java index 038d54ca0..101f86fd3 100644 --- a/trellis-triplestore/src/main/java/org/trellisldp/triplestore/TriplestoreResourceService.java +++ b/trellis-triplestore/src/main/java/org/trellisldp/triplestore/TriplestoreResourceService.java @@ -19,12 +19,14 @@ import static java.util.Collections.singletonList; import static java.util.Collections.sort; import static java.util.Collections.unmodifiableList; +import static java.util.Collections.unmodifiableSet; import static java.util.Objects.nonNull; import static java.util.Objects.requireNonNull; import static java.util.Optional.of; import static java.util.Optional.ofNullable; import static java.util.concurrent.CompletableFuture.supplyAsync; import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; import static java.util.stream.Stream.builder; import static java.util.stream.Stream.empty; import static org.apache.commons.lang3.Range.between; @@ -50,6 +52,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.concurrent.Future; import java.util.function.Predicate; import java.util.function.Supplier; @@ -121,6 +124,7 @@ public class TriplestoreResourceService extends DefaultAuditService implements R private final RDFConnection rdfConnection; private final Optional eventService; private final Optional mementoService; + private final Set supportedIxnModels; /** * Create a triplestore-backed resource service. @@ -138,6 +142,8 @@ public TriplestoreResourceService(final RDFConnection rdfConnection, final Ident this.supplier = identifierService.getSupplier(); this.eventService = ofNullable(eventService); this.mementoService = ofNullable(mementoService); + this.supportedIxnModels = unmodifiableSet(asList(LDP.Resource, LDP.RDFSource, LDP.NonRDFSource, LDP.Container, + LDP.BasicContainer, LDP.DirectContainer, LDP.IndirectContainer).stream().collect(toSet())); init(); } @@ -684,6 +690,11 @@ public Future add(final IRI id, final Session session, final Dataset da }); } + @Override + public Set supportedInteractionModels() { + return supportedIxnModels; + } + /** * TODO Replace when COMMONSRDF-74 is released. * diff --git a/trellis-vocabulary/src/main/java/org/trellisldp/vocabulary/Trellis.java b/trellis-vocabulary/src/main/java/org/trellisldp/vocabulary/Trellis.java index 6e0ad5558..2ded4f928 100644 --- a/trellis-vocabulary/src/main/java/org/trellisldp/vocabulary/Trellis.java +++ b/trellis-vocabulary/src/main/java/org/trellisldp/vocabulary/Trellis.java @@ -48,6 +48,7 @@ public final class Trellis { public static final IRI PreferAudit = createIRI(URI + "PreferAudit"); public static final IRI PreferServerManaged = createIRI(URI + "PreferServerManaged"); public static final IRI PreferUserManaged = createIRI(URI + "PreferUserManaged"); + public static final IRI UnsupportedInteractionModel = createIRI(URI + "UnsupportedInteractionModel"); private Trellis() { // prevent instantiation