Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions jena/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.inrupt.client</groupId>
<artifactId>inrupt-client-jackson</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
Expand Down
109 changes: 87 additions & 22 deletions jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,13 @@
*/
package com.inrupt.client.jena;

import com.inrupt.client.ClientHttpException;
import com.inrupt.client.Response;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;

import org.apache.jena.atlas.web.ContentType;
import org.apache.jena.graph.Graph;
Expand All @@ -44,13 +46,20 @@ public final class JenaBodyHandlers {

private static final String CONTENT_TYPE = "Content-Type";

/**
* Populate a Jena {@link Model} with an HTTP response body.
*
* @return an HTTP body handler
*/
public static Response.BodyHandler<Model> ofModel() {
return responseInfo -> responseInfo.headers().firstValue(CONTENT_TYPE)
private static void throwOnError(final Response.ResponseInfo responseInfo) {
if (!Response.isSuccess(responseInfo.statusCode())) {
throw new ClientHttpException(
"Could not map to a Jena entity.",
responseInfo.uri(),
responseInfo.statusCode(),
responseInfo.headers(),
new String(responseInfo.body().array(), StandardCharsets.UTF_8)
);
}
}

private static Model responseToModel(final Response.ResponseInfo responseInfo) {
return responseInfo.headers().firstValue(CONTENT_TYPE)
.map(JenaBodyHandlers::toJenaLang).map(lang -> {
try (final var input = new ByteArrayInputStream(responseInfo.body().array())) {
final var model = ModelFactory.createDefaultModel();
Expand All @@ -65,12 +74,29 @@ public static Response.BodyHandler<Model> ofModel() {
}

/**
* Populate a Jena {@link Graph} with an HTTP response.
* Populate a Jena {@link Model} with an HTTP response body.
*
* @return an HTTP body handler
* @deprecated Use {@link JenaBodyHandlers#ofJenaModel()} instead for consistent HTTP error handling.
*/
public static Response.BodyHandler<Graph> ofGraph() {
return responseInfo -> responseInfo.headers().firstValue(CONTENT_TYPE)
public static Response.BodyHandler<Model> ofModel() {
return JenaBodyHandlers::responseToModel;
}

/**
* Populate a Jena {@link Model} with an HTTP response body.
*
* @return an HTTP body handler
*/
public static Response.BodyHandler<Model> ofJenaModel() {
return responseInfo -> {
JenaBodyHandlers.throwOnError(responseInfo);
return JenaBodyHandlers.responseToModel(responseInfo);
};
}

private static Graph responseToGraph(final Response.ResponseInfo responseInfo) {
return responseInfo.headers().firstValue(CONTENT_TYPE)
.map(JenaBodyHandlers::toJenaLang).map(lang -> {
try (final var input = new ByteArrayInputStream(responseInfo.body().array())) {
final var graph = GraphMemFactory.createDefaultGraph();
Expand All @@ -84,24 +110,63 @@ public static Response.BodyHandler<Graph> ofGraph() {
.orElseGet(GraphMemFactory::createDefaultGraph);
}

/**
* Populate a Jena {@link Graph} with an HTTP response.
*
* @return an HTTP body handler
* @deprecated Use {@link JenaBodyHandlers#ofJenaGraph} instead for consistent HTTP error handling.
*/
public static Response.BodyHandler<Graph> ofGraph() {
return JenaBodyHandlers::responseToGraph;
}

/**
* Populate a Jena {@link Graph} with an HTTP response.
*
* @return an HTTP body handler
*/
public static Response.BodyHandler<Graph> ofJenaGraph() {
return responseInfo -> {
JenaBodyHandlers.throwOnError(responseInfo);
return JenaBodyHandlers.responseToGraph(responseInfo);
};
}

private static Dataset responseToDataset(final Response.ResponseInfo responseInfo) {
return responseInfo.headers().firstValue(CONTENT_TYPE)
.map(JenaBodyHandlers::toJenaLang).map(lang -> {
try (final var input = new ByteArrayInputStream(responseInfo.body().array())) {
final var dataset = DatasetFactory.create();
RDFDataMgr.read(dataset, input, responseInfo.uri().toString(), lang);
return dataset;
} catch (final IOException ex) {
throw new UncheckedIOException(
"An I/O error occurred while data was read from the InputStream into a Dataset", ex);
}
})
.orElseGet(DatasetFactory::create);
}

/**
* Populate a Jena {@link Dataset} with an HTTP response.
*
* @return an HTTP body handler
* @deprecated Use {@link JenaBodyHandlers#ofJenaDataset} instead for consistent HTTP error handling.
*/
public static Response.BodyHandler<Dataset> ofDataset() {
return responseInfo -> responseInfo.headers().firstValue(CONTENT_TYPE)
.map(JenaBodyHandlers::toJenaLang).map(lang -> {
try (final var input = new ByteArrayInputStream(responseInfo.body().array())) {
final var dataset = DatasetFactory.create();
RDFDataMgr.read(dataset, input, responseInfo.uri().toString(), lang);
return dataset;
} catch (final IOException ex) {
throw new UncheckedIOException(
"An I/O error occurred while data was read from the InputStream into a Dataset", ex);
}
})
.orElseGet(DatasetFactory::create);
return JenaBodyHandlers::responseToDataset;
}

/**
* Populate a Jena {@link Dataset} with an HTTP response.
*
* @return an HTTP body handler
*/
public static Response.BodyHandler<Dataset> ofJenaDataset() {
return responseInfo -> {
JenaBodyHandlers.throwOnError(responseInfo);
return JenaBodyHandlers.responseToDataset(responseInfo);
};
}

static Lang toJenaLang(final String mediaType) {
Expand Down
100 changes: 84 additions & 16 deletions jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import static org.junit.jupiter.api.Assertions.*;

import com.inrupt.client.ClientHttpException;
import com.inrupt.client.Request;
import com.inrupt.client.Response;
import com.inrupt.client.spi.HttpService;
Expand All @@ -32,6 +33,7 @@
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;

import org.apache.jena.graph.NodeFactory;
Expand All @@ -57,14 +59,14 @@ static void teardown() {
}

@Test
void testOfModelHandler() throws IOException,
void testOfJenaModelHandler() throws IOException,
InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/oneTriple"))
.GET()
.build();

final var response = client.send(request, JenaBodyHandlers.ofModel())
final var response = client.send(request, JenaBodyHandlers.ofJenaModel())
.toCompletableFuture().join();

assertEquals(200, response.statusCode());
Expand All @@ -78,15 +80,15 @@ void testOfModelHandler() throws IOException,
}

@Test
void testOfModelHandlerAsync() throws IOException,
void testOfJenaModelHandlerAsync() throws IOException,
InterruptedException, ExecutionException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/oneTriple"))
.header("Accept", "text/turtle")
.GET()
.build();

final var asyncResponse = client.send(request, JenaBodyHandlers.ofModel());
final var asyncResponse = client.send(request, JenaBodyHandlers.ofJenaModel());

final int statusCode = asyncResponse.thenApply(Response::statusCode).toCompletableFuture().join();
assertEquals(200, statusCode);
Expand All @@ -101,13 +103,13 @@ void testOfModelHandlerAsync() throws IOException,
}

@Test
void testOfModelHandlerWithURL() throws IOException, InterruptedException {
void testOfJenaModelHandlerWithURL() throws IOException, InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/example"))
.GET()
.build();

final var response = client.send(request, JenaBodyHandlers.ofModel())
final var response = client.send(request, JenaBodyHandlers.ofJenaModel())
.toCompletableFuture().join();

assertEquals(200, response.statusCode());
Expand All @@ -120,14 +122,36 @@ void testOfModelHandlerWithURL() throws IOException, InterruptedException {
}

@Test
void testOfDatasetHandler() throws IOException,
void testOfJenaModelHandlerError() throws IOException,
InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/error"))
.GET()
.build();

final CompletionException completionException = assertThrows(
CompletionException.class,
() -> client.send(request, JenaBodyHandlers.ofJenaModel()).toCompletableFuture().join()
);

final ClientHttpException httpException = (ClientHttpException) completionException.getCause();

assertEquals(429, httpException.getProblemDetails().getStatus());
assertEquals("Too Many Requests", httpException.getProblemDetails().getTitle());
assertEquals("Some details", httpException.getProblemDetails().getDetails());
assertEquals("https://example.org/type", httpException.getProblemDetails().getType().toString());
assertEquals("https://example.org/instance", httpException.getProblemDetails().getInstance().toString());
}

@Test
void testOfJenaDatasetHandler() throws IOException,
InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/oneTriple"))
.GET()
.build();

final var response = client.send(request, JenaBodyHandlers.ofDataset())
final var response = client.send(request, JenaBodyHandlers.ofJenaDataset())
.toCompletableFuture().join();

assertEquals(200, response.statusCode());
Expand All @@ -142,13 +166,13 @@ void testOfDatasetHandler() throws IOException,
}

@Test
void testOfDatasetHandlerWithURL() throws IOException, InterruptedException {
void testOfJenaDatasetHandlerWithURL() throws IOException, InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/example"))
.GET()
.build();

final var response = client.send(request, JenaBodyHandlers.ofDataset())
final var response = client.send(request, JenaBodyHandlers.ofJenaDataset())
.toCompletableFuture().join();

assertEquals(200, response.statusCode());
Expand All @@ -163,15 +187,37 @@ void testOfDatasetHandlerWithURL() throws IOException, InterruptedException {
}

@Test
void testOfGraphHandlerAsync() throws IOException,
void testOfJenaDatasetHandlerError() throws IOException,
InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/error"))
.GET()
.build();

final CompletionException completionException = assertThrows(
CompletionException.class,
() -> client.send(request, JenaBodyHandlers.ofJenaDataset()).toCompletableFuture().join()
);

final ClientHttpException httpException = (ClientHttpException) completionException.getCause();

assertEquals(429, httpException.getProblemDetails().getStatus());
assertEquals("Too Many Requests", httpException.getProblemDetails().getTitle());
assertEquals("Some details", httpException.getProblemDetails().getDetails());
assertEquals("https://example.org/type", httpException.getProblemDetails().getType().toString());
assertEquals("https://example.org/instance", httpException.getProblemDetails().getInstance().toString());
}

@Test
void testOfJenaGraphHandlerAsync() throws IOException,
InterruptedException, ExecutionException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/oneTriple"))
.header("Accept", "text/turtle")
.GET()
.build();

final var asyncResponse = client.send(request, JenaBodyHandlers.ofGraph());
final var asyncResponse = client.send(request, JenaBodyHandlers.ofJenaGraph());

final int statusCode = asyncResponse.thenApply(Response::statusCode).toCompletableFuture().join();
assertEquals(200, statusCode);
Expand All @@ -186,14 +232,14 @@ void testOfGraphHandlerAsync() throws IOException,
}

@Test
void testOfGraphHandler() throws IOException,
void testOfJenaGraphHandler() throws IOException,
InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/oneTriple"))
.GET()
.build();

final var response = client.send(request, JenaBodyHandlers.ofGraph())
final var response = client.send(request, JenaBodyHandlers.ofJenaGraph())
.toCompletableFuture().join();

assertEquals(200, response.statusCode());
Expand All @@ -207,13 +253,13 @@ void testOfGraphHandler() throws IOException,
}

@Test
void testOfGraphHandlerWithURL() throws IOException, InterruptedException {
void testOfJenaGraphHandlerWithURL() throws IOException, InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/example"))
.GET()
.build();

final var response = client.send(request, JenaBodyHandlers.ofGraph())
final var response = client.send(request, JenaBodyHandlers.ofJenaGraph())
.toCompletableFuture().join();

assertEquals(200, response.statusCode());
Expand All @@ -225,4 +271,26 @@ void testOfGraphHandlerWithURL() throws IOException, InterruptedException {
null)
);
}

@Test
void testOfJenaGraphHandlerError() throws IOException,
InterruptedException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/error"))
.GET()
.build();

final CompletionException completionException = assertThrows(
CompletionException.class,
() -> client.send(request, JenaBodyHandlers.ofJenaGraph()).toCompletableFuture().join()
);

final ClientHttpException httpException = (ClientHttpException) completionException.getCause();

assertEquals(429, httpException.getProblemDetails().getStatus());
assertEquals("Too Many Requests", httpException.getProblemDetails().getTitle());
assertEquals("Some details", httpException.getProblemDetails().getDetails());
assertEquals("https://example.org/type", httpException.getProblemDetails().getType().toString());
assertEquals("https://example.org/instance", httpException.getProblemDetails().getInstance().toString());
}
}
Loading