Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
*/
package com.inrupt.client.rdf4j;

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

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

import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.impl.DynamicModelFactory;
Expand All @@ -41,45 +43,91 @@
*/
public final class RDF4JBodyHandlers {

private static void throwOnError(final Response.ResponseInfo responseInfo) {
if (!Response.isSuccess(responseInfo.statusCode())) {
throw new ClientHttpException(
"Could not map to an RDF4J entity.",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The real issue is that the request failed rather been an issue mapping it to a RDF4J entity. Is this message a bit misleading?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point. Is 475adc0 better?

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(RDF4JBodyHandlers::toRDF4JFormat).map(format -> {
try (final InputStream stream = new ByteArrayInputStream(responseInfo.body().array())) {
return Rio.parse(stream, responseInfo.uri().toString(), format);
} catch (final IOException ex) {
throw new UncheckedIOException(
"An I/O error occurred while data was read from the InputStream", ex);
}
})
.orElseGet(() -> new DynamicModelFactory().createEmptyModel());
}

/**
* Populate a RDF4J {@link Model} with an HTTP response.
*
* @return an HTTP body handler
* @deprecated Use {@link RDF4JBodyHandlers#ofRDF4JModel} instead for consistent HTTP error handling.
*/
public static Response.BodyHandler<Model> ofModel() {
return responseInfo -> responseInfo.headers().firstValue("Content-Type")
.map(RDF4JBodyHandlers::toRDF4JFormat).map(format -> {
try (final InputStream stream = new ByteArrayInputStream(responseInfo.body().array())) {
return Rio.parse(stream, responseInfo.uri().toString(), format);
} catch (final IOException ex) {
throw new UncheckedIOException(
"An I/O error occurred while data was read from the InputStream", ex);
}
})
.orElseGet(() -> new DynamicModelFactory().createEmptyModel());
return RDF4JBodyHandlers::responseToModel;
}

/**
* Populate a RDF4J {@link Repository} with an HTTP response.
* Populate a RDF4J {@link Model} with an HTTP response.
*
* @return an HTTP body handler
*/
public static Response.BodyHandler<Repository> ofRepository() {
return responseInfo -> responseInfo.headers().firstValue("Content-Type")
public static Response.BodyHandler<Model> ofRDF4JModel() {
return responseInfo -> {
RDF4JBodyHandlers.throwOnError(responseInfo);
return RDF4JBodyHandlers.responseToModel(responseInfo);
};
}

private static Repository responseToRepository(final Response.ResponseInfo responseInfo) {
return responseInfo.headers().firstValue("Content-Type")
.map(RDF4JBodyHandlers::toRDF4JFormat).map(format -> {
final Repository repository = new SailRepository(new MemoryStore());
try (final InputStream stream = new ByteArrayInputStream(responseInfo.body().array());
final RepositoryConnection conn = repository.getConnection()) {
final RepositoryConnection conn = repository.getConnection()) {
conn.add(stream, responseInfo.uri().toString(), format);
} catch (final IOException ex) {
throw new UncheckedIOException(
"An I/O error occurred while data was read from the InputStream", ex);
"An I/O error occurred while data was read from the InputStream", ex);
}
return repository;
})
.orElseGet(() -> new SailRepository(new MemoryStore()));
}

/**
* Populate a RDF4J {@link Repository} with an HTTP response.
*
* @return an HTTP body handler
* @deprecated Use {@link RDF4JBodyHandlers#ofRDF4JRepository} instead for consistent HTTP error handling.
*/
public static Response.BodyHandler<Repository> ofRepository() {
return RDF4JBodyHandlers::responseToRepository;
}

/**
* Populate a RDF4J {@link Repository} with an HTTP response.
*
* @return an HTTP body handler
*/
public static Response.BodyHandler<Repository> ofRDF4JRepository() {
return responseInfo -> {
RDF4JBodyHandlers.throwOnError(responseInfo);
return RDF4JBodyHandlers.responseToRepository(responseInfo);
};
}

static RDFFormat toRDF4JFormat(final String mediaType) {
return Rio.getParserFormatForMIMEType(mediaType).orElse(RDFFormat.TURTLE);
}
Expand Down
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 java.util.concurrent.TimeoutException;

Expand Down Expand Up @@ -61,14 +63,14 @@ static void teardown() {
}

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

final Response<Model> res = client.send(request, RDF4JBodyHandlers.ofModel()).toCompletableFuture().join();
final Response<Model> res = client.send(request, RDF4JBodyHandlers.ofRDF4JModel()).toCompletableFuture().join();
Comment thread
NSeydoux marked this conversation as resolved.

final int statusCode = res.statusCode();
final Model responseBody = res.body();
Expand All @@ -84,14 +86,14 @@ void testOfModelHandlerAsync() throws IOException,
}

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

final Response<Model> response = client.send(request, RDF4JBodyHandlers.ofModel())
final Response<Model> response = client.send(request, RDF4JBodyHandlers.ofRDF4JModel())
.toCompletableFuture().join();

assertEquals(200, response.statusCode());
Expand All @@ -106,13 +108,13 @@ void testOfModelHandler() throws IOException,
}

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

final Response<Model> response = client.send(request, RDF4JBodyHandlers.ofModel())
final Response<Model> response = client.send(request, RDF4JBodyHandlers.ofRDF4JModel())
.toCompletableFuture().join();

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

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

final CompletionException completionException = assertThrows(
CompletionException.class,
() -> client.send(request, RDF4JBodyHandlers.ofRDF4JModel()).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 testOfRDF4JRepositoryHandlerAsync() throws IOException,
InterruptedException, ExecutionException, TimeoutException {
final Request request = Request.newBuilder()
.uri(URI.create(config.get("rdf_uri") + "/oneTriple"))
.GET()
.build();

final Response<Repository> res = client.send(request, RDF4JBodyHandlers.ofRepository())
final Response<Repository> res = client.send(request, RDF4JBodyHandlers.ofRDF4JRepository())
.toCompletableFuture().join();

final int statusCode = res.statusCode();
Expand All @@ -155,14 +179,14 @@ void testOfRepositoryHandlerAsync() throws IOException,
}

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

final Response<Repository> response = client.send(request, RDF4JBodyHandlers.ofRepository())
final Response<Repository> response = client.send(request, RDF4JBodyHandlers.ofRDF4JRepository())
.toCompletableFuture().join();
assertEquals(200, response.statusCode());

Expand All @@ -180,13 +204,13 @@ void testOfRepositoryHandler() throws IOException,
}

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

final Response<Repository> response = client.send(request, RDF4JBodyHandlers.ofRepository())
final Response<Repository> response = client.send(request, RDF4JBodyHandlers.ofRDF4JRepository())
.toCompletableFuture().join();
assertEquals(200, response.statusCode());

Expand All @@ -202,4 +226,26 @@ void testOfRepositoryHandlerWithURL() throws IOException, InterruptedException {
);
}
}

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

final CompletionException completionException = assertThrows(
CompletionException.class,
() -> client.send(request, RDF4JBodyHandlers.ofRDF4JRepository()).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());
}
}