edu.byu.hbll.box.client.HttpBoxClient Maven / Gradle / Ivy
The newest version!
package edu.byu.hbll.box.client;
import com.fasterxml.jackson.databind.JsonNode;
import edu.byu.hbll.json.ObjectMapperFactory;
import edu.byu.hbll.json.UncheckedObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.time.Instant;
import java.util.Base64;
import lombok.Builder;
import lombok.Getter;
import lombok.NonNull;
/**
* A client for communicating with Box through it's web api.
*
* @author Charles Draper
*/
public class HttpBoxClient extends AbstractHttpBoxClient {
private static final UncheckedObjectMapper mapper = ObjectMapperFactory.newUnchecked();
private static final int LOWEST_BAD_STATUS_CODE = 400;
/** The base URI of the upstream Box source. Should include path to the desired source. */
@NonNull private final URI uri;
/** The underlying http client to use. */
@Getter private final HttpClient client;
/** Username for basic authentication. */
@Getter private final String username;
/** Password for basic authentication. */
@Getter private final String password;
/** Access token URI for OAuth2. */
@Getter private final URI accessTokenUri;
/** Client ID for OAuth2. */
@Getter private final String clientId;
/** Client secret for OAuth2. */
@Getter private final String clientSecret;
private final HttpClient oauth2Client =
HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
private String accessToken;
private Instant accessTokenExpiration = Instant.MIN;
@Builder(toBuilder = true)
private HttpBoxClient(
URI uri,
HttpClient client,
String username,
String password,
URI accessTokenUri,
String clientId,
String clientSecret) {
super(uri);
this.uri = uri;
this.client =
client != null
? client
: HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build();
this.username = username;
this.password = password;
this.accessTokenUri = accessTokenUri;
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/**
* Creates a new {@link HttpBoxClient} with the given base uri. A new {@link HttpClient} is used
* with default settings plus it always follows redirects.
*
* @param uri the base uri of the box source (eg, http://localhost:8080/app/box)
*/
public HttpBoxClient(URI uri) {
this(uri, HttpClient.newBuilder().followRedirects(HttpClient.Redirect.ALWAYS).build());
}
/**
* Creates a new {@link HttpBoxClient} with the given base uri and client.
*
* @param uri the base uri of the box source (eg, http://localhost:8080/app/box)
* @param client http client to use
*/
public HttpBoxClient(URI uri, HttpClient client) {
this(uri, client, null, null, null, null, null);
}
@Override
protected InputStream send(URI uri) {
HttpResponse response;
try {
response =
client.send(
addAuthHeaders(HttpRequest.newBuilder(uri)).build(), BodyHandlers.ofInputStream());
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
if (response.statusCode() >= LOWEST_BAD_STATUS_CODE) {
throw new UncheckedIOException(
new IOException(
"Error retrieving documents at " + uri + ": HTTP " + response.statusCode()));
}
InputStream input = response.body();
return input;
}
/**
* Adds authentication headers to the request for either basic authentication or oauth2.
*
* @param builder the http request builder to add the headers to
* @return the same http request builder
*/
private HttpRequest.Builder addAuthHeaders(HttpRequest.Builder builder) {
// if basic auth
if (username != null) {
builder.header(
"Authorization",
"Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes()));
} else if (clientId != null) {
// if oauth2
synchronized (this) {
// if token expired
if (Instant.now().isAfter(accessTokenExpiration)) {
// get new token
String tokenRequest =
"client_id="
+ clientId
+ "&client_secret="
+ clientSecret
+ "&grant_type=client_credentials";
HttpRequest oauth2Request =
HttpRequest.newBuilder()
.header("Content-Type", "application/x-www-form-urlencoded")
.uri(accessTokenUri)
.POST(HttpRequest.BodyPublishers.ofString(tokenRequest))
.build();
Instant requestTimestamp = Instant.now();
HttpResponse oauth2Response;
try {
oauth2Response = oauth2Client.send(oauth2Request, HttpResponse.BodyHandlers.ofString());
} catch (IOException e) {
throw new UncheckedIOException(e);
} catch (InterruptedException e) {
throw new RuntimeException("Interruption occurred waiting for access token response");
}
JsonNode token = mapper.readTree(oauth2Response.body());
accessToken = token.path("access_token").asText();
accessTokenExpiration =
requestTimestamp.plusSeconds(token.path("expires_in").asLong()).minusSeconds(1);
}
}
builder.header("Authorization", "Bearer " + accessToken);
}
return builder;
}
/** Builder for {@link HttpBoxClient}. */
public static class HttpBoxClientBuilder {
/**
* Sets the base URI of the upstream Box source. Should include path to the desired source.
*
* @param uri the base URI of the upstream Box source
* @return this
*/
public HttpBoxClientBuilder uri(URI uri) {
this.uri = uri;
return this;
}
/**
* Sets the base URI of the upstream Box source. Should include path to the desired source.
*
* @param uri the base URI of the upstream Box source
* @return this
*/
public HttpBoxClientBuilder uri(String uri) {
return uri(URI.create(uri));
}
/**
* Sets the access token URI for OAuth2.
*
* @param accessTokenUri access token URI for OAuth2
* @return this
*/
public HttpBoxClientBuilder accessTokenUri(URI accessTokenUri) {
this.accessTokenUri = accessTokenUri;
return this;
}
/**
* Sets the access token URI for OAuth2.
*
* @param accessTokenUri access token URI for OAuth2
* @return this
*/
public HttpBoxClientBuilder accessTokenUri(String accessTokenUri) {
if (accessTokenUri == null) {
return accessTokenUri((URI) null);
} else {
return accessTokenUri(URI.create(accessTokenUri));
}
}
/**
* Initialize to use basic authentication.
*
* @param username the username
* @param password the password
* @return this
*/
public HttpBoxClientBuilder basicAuth(String username, String password) {
username(username);
password(password);
return this;
}
/**
* Initialize to use OAuth2.
*
* @param accessTokenUri the access token URI
* @param clientId the client ID
* @param clientSecret the client secret
* @return this
*/
public HttpBoxClientBuilder oauth2(
String accessTokenUri, String clientId, String clientSecret) {
accessTokenUri(accessTokenUri);
clientId(clientId);
clientSecret(clientSecret);
return this;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy