Skip to content

Commit

Permalink
Don't throw in low level client (#1253)
Browse files Browse the repository at this point in the history
* Revert "JCL-402: RDF4J body handlers http error handling (#1162)"

This reverts commit e03eb1e.

* Revert "JCL-402: JSONB test coverage (#1184)"

This reverts commit 52d485b.

* Revert "JCL-402: HTTP error handling in JenaBodyHandlers (#1159)"

This reverts commit 89534b4.

* Move ProblemDetails to solid module, remove ClientHttpException

* ProblemDetails to interface in API module
  • Loading branch information
acoburn authored May 15, 2024
1 parent 2da19cc commit 8be3269
Show file tree
Hide file tree
Showing 17 changed files with 353 additions and 1,131 deletions.
98 changes: 0 additions & 98 deletions api/src/main/java/com/inrupt/client/ClientHttpException.java

This file was deleted.

5 changes: 4 additions & 1 deletion api/src/main/java/com/inrupt/client/Headers.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.inrupt.client.auth.Challenge;
import com.inrupt.client.spi.ServiceProvider;

import java.io.Serializable;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -39,7 +40,9 @@
/**
* A read-only view of a collection of HTTP headers.
*/
public final class Headers {
public final class Headers implements Serializable {

private static final long serialVersionUID = 3845207335727836025L;

private final NavigableMap<String, List<String>> data = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

Expand Down
157 changes: 9 additions & 148 deletions api/src/main/java/com/inrupt/client/ProblemDetails.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,191 +20,52 @@
*/
package com.inrupt.client;

import com.inrupt.client.spi.JsonService;
import com.inrupt.client.spi.ServiceProvider;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.util.Optional;

/**
* A data class representing a structured problem description sent by the server on error response.
*
* @see <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC 9457 Problem Details for HTTP APIs</a>
*/
public class ProblemDetails implements Serializable {

private static final long serialVersionUID = -4597170432270957765L;
public interface ProblemDetails {

/**
* The <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a> default MIME type.
*/
public static final String MIME_TYPE = "application/problem+json";
/**
* The <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a> default problem type.
*/
public static final String DEFAULT_TYPE = "about:blank";
private final URI type;
private final String title;
private final String details;
private final int status;
private final URI instance;
private static JsonService jsonService;
private static boolean isJsonServiceInitialized;
String MIME_TYPE = "application/problem+json";

/**
* Build a ProblemDetails instance providing the expected fields as described in
* <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a>.
* @param type the problem type
* @param title the problem title
* @param details the problem details
* @param status the error response status code
* @param instance the problem instance
* The <a href="https://www.rfc-editor.org/rfc/rfc9457">RFC9457</a> default problem type.
*/
public ProblemDetails(
final URI type,
final String title,
final String details,
final int status,
final URI instance
) {
this.type = type;
this.title = title;
this.details = details;
this.status = status;
this.instance = instance;
}
String DEFAULT_TYPE = "about:blank";

/**
* The problem type.
* @return the type
*/
public URI getType() {
return this.type;
}
URI getType();

/**
* The problem title.
* @return the title
*/
public String getTitle() {
return this.title;
}
String getTitle();

/**
* The problem details.
* @return the details
*/
public String getDetails() {
return this.details;
}
String getDetails();

/**
* The problem status code.
* @return the status code
*/
public int getStatus() {
return this.status;
}
int getStatus();

/**
* The problem instance.
* @return the instance
*/
public URI getInstance() {
return this.instance;
}

/**
* This inner class is only ever used for JSON deserialization. Please do not use in any other context.
*/
public static class Data {
/**
* The problem type.
*/
public URI type;
/**
* The problem title.
*/
public String title;
/**
* The problem details.
*/
public String details;
/**
* The problem status code.
*/
public int status;
/**
* The problem instance.
*/
public URI instance;
}

private static JsonService getJsonService() {
if (ProblemDetails.isJsonServiceInitialized) {
return ProblemDetails.jsonService;
}
// It is a legitimate use case that this is loaded in a context where no implementation of the JSON service is
// available. On SPI lookup failure, the ProblemDetails exceptions will fall back to default and not be parsed.
try {
ProblemDetails.jsonService = ServiceProvider.getJsonService();
} catch (IllegalStateException e) {
ProblemDetails.jsonService = null;
}
ProblemDetails.isJsonServiceInitialized = true;
return ProblemDetails.jsonService;
}

/**
* Builds a {@link ProblemDetails} instance from an HTTP error response.
* @param statusCode the HTTP error response status code
* @param headers the HTTP error response headers
* @param body the HTTP error response body
* @return a {@link ProblemDetails} instance
*/
public static ProblemDetails fromErrorResponse(
final int statusCode,
final Headers headers,
final byte[] body
) {
final JsonService jsonService = getJsonService();
if (jsonService == null
|| (headers != null && !headers.allValues("Content-Type").contains(ProblemDetails.MIME_TYPE))) {
return new ProblemDetails(
URI.create(ProblemDetails.DEFAULT_TYPE),
null,
null,
statusCode,
null
);
}
try {
final Data pdData = jsonService.fromJson(
new ByteArrayInputStream(body),
Data.class
);
final URI type = Optional.ofNullable(pdData.type)
.orElse(URI.create(ProblemDetails.DEFAULT_TYPE));
// JSON mappers map invalid integers to 0, which is an invalid value in our case anyway.
final int status = Optional.of(pdData.status).filter(s -> s != 0).orElse(statusCode);
return new ProblemDetails(
type,
pdData.title,
pdData.details,
status,
pdData.instance
);
} catch (IOException e) {
return new ProblemDetails(
URI.create(ProblemDetails.DEFAULT_TYPE),
null,
null,
statusCode,
null
);
}
}
URI getInstance();
}
9 changes: 0 additions & 9 deletions api/src/main/java/com/inrupt/client/Response.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,6 @@ interface ResponseInfo {
ByteBuffer body();
}

/**
* Indicates whether a status code reflects a successful HTTP response.
* @param statusCode the HTTP response status code
* @return true if the status code is in the success range, namely [200, 299].
*/
static boolean isSuccess(final int statusCode) {
return statusCode >= 200 && statusCode < 300;
}

/**
* An interface for mapping an HTTP response into a specific Java type.
* @param <T> the body type
Expand Down
Loading

0 comments on commit 8be3269

Please sign in to comment.