From c384bca8501131270c51ecfa53ef9f95d2af678d Mon Sep 17 00:00:00 2001 From: Nicolas Ayral Seydoux Date: Fri, 12 Apr 2024 22:51:15 +0200 Subject: [PATCH 1/5] JCL-402: HTTP Error handling in Jena body handler This deprecates the current methods in `JenaBodyHandlers`, and replaces them with similar method throwing an appropriate exception with error details on HTTP error instead of returning an empty dataset. The new method now have `Jena` in their name for this not to be a breaking change: `ofModel` becomes `ofJenaModel`, etc. --- jena/pom.xml | 6 + .../inrupt/client/jena/JenaBodyHandlers.java | 148 +++++++++++++++--- .../client/jena/JenaBodyHandlersTest.java | 100 ++++++++++-- .../inrupt/client/test/RdfMockService.java | 22 ++- 4 files changed, 234 insertions(+), 42 deletions(-) diff --git a/jena/pom.xml b/jena/pom.xml index 5fe41539dde..99f1602e647 100644 --- a/jena/pom.xml +++ b/jena/pom.xml @@ -67,6 +67,12 @@ ${slf4j.version} test + + com.inrupt.client + inrupt-client-jackson + ${project.version} + test + org.wiremock wiremock diff --git a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java index d734b7906fe..88378f55f04 100644 --- a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java +++ b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java @@ -20,7 +20,11 @@ */ package com.inrupt.client.jena; +import com.inrupt.client.ClientHttpException; +import com.inrupt.client.ProblemDetails; import com.inrupt.client.Response; +import com.inrupt.client.spi.JsonService; +import com.inrupt.client.spi.ServiceProvider; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -43,14 +47,28 @@ public final class JenaBodyHandlers { private static final String CONTENT_TYPE = "Content-Type"; + private static JsonService jsonService; + private static boolean isJsonServiceInitialized = false; - /** - * Populate a Jena {@link Model} with an HTTP response body. - * - * @return an HTTP body handler - */ - public static Response.BodyHandler ofModel() { - return responseInfo -> responseInfo.headers().firstValue(CONTENT_TYPE) + private static JsonService getJsonService() { + if (JenaBodyHandlers.isJsonServiceInitialized) { + return JenaBodyHandlers.jsonService; + } + // It is acceptable for a JenaBodyHandlers instance to be in a classpath without any implementation for + // JsonService, in which case the ProblemDetails exceptions will fallback to default and not be parsed. + JsonService js; + try { + js = ServiceProvider.getJsonService(); + } catch (IllegalStateException e) { + js = null; + } + JenaBodyHandlers.jsonService = js; + JenaBodyHandlers.isJsonServiceInitialized = true; + return JenaBodyHandlers.jsonService; + } + + 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(); @@ -65,12 +83,39 @@ public static Response.BodyHandler 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 ofJenaModel instead for consistent HTTP error handling. */ - public static Response.BodyHandler ofGraph() { - return responseInfo -> responseInfo.headers().firstValue(CONTENT_TYPE) + public static Response.BodyHandler ofModel() { + return JenaBodyHandlers::responseToModel; + } + + /** + * Populate a Jena {@link Model} with an HTTP response body. + * + * @return an HTTP body handler + */ + public static Response.BodyHandler ofJenaModel() { + return responseInfo -> { + if (responseInfo.statusCode() >= 300) { + throw new ClientHttpException( + ProblemDetails.fromErrorResponse( + responseInfo.statusCode(), + responseInfo.headers(), + responseInfo.body().array(), + getJsonService() + ), + "Deserializing the RDF from " + responseInfo.uri() + " failed" + ); + } + return 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(); @@ -84,24 +129,83 @@ public static Response.BodyHandler ofGraph() { .orElseGet(GraphMemFactory::createDefaultGraph); } + /** + * Populate a Jena {@link Graph} with an HTTP response. + * + * @return an HTTP body handler + * @deprecated Use ofJenaGraph instead for consistent HTTP error handling. + */ + public static Response.BodyHandler ofGraph() { + return JenaBodyHandlers::responseToGraph; + } + + /** + * Populate a Jena {@link Graph} with an HTTP response. + * + * @return an HTTP body handler + */ + public static Response.BodyHandler ofJenaGraph() { + return responseInfo -> { + if (responseInfo.statusCode() > 300) { + throw new ClientHttpException( + ProblemDetails.fromErrorResponse( + responseInfo.statusCode(), + responseInfo.headers(), + responseInfo.body().array(), + getJsonService() + ), + "Deserializing the RDF from " + responseInfo.uri() + " failed" + ); + } + 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 ofJenaDataset instead for consistent HTTP error handling. */ public static Response.BodyHandler 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 ofJenaDataset() { + return responseInfo -> { + if (responseInfo.statusCode() > 300) { + throw new ClientHttpException( + ProblemDetails.fromErrorResponse( + responseInfo.statusCode(), + responseInfo.headers(), + responseInfo.body().array(), + getJsonService() + ), + "Deserializing the RDF from " + responseInfo.uri() + " failed" + ); + } + return JenaBodyHandlers.responseToDataset(responseInfo); + }; } static Lang toJenaLang(final String mediaType) { diff --git a/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java b/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java index 8c8a9e030bc..712541142b2 100644 --- a/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java +++ b/jena/src/test/java/com/inrupt/client/jena/JenaBodyHandlersTest.java @@ -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; @@ -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; @@ -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()); @@ -78,7 +80,7 @@ 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")) @@ -86,7 +88,7 @@ void testOfModelHandlerAsync() throws IOException, .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); @@ -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()); @@ -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()); @@ -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()); @@ -163,7 +187,29 @@ 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")) @@ -171,7 +217,7 @@ void testOfGraphHandlerAsync() throws IOException, .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); @@ -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()); @@ -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()); @@ -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()); + } } diff --git a/test/src/main/java/com/inrupt/client/test/RdfMockService.java b/test/src/main/java/com/inrupt/client/test/RdfMockService.java index 768a2ba1aec..7814e34ca73 100644 --- a/test/src/main/java/com/inrupt/client/test/RdfMockService.java +++ b/test/src/main/java/com/inrupt/client/test/RdfMockService.java @@ -24,6 +24,7 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.core.WireMockConfiguration; +import com.inrupt.client.ProblemDetails; import java.util.Collections; import java.util.Map; @@ -33,6 +34,8 @@ */ public class RdfMockService { + private static final String CONTENT_TYPE = "Content-Type"; + private final WireMockServer wireMockServer; public RdfMockService() { @@ -49,30 +52,41 @@ private void setupMocks() { wireMockServer.stubFor(get(urlEqualTo("/oneTriple")) .willReturn(aResponse() .withStatus(200) - .withHeader("Content-Type", "text/turtle") + .withHeader(CONTENT_TYPE, "text/turtle") .withBody(" ."))); wireMockServer.stubFor(post(urlEqualTo("/postOneTriple")) .withRequestBody(matching( ".*\\s+" + "\\s+\"object\"\\s+\\..*")) - .withHeader("Content-Type", containing("text/turtle")) + .withHeader(CONTENT_TYPE, containing("text/turtle")) .willReturn(aResponse() .withStatus(204))); wireMockServer.stubFor(get(urlEqualTo("/example")) .willReturn(aResponse() .withStatus(200) - .withHeader("Content-Type", "text/turtle") + .withHeader(CONTENT_TYPE, "text/turtle") .withBody(getExampleTTL()))); wireMockServer.stubFor(patch(urlEqualTo("/sparqlUpdate")) - .withHeader("Content-Type", containing("application/sparql-update")) + .withHeader(CONTENT_TYPE, containing("application/sparql-update")) .withRequestBody(matching( "INSERT DATA\\s+\\{\\s*\\s+" + "\\s+\\s*\\.\\s*\\}\\s*")) .willReturn(aResponse() .withStatus(204))); + + wireMockServer.stubFor(get(urlEqualTo("/error")) + .willReturn(aResponse() + .withStatus(429) + .withHeader(CONTENT_TYPE, ProblemDetails.MIME_TYPE) + .withBody("{" + + "\"title\":\"Too Many Requests\"," + + "\"status\":429," + + "\"details\":\"Some details\"," + + "\"instance\":\"https://example.org/instance\"," + + "\"type\":\"https://example.org/type\"}"))); } private String getExampleTTL() { From b583e40b2f471065365f2ac28c09b3e5f8cda127 Mon Sep 17 00:00:00 2001 From: Nicolas Ayral Seydoux Date: Wed, 17 Apr 2024 09:58:07 +0200 Subject: [PATCH 2/5] Use throwing body handler --- .../inrupt/client/jena/JenaBodyHandlers.java | 79 ++++--------------- 1 file changed, 15 insertions(+), 64 deletions(-) diff --git a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java index 88378f55f04..687b128207b 100644 --- a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java +++ b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java @@ -20,11 +20,7 @@ */ package com.inrupt.client.jena; -import com.inrupt.client.ClientHttpException; -import com.inrupt.client.ProblemDetails; import com.inrupt.client.Response; -import com.inrupt.client.spi.JsonService; -import com.inrupt.client.spi.ServiceProvider; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -47,24 +43,9 @@ public final class JenaBodyHandlers { private static final String CONTENT_TYPE = "Content-Type"; - private static JsonService jsonService; - private static boolean isJsonServiceInitialized = false; - - private static JsonService getJsonService() { - if (JenaBodyHandlers.isJsonServiceInitialized) { - return JenaBodyHandlers.jsonService; - } - // It is acceptable for a JenaBodyHandlers instance to be in a classpath without any implementation for - // JsonService, in which case the ProblemDetails exceptions will fallback to default and not be parsed. - JsonService js; - try { - js = ServiceProvider.getJsonService(); - } catch (IllegalStateException e) { - js = null; - } - JenaBodyHandlers.jsonService = js; - JenaBodyHandlers.isJsonServiceInitialized = true; - return JenaBodyHandlers.jsonService; + + private static Boolean isSuccess(final Response.ResponseInfo responseInfo) { + return responseInfo.statusCode() < 300; } private static Model responseToModel(final Response.ResponseInfo responseInfo) { @@ -98,20 +79,10 @@ public static Response.BodyHandler ofModel() { * @return an HTTP body handler */ public static Response.BodyHandler ofJenaModel() { - return responseInfo -> { - if (responseInfo.statusCode() >= 300) { - throw new ClientHttpException( - ProblemDetails.fromErrorResponse( - responseInfo.statusCode(), - responseInfo.headers(), - responseInfo.body().array(), - getJsonService() - ), - "Deserializing the RDF from " + responseInfo.uri() + " failed" - ); - } - return responseToModel(responseInfo); - }; + return Response.BodyHandlers.throwOnError( + JenaBodyHandlers::responseToModel, + JenaBodyHandlers::isSuccess + ); } private static Graph responseToGraph(final Response.ResponseInfo responseInfo) { @@ -145,20 +116,10 @@ public static Response.BodyHandler ofGraph() { * @return an HTTP body handler */ public static Response.BodyHandler ofJenaGraph() { - return responseInfo -> { - if (responseInfo.statusCode() > 300) { - throw new ClientHttpException( - ProblemDetails.fromErrorResponse( - responseInfo.statusCode(), - responseInfo.headers(), - responseInfo.body().array(), - getJsonService() - ), - "Deserializing the RDF from " + responseInfo.uri() + " failed" - ); - } - return JenaBodyHandlers.responseToGraph(responseInfo); - }; + return Response.BodyHandlers.throwOnError( + JenaBodyHandlers::responseToGraph, + JenaBodyHandlers::isSuccess + ); } private static Dataset responseToDataset(final Response.ResponseInfo responseInfo) { @@ -192,20 +153,10 @@ public static Response.BodyHandler ofDataset() { * @return an HTTP body handler */ public static Response.BodyHandler ofJenaDataset() { - return responseInfo -> { - if (responseInfo.statusCode() > 300) { - throw new ClientHttpException( - ProblemDetails.fromErrorResponse( - responseInfo.statusCode(), - responseInfo.headers(), - responseInfo.body().array(), - getJsonService() - ), - "Deserializing the RDF from " + responseInfo.uri() + " failed" - ); - } - return JenaBodyHandlers.responseToDataset(responseInfo); - }; + return Response.BodyHandlers.throwOnError( + JenaBodyHandlers::responseToDataset, + JenaBodyHandlers::isSuccess + ); } static Lang toJenaLang(final String mediaType) { From ffd5d830054cd2225e71e53b512c45fcca3e16e0 Mon Sep 17 00:00:00 2001 From: Nicolas Ayral Seydoux Date: Thu, 18 Apr 2024 14:35:33 +0200 Subject: [PATCH 3/5] Use api isSuccess --- .../java/com/inrupt/client/jena/JenaBodyHandlers.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java index 687b128207b..231f5800c41 100644 --- a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java +++ b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java @@ -44,10 +44,6 @@ public final class JenaBodyHandlers { private static final String CONTENT_TYPE = "Content-Type"; - private static Boolean isSuccess(final Response.ResponseInfo responseInfo) { - return responseInfo.statusCode() < 300; - } - private static Model responseToModel(final Response.ResponseInfo responseInfo) { return responseInfo.headers().firstValue(CONTENT_TYPE) .map(JenaBodyHandlers::toJenaLang).map(lang -> { @@ -81,7 +77,7 @@ public static Response.BodyHandler ofModel() { public static Response.BodyHandler ofJenaModel() { return Response.BodyHandlers.throwOnError( JenaBodyHandlers::responseToModel, - JenaBodyHandlers::isSuccess + (r) -> Response.isSuccess(r.statusCode()) ); } @@ -118,7 +114,7 @@ public static Response.BodyHandler ofGraph() { public static Response.BodyHandler ofJenaGraph() { return Response.BodyHandlers.throwOnError( JenaBodyHandlers::responseToGraph, - JenaBodyHandlers::isSuccess + (r) -> Response.isSuccess(r.statusCode()) ); } @@ -155,7 +151,7 @@ public static Response.BodyHandler ofDataset() { public static Response.BodyHandler ofJenaDataset() { return Response.BodyHandlers.throwOnError( JenaBodyHandlers::responseToDataset, - JenaBodyHandlers::isSuccess + (r) -> Response.isSuccess(r.statusCode()) ); } From 362276c4de98c1453be1f2501f4132d6960358b3 Mon Sep 17 00:00:00 2001 From: Nicolas Ayral Seydoux Date: Thu, 18 Apr 2024 14:38:38 +0200 Subject: [PATCH 4/5] Improve Javadoc --- .../main/java/com/inrupt/client/jena/JenaBodyHandlers.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java index 231f5800c41..8ca0dcece5c 100644 --- a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java +++ b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java @@ -63,7 +63,7 @@ private static Model responseToModel(final Response.ResponseInfo responseInfo) { * Populate a Jena {@link Model} with an HTTP response body. * * @return an HTTP body handler - * @deprecated Use ofJenaModel instead for consistent HTTP error handling. + * @deprecated Use {@link JenaBodyHandlers#ofJenaModel()} instead for consistent HTTP error handling. */ public static Response.BodyHandler ofModel() { return JenaBodyHandlers::responseToModel; @@ -100,7 +100,7 @@ private static Graph responseToGraph(final Response.ResponseInfo responseInfo) { * Populate a Jena {@link Graph} with an HTTP response. * * @return an HTTP body handler - * @deprecated Use ofJenaGraph instead for consistent HTTP error handling. + * @deprecated Use {@link JenaBodyHandlers#ofJenaGraph} instead for consistent HTTP error handling. */ public static Response.BodyHandler ofGraph() { return JenaBodyHandlers::responseToGraph; @@ -137,7 +137,7 @@ private static Dataset responseToDataset(final Response.ResponseInfo responseInf * Populate a Jena {@link Dataset} with an HTTP response. * * @return an HTTP body handler - * @deprecated Use ofJenaDataset instead for consistent HTTP error handling. + * @deprecated Use {@link JenaBodyHandlers#ofJenaDataset} instead for consistent HTTP error handling. */ public static Response.BodyHandler ofDataset() { return JenaBodyHandlers::responseToDataset; From 63be5e6fcd8fb2668d3004001ea486d9d70ba931 Mon Sep 17 00:00:00 2001 From: Nicolas Ayral Seydoux Date: Mon, 22 Apr 2024 17:26:17 +0200 Subject: [PATCH 5/5] Remove usage of throwOnError body handler --- .../inrupt/client/jena/JenaBodyHandlers.java | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java index 8ca0dcece5c..697e0f82f2b 100644 --- a/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java +++ b/jena/src/main/java/com/inrupt/client/jena/JenaBodyHandlers.java @@ -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; @@ -44,6 +46,18 @@ public final class JenaBodyHandlers { private static final String CONTENT_TYPE = "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 -> { @@ -75,10 +89,10 @@ public static Response.BodyHandler ofModel() { * @return an HTTP body handler */ public static Response.BodyHandler ofJenaModel() { - return Response.BodyHandlers.throwOnError( - JenaBodyHandlers::responseToModel, - (r) -> Response.isSuccess(r.statusCode()) - ); + return responseInfo -> { + JenaBodyHandlers.throwOnError(responseInfo); + return JenaBodyHandlers.responseToModel(responseInfo); + }; } private static Graph responseToGraph(final Response.ResponseInfo responseInfo) { @@ -112,10 +126,10 @@ public static Response.BodyHandler ofGraph() { * @return an HTTP body handler */ public static Response.BodyHandler ofJenaGraph() { - return Response.BodyHandlers.throwOnError( - JenaBodyHandlers::responseToGraph, - (r) -> Response.isSuccess(r.statusCode()) - ); + return responseInfo -> { + JenaBodyHandlers.throwOnError(responseInfo); + return JenaBodyHandlers.responseToGraph(responseInfo); + }; } private static Dataset responseToDataset(final Response.ResponseInfo responseInfo) { @@ -149,10 +163,10 @@ public static Response.BodyHandler ofDataset() { * @return an HTTP body handler */ public static Response.BodyHandler ofJenaDataset() { - return Response.BodyHandlers.throwOnError( - JenaBodyHandlers::responseToDataset, - (r) -> Response.isSuccess(r.statusCode()) - ); + return responseInfo -> { + JenaBodyHandlers.throwOnError(responseInfo); + return JenaBodyHandlers.responseToDataset(responseInfo); + }; } static Lang toJenaLang(final String mediaType) {