org.eurekaclinical.common.comm.clients.EurekaClinicalClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eurekaclinical-common Show documentation
Show all versions of eurekaclinical-common Show documentation
A library of classes shared across the Eureka! Clinical platform.
package org.eurekaclinical.common.comm.clients;
/*-
* #%L
* Eureka! Clinical Common
* %%
* Copyright (C) 2016 Emory University
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import com.sun.jersey.api.client.ClientHandlerException;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.GenericType;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.filter.GZIPContentEncodingFilter;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.client.apache4.ApacheHttpClient4;
import com.sun.jersey.client.apache4.config.ApacheHttpClient4Config;
import com.sun.jersey.client.apache4.config.DefaultApacheHttpClient4Config;
import com.sun.jersey.multipart.Boundary;
import com.sun.jersey.multipart.FormDataMultiPart;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.ContextResolver;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.codehaus.jackson.map.ObjectMapper;
/**
* Base class for creating REST API clients. This class is thread-safe.
*
* @author Andrew Post
* @author hrathod
*/
public abstract class EurekaClinicalClient implements AutoCloseable {
private final WebResourceWrapperFactory webResourceWrapperFactory;
private final Class extends ContextResolver extends ObjectMapper>> contextResolverCls;
private final ApacheHttpClient4 client;
private final ClientConnectionManager clientConnManager;
private final Lock readLock;
private final Lock writeLock;
/**
* Constructor for passing in the object mapper instance that is used for
* converting from/to JSON.
*
* @param cls the class of the object mapper.
*/
protected EurekaClinicalClient(Class extends ContextResolver extends ObjectMapper>> cls) {
this.webResourceWrapperFactory = new CasWebResourceWrapperFactory();
this.contextResolverCls = cls;
ApacheHttpClient4Config clientConfig = new DefaultApacheHttpClient4Config();
Map properties = clientConfig.getProperties();
properties.put(ApacheHttpClient4Config.PROPERTY_DISABLE_COOKIES, false);
this.clientConnManager = new ThreadSafeClientConnManager();
properties.put(ApacheHttpClient4Config.PROPERTY_CONNECTION_MANAGER, this.clientConnManager);
clientConfig.getFeatures().put(
JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
if (this.contextResolverCls != null) {
clientConfig.getClasses().add(this.contextResolverCls);
}
this.client = ApacheHttpClient4.create(clientConfig);
this.client.addFilter(new GZIPContentEncodingFilter(false));
ReadWriteLock lock = new ReentrantReadWriteLock();
this.readLock = lock.readLock();
this.writeLock = lock.writeLock();
}
@Override
public void close() {
this.writeLock.lock();
try {
this.client.destroy();
this.clientConnManager.shutdown();
} finally {
this.writeLock.unlock();
}
}
protected abstract URI getResourceUrl();
private WebResourceWrapper getResourceWrapper() {
return this.webResourceWrapperFactory.getInstance(this.client, getResourceUrl());
}
/**
* Deletes the resource specified by the path. Passes no HTTP headers.
*
* @param path the path to the resource. Cannot be null
.
* @throws ClientException if a status code other than 204 (No Content), 202
* (Accepted), and 200 (OK) is returned.
*/
protected void doDelete(String path) throws ClientException {
doDelete(path, null);
}
/**
* Deletes the resource specified by the path.
*
* @param headers any HTTP headers. Can be null
.
* @param path the path to the resource. Cannot be null
.
*
* @throws ClientException if a status code other than 204 (No Content), 202
* (Accepted), and 200 (OK) is returned.
*/
protected void doDelete(String path, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
ClientResponse response = this.getResourceWrapper()
.rewritten(path, HttpMethod.DELETE)
.delete(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT, ClientResponse.Status.ACCEPTED);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Updates the resource specified by the path, for situations where the
* nature of the update is completely specified by the path alone.
*
* @param path the path to the resource. Cannot be null
.
*
* @throws ClientException if a status code other than 204 (No Content) and
* 200 (OK) is returned.
*/
protected void doPut(String path) throws ClientException {
this.readLock.lock();
try {
ClientResponse response = this.getResourceWrapper()
.rewritten(path, HttpMethod.PUT)
.put(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Updates the resource specified by the path. Sends to the server a Content
* Type header for JSON.
*
* @param o the updated object, will be transmitted as JSON. It must be a
* Java bean or an object that the object mapper that is in use knows about.
* @param path the path to the resource. Cannot be null
.
*
* @throws ClientException if a status code other than 204 (No Content) or
* 200 (OK) is returned.
*/
protected void doPut(String path, Object o) throws ClientException {
doPut(path, o, null);
}
/**
* Updates the resource specified by the path.
*
* @param o the updated object, will be transmitted as JSON. It must be a
* Java bean or an object that the object mapper that is in use knows about.
* @param path the path to the resource. Cannot be null
.
* @param headers any headers to pass along. Can be null
. If
* there is no content type header in the provided headers, this method will
* add a Content Type header for JSON.
*
* @throws ClientException if a status code other than 204 (No Content) or
* 200 (OK) is returned.
*/
protected void doPut(String path, Object o, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource rewritten = this.getResourceWrapper()
.rewritten(path, HttpMethod.PUT);
WebResource.Builder requestBuilder = rewritten.getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, true, false);
ClientResponse response = requestBuilder.put(ClientResponse.class, o);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Gets the resource specified by the path. Sends to the server an Accepts
* header for JSON.
*
* @param the type of the resource.
* @param path the path to the resource. Cannot be null
.
* @param cls the type of the resource. Cannot be null
.
*
* @return the resource.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, Class cls) throws ClientException {
return doGet(path, cls, null);
}
/**
* Gets the resource specified by the path.
*
* @param the type of the resource.
* @param path the path to the resource. Cannot be null
.
* @param cls the type of the resource. Cannot be null
.
* @param headers any headers. if no Accepts header is provided, an Accepts
* header for JSON will be added.
*
* @return the resource.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, Class cls, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, false, true);
ClientResponse response = requestBuilder.get(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(cls);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Gets the resource specified by the path and the provided query
* parameters. Sends to the server an Accepts header for JSON.
*
* @param the type of the resource.
* @param path the path to the resource.
* @param queryParams any query parameters. Cannot be null
.
* @param cls the type of the resource. Cannot be null
.
*
* @return the resource.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, MultivaluedMap queryParams, Class cls) throws ClientException {
return doGet(path, queryParams, cls, null);
}
/**
* Gets the resource specified by the path and the provided query
* parameters.
*
* @param the type of the resource.
* @param path the path to the resource.
* @param queryParams any query parameters. Cannot be null
.
* @param cls the type of the resource. Cannot be null
.
* @param headers any headers. If no Accepts header is provided, an Accepts
* header for JSON will be added.
*
* @return the resource.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, MultivaluedMap queryParams, Class cls, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET, queryParams).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, false, true);
ClientResponse response = requestBuilder.get(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(cls);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Makes the GET call and returns the response. The response must be closed
* explicitly unless the getEntity
method is called. Sends to
* the server an Accepts header for JSON.
*
* @param path the path to call. Cannot be null
.
*
* @return the client response.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected ClientResponse doGetResponse(String path) throws ClientException {
return doGetResponse(path, null);
}
/**
* Makes the GET call with the provided headers and returns the response.
* The response must be closed explicitly unless the getEntity
* method is called.
*
* @param path the path to call. Cannot be null
.
* @param headers any headers. If no Accepts header is provided, this method
* will add an Accepts header for JSON.
*
* @return the client response.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected ClientResponse doGetResponse(String path, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, false, true);
ClientResponse response = requestBuilder.get(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response;
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Gets the requested resource. Adds an appropriate Accepts header.
*
* @param the type of the requested resource.
* @param path the path to the resource.
* @param genericType the type of the requested resource.
* @return the requested resource.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, GenericType genericType) throws ClientException {
return doGet(path, genericType, null);
}
/**
* Gets the requested resource
*
* @param the type of the requested resource.
* @param path the path to the resource.
* @param genericType the type of the requested resource.
* @param headers any headers. If no Accepts header is provided, it adds an
* Accepts header for JSON.
* @return the requested resource.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, GenericType genericType, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, false, true);
ClientResponse response = requestBuilder.get(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(genericType);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Gets the requested resource. Adds an appropriate Accepts header.
*
* @param the type of the requested resource.
* @param path the path to the resource.
* @param queryParams any query parameters to send.
* @param genericType the type of the requested resource.
* @return the requested resource.
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, MultivaluedMap queryParams, GenericType genericType) throws ClientException {
return doGet(path, queryParams, genericType, null);
}
/**
* Gets the requested resource
*
* @param the type of the requested resource.
* @param path the path to the resource.
* @param queryParams any query parameters to send.
* @param genericType the type of the requested resource.
* @param headers any headers. If no Accepts header is provided, it adds an
* Accepts header for JSON.
* @return the requested resource.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doGet(String path, MultivaluedMap queryParams, GenericType genericType, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET, queryParams).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, false, true);
ClientResponse response = requestBuilder.get(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(genericType);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Submits a form and gets back a JSON object. Adds appropriate Accepts and
* Content Type headers.
*
* @param the type of object that is expected in the response.
* @param path the API to call.
* @param formParams the form parameters to send.
* @param cls the type of object that is expected in the response.
* @return the object in the response.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doPost(String path, MultivaluedMap formParams, Class cls) throws ClientException {
return doPost(path, formParams, cls, null);
}
/**
* Submits a form and gets back a JSON object.
*
* @param the type of the object that is expected in the response.
* @param path the API to call.
* @param formParams the form parameters to send.
* @param cls the type of object that is expected in the response.
* @param headers any headers. If there is no Accepts header, an Accepts
* header is added for JSON. If there is no Content Type header, a Content
* Type header is added for forms.
* @return the object in the response.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doPost(String path, MultivaluedMap formParams, Class cls, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
ensurePostFormHeaders(headers, requestBuilder, true, true);
ClientResponse response = requestBuilder.post(ClientResponse.class, formParams);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(cls);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Submits a form and gets back a JSON object. Adds appropriate Accepts and
* Content Type headers.
*
* @param the type of object that is expected in the response.
* @param path the API to call.
* @param formParams the form parameters to send.
* @param genericType the type of object that is expected in the response.
* @return the object in the response.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doPost(String path, MultivaluedMap formParams, GenericType genericType) throws ClientException {
return doPost(path, formParams, genericType, null);
}
/**
* Submits a form and gets back a JSON object.
*
* @param the type of the object that is expected in the response.
* @param path the API to call.
* @param formParams the form parameters to send.
* @param genericType the type of object that is expected in the response.
* @param headers any headers. If there is no Accepts header, an Accepts
* header is added for JSON. If there is no Content Type header, a Content
* Type header is added for forms.
* @return the object in the response.
*
* @throws ClientException if a status code other than 200 (OK) is returned.
*/
protected T doPost(String path, MultivaluedMap formParams, GenericType genericType, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
ensurePostFormHeaders(headers, requestBuilder, true, true);
ClientResponse response = requestBuilder.post(ClientResponse.class, formParams);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(genericType);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Makes a POST call to the specified path.
*
* @param path the path to call.
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
protected void doPost(String path) throws ClientException {
this.readLock.lock();
try {
ClientResponse response = getResourceWrapper().rewritten(path, HttpMethod.POST)
.post(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Submits a form. Adds appropriate Accepts and Content Type headers.
*
* @param path the API to call.
* @param formParams the form parameters to send.
*
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
protected void doPostForm(String path, MultivaluedMap formParams) throws ClientException {
doPostForm(path, formParams, null);
}
/**
* Submits a form.
*
* @param path the API to call.
* @param formParams the multi-part form content.
* @param headers any headers to send. If no Content Type header is
* specified, it adds a Content Type for form data.
*
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
protected void doPostForm(String path, MultivaluedMap formParams, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
ensurePostFormHeaders(headers, requestBuilder, true, false);
ClientResponse response = requestBuilder.post(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Makes a POST request with the provided object in the body as JSON. Adds a
* Content Type header for JSON.
*
* @param path the API to call.
* @param o the object to send.
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
protected void doPost(String path, Object o) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
requestBuilder = ensureJsonHeaders(null, requestBuilder, true, false);
ClientResponse response = requestBuilder.post(ClientResponse.class, o);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Makes a POST request with the provided object in the body as JSON.
*
* @param path the API to call.
* @param o the object to send.
* @param headers any headers. If no Content Type header is provided, this
* method adds a Content Type header for JSON.
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
protected void doPost(String path, Object o, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, true, false);
ClientResponse response = requestBuilder.post(ClientResponse.class, o);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Submits a multi-part form. Adds appropriate Accepts and Content Type
* headers.
*
* @param path the API to call.
* @param formDataMultiPart the multi-part form content.
*
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
public void doPostMultipart(String path, FormDataMultiPart formDataMultiPart) throws ClientException {
this.readLock.lock();
try {
ClientResponse response = getResourceWrapper()
.rewritten(path, HttpMethod.POST)
.type(Boundary.addBoundary(MediaType.MULTIPART_FORM_DATA_TYPE))
.post(ClientResponse.class, formDataMultiPart);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Submits a multi-part form.
*
* @param path the API to call.
* @param formDataMultiPart the multi-part form content.
* @param headers any headers to add. If no Content Type header is provided,
* this method adds a Content Type header for multi-part forms data.
*
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
public void doPostMultipart(String path, FormDataMultiPart formDataMultiPart, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper()
.rewritten(path, HttpMethod.POST).getRequestBuilder();
requestBuilder = ensurePostMultipartHeaders(headers, requestBuilder);
ClientResponse response = requestBuilder
.post(ClientResponse.class, formDataMultiPart);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Submits a multi-part form in an input stream. Adds appropriate Accepts
* and Content Type headers.
*
* @param path the the API to call.
* @param inputStream the multi-part form content.
*
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
protected void doPostMultipart(String path, InputStream inputStream) throws ClientException {
doPostMultipart(path, inputStream, null);
}
/**
* Submits a multi-part form in an input stream.
*
* @param path the the API to call.
* @param inputStream the multi-part form content.
* @param headers the headers to send. If no Accepts header is provided,
* this method as an Accepts header for text/plain. If no Content Type
* header is provided, this method adds a Content Type header for multi-part
* forms data.
*
* @throws ClientException if a status code other than 200 (OK) and 204 (No
* Content) is returned.
*/
protected void doPostMultipart(String path, InputStream inputStream, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
requestBuilder = ensurePostMultipartHeaders(headers, requestBuilder);
ClientResponse response = requestBuilder.post(ClientResponse.class, inputStream);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.NO_CONTENT);
response.close();
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Creates a resource specified as a JSON object. Adds appropriate Accepts
* and Content Type headers.
*
* @param path the the API to call.
* @param o the object that will be converted to JSON for sending. Must
* either be a Java bean or be recognized by the object mapper.
* @return the URI representing the created resource, for use in subsequent
* operations on the resource.
* @throws ClientException if a status code other than 201 (Created) is
* returned.
*/
protected URI doPostCreate(String path, Object o) throws ClientException {
return doPostCreate(path, o, null);
}
/**
* Creates a resource specified as a JSON object.
*
* @param path the the API to call.
* @param o the object that will be converted to JSON for sending. Must
* either be a Java bean or be recognized by the object mapper.
* @param headers If no Content Type header is provided, this method adds a
* Content Type header for JSON.
* @return the URI representing the created resource, for use in subsequent
* operations on the resource.
* @throws ClientException if a status code other than 200 (OK) and 201
* (Created) is returned.
*/
protected URI doPostCreate(String path, Object o, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
requestBuilder = ensurePostCreateJsonHeaders(headers, requestBuilder, true, false);
ClientResponse response = requestBuilder.post(ClientResponse.class, o);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.CREATED);
try {
return response.getLocation();
} finally {
response.close();
}
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Creates a resource specified as a multi-part form in an input stream.
* Adds appropriate Accepts and Content Type headers.
*
* @param path the the API to call.
* @param inputStream the multi-part form content.
* @return the URI representing the created resource, for use in subsequent
* operations on the resource.
* @throws ClientException if a status code other than 200 (OK) and 201
* (Created) is returned.
*/
protected URI doPostCreateMultipart(String path, InputStream inputStream) throws ClientException {
return doPostCreateMultipart(path, inputStream, null);
}
/**
* Creates a resource specified as a multi-part form in an input stream.
*
* @param path the the API to call.
* @param inputStream the multi-part form content.
* @param headers any headers to send. If no Accepts header is provided,
* this method as an Accepts header for text/plain. If no Content Type
* header is provided, this method adds a Content Type header for multi-part
* forms data.
* @return the URI representing the created resource, for use in subsequent
* operations on the resource.
* @throws ClientException if a status code other than 200 (OK) and 201
* (Created) is returned.
*/
protected URI doPostCreateMultipart(String path, InputStream inputStream, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST).getRequestBuilder();
requestBuilder = ensurePostCreateMultipartHeaders(headers, requestBuilder);
ClientResponse response = requestBuilder.post(ClientResponse.class, inputStream);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.CREATED);
try {
return response.getLocation();
} finally {
response.close();
}
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Creates a resource specified as a multi-part form. Adds appropriate
* Accepts and Content Type headers.
*
* @param path the the API to call.
* @param formDataMultiPart the form content.
* @return the URI representing the created resource, for use in subsequent
* operations on the resource.
* @throws ClientException if a status code other than 200 (OK) and 201
* (Created) is returned.
*/
protected URI doPostCreateMultipart(String path, FormDataMultiPart formDataMultiPart) throws ClientException {
this.readLock.lock();
try {
ClientResponse response = getResourceWrapper()
.rewritten(path, HttpMethod.POST)
.type(Boundary.addBoundary(MediaType.MULTIPART_FORM_DATA_TYPE))
.accept(MediaType.TEXT_PLAIN)
.post(ClientResponse.class, formDataMultiPart);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK, ClientResponse.Status.CREATED);
try {
return response.getLocation();
} finally {
response.close();
}
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Passes a new resource, form or other POST body to a proxied server.
*
* @param path the path to the resource. Cannot be null
.
* @param inputStream the contents of the POST body. Cannot be
* null
.
* @param parameterMap query parameters. May be null
.
* @param headers any request headers to add. May be null
.
*
* @return ClientResponse the proxied server's response information.
*
* @throws ClientException if the proxied server responds with an "error"
* status code, which is dependent on the server being called.
* @see #getResourceUrl() for the URL of the proxied server.
*/
protected ClientResponse doPostForProxy(String path, InputStream inputStream, MultivaluedMap parameterMap, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST, parameterMap).getRequestBuilder();
copyHeaders(headers, requestBuilder);
return requestBuilder.post(ClientResponse.class, inputStream);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Passes a new resource, form or other POST body to a proxied server.
*
* @param path the path to the resource. Cannot be null
.
* @param body the contents of the POST body. Cannot be
* null
.
* @param parameterMap query parameters. May be null
.
* @param headers any request headers to add. May be null
.
*
* @return ClientResponse the proxied server's response information.
*
* @throws ClientException if the proxied server responds with an "error"
* status code, which is dependent on the server being called.
* @see #getResourceUrl() for the URL of the proxied server.
*/
protected ClientResponse doPostForProxy(String path, String body, MultivaluedMap parameterMap, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.POST, parameterMap).getRequestBuilder();
copyHeaders(headers, requestBuilder);
return requestBuilder.post(ClientResponse.class, body);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Passes a resource update to a proxied server.
*
* @param path the path to the resource. Cannot be null
.
* @param inputStream the contents of the update. Cannot be
* null
.
* @param parameterMap query parameters. May be null
.
* @param headers any request headers to add.
*
* @return ClientResponse the proxied server's response information.
*
* @throws ClientException if the proxied server responds with an "error"
* status code, which is dependent on the server being called.
* @see #getResourceUrl() for the URL of the proxied server.
*/
protected ClientResponse doPutForProxy(String path, InputStream inputStream, MultivaluedMap parameterMap, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.PUT, parameterMap).getRequestBuilder();
copyHeaders(headers, requestBuilder);
return requestBuilder.put(ClientResponse.class, inputStream);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Passes a resource update to a proxied server.
*
* @param path the path to the resource. Cannot be null
.
* @param contents the contents of the update. Cannot be
* null
.
* @param parameterMap query parameters. May be null
.
* @param headers any request headers to add.
*
* @return ClientResponse the proxied server's response information.
*
* @throws ClientException if the proxied server responds with an "error"
* status code, which is dependent on the server being called.
* @see #getResourceUrl() for the URL of the proxied server.
*/
protected ClientResponse doPutForProxy(String path, String contents, MultivaluedMap parameterMap, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.PUT, parameterMap).getRequestBuilder();
copyHeaders(headers, requestBuilder);
return requestBuilder.put(ClientResponse.class, contents);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Gets a resource from a proxied server.
*
* @param path the path to the resource. Cannot be null
.
* @param parameterMap query parameters. May be null
.
* @param headers any request headers to add.
*
* @return ClientResponse the proxied server's response information.
*
* @throws ClientException if the proxied server responds with an "error"
* status code, which is dependent on the server being called.
* @see #getResourceUrl() for the URL of the proxied server.
*/
protected ClientResponse doGetForProxy(String path, MultivaluedMap parameterMap, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET, parameterMap).getRequestBuilder();
copyHeaders(headers, requestBuilder);
return requestBuilder.get(ClientResponse.class);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Deletes a resource from a proxied server.
*
* @param path the path to the resource. Cannot be null
.
* @param parameterMap query parameters. May be null
.
* @param headers any request headers to add.
*
* @return ClientResponse the proxied server's response information.
*
* @throws ClientException if the proxied server responds with an "error"
* status code, which is dependent on the server being called.
* @see #getResourceUrl() for the URL of the proxied server.
*/
protected ClientResponse doDeleteForProxy(String path, MultivaluedMap parameterMap, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.DELETE, parameterMap).getRequestBuilder();
copyHeaders(headers, requestBuilder);
return requestBuilder.delete(ClientResponse.class);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* If there is an unexpected status code, this method gets the status
* message, closes the response, and throws an exception.
*
* @param response the response.
* @param status the expected status code(s).
* @throws ClientException if the response had a status code other than
* those listed.
*/
protected void errorIfStatusEqualTo(ClientResponse response,
ClientResponse.Status... status) throws ClientException {
errorIf(response, status, true);
}
/**
* If there is an unexpected status code, it gets the status message, closes
* the response, and throws an exception.
*
* @param response the response.
* @param status the expected status code(s).
* @throws ClientException if the response had a status code other than
* those listed.
*/
protected void errorIfStatusNotEqualTo(ClientResponse response,
ClientResponse.Status... status) throws ClientException {
errorIf(response, status, false);
}
/**
* Extracts the id of the resource specified in the response body from a
* POST call.
*
* @param uri The URI.
* @return the id of the resource.
*/
protected Long extractId(URI uri) {
String uriStr = uri.toString();
return Long.valueOf(uriStr.substring(uriStr.lastIndexOf("/") + 1));
}
/**
* Gets the specified resource as a string.
*
* @param path the path to the resource.
* @param headers any headers. If no Accepts header is provided, an Accepts
* header is added specifying JSON.
* @return a string containing the requested resource.
*
* @throws ClientException if the response had a status code other than 200
* (OK).
*/
String doGet(String path, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, false, true);
ClientResponse response = requestBuilder.get(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(String.class);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* Gets the specified resource as a string.
*
* @param path the path to the resource.
* @param queryParams any query parameters. Cannot be null
.
* @param headers any headers. If no Accepts header is provided, an Accepts
* header is added specifying JSON.
* @return a string containing the requested resource.
*
* @throws ClientException if the response had a status code other than 200
* (OK).
*/
String doGet(String path, MultivaluedMap queryParams, MultivaluedMap headers) throws ClientException {
this.readLock.lock();
try {
WebResource.Builder requestBuilder = getResourceWrapper().rewritten(path, HttpMethod.GET, queryParams).getRequestBuilder();
requestBuilder = ensureJsonHeaders(headers, requestBuilder, false, true);
ClientResponse response = requestBuilder.get(ClientResponse.class);
errorIfStatusNotEqualTo(response, ClientResponse.Status.OK);
return response.getEntity(String.class);
} catch (ClientHandlerException ex) {
throw new ClientException(ClientResponse.Status.INTERNAL_SERVER_ERROR, ex.getMessage());
} finally {
this.readLock.unlock();
}
}
/**
* If there is an unexpected status code, it gets the status message, closes
* the response, and throws an exception.
*
* @throws ClientException
*/
private void errorIf(ClientResponse response,
ClientResponse.Status[] status, boolean bool)
throws ClientException {
ClientResponse.Status clientResponseStatus
= response.getClientResponseStatus();
if (bool) {
if (contains(status, clientResponseStatus)) {
String message = response.getEntity(String.class);
throw new ClientException(clientResponseStatus, message);
}
} else if (!contains(status, clientResponseStatus)) {
String message = response.getEntity(String.class);
throw new ClientException(clientResponseStatus, message);
}
}
/**
* Tests array membership.
*
* @param arr the array.
* @param member an object.
* @return true
if the provided object is a member of the
* provided array, or false
if not.
*/
private static boolean contains(Object[] arr, Object member) {
for (Object mem : arr) {
if (Objects.equals(mem, member)) {
return true;
}
}
return false;
}
/**
* Adds the specified headers to the request builder. Provides default
* headers for JSON objects requests and submissions.
*
* @param headers the headers to add.
* @param requestBuilder the request builder. Cannot be null
.
* @param contentType true
to add a default Content Type header
* if no Content Type header is provided.
* @param accept true
to add a default Accepts header if no
* Accepts header is provided.
* @return the resulting request builder. Guaranteed not null
.
*/
private static WebResource.Builder ensureJsonHeaders(MultivaluedMap headers, WebResource.Builder requestBuilder, boolean contentType, boolean accept) {
boolean hasContentType = false;
boolean hasAccept = false;
if (headers != null) {
for (Map.Entry> entry : headers.entrySet()) {
String key = entry.getKey();
for (String val : entry.getValue()) {
if (contentType && HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(key)) {
hasContentType = true;
} else if (accept && HttpHeaders.ACCEPT.equalsIgnoreCase(key)) {
hasAccept = true;
}
requestBuilder = requestBuilder.header(key, val);
}
}
}
if (!hasContentType) {
requestBuilder = requestBuilder.type(MediaType.APPLICATION_JSON);
}
if (!hasAccept) {
requestBuilder = requestBuilder.accept(MediaType.APPLICATION_JSON);
}
return requestBuilder;
}
/**
* Adds the specified headers to the request builder. Provides default
* headers for form submissions, optionally with JSON responses.
*
* @param headers the headers to add.
* @param requestBuilder the request builder. Cannot be null
.
* @param contentType true
to add a default Content Type header
* if no Content Type header is provided.
* @param accept true
to add a default Accepts header if no
* Accepts header is provided.
* @return the resulting request builder. Guaranteed not null
.
*/
private static WebResource.Builder ensurePostFormHeaders(MultivaluedMap headers, WebResource.Builder requestBuilder, boolean contentType, boolean accept) {
boolean hasContentType = false;
boolean hasAccept = false;
if (headers != null) {
for (Map.Entry> entry : headers.entrySet()) {
String key = entry.getKey();
for (String val : entry.getValue()) {
if (contentType && HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(key)) {
hasContentType = true;
} else if (accept && HttpHeaders.ACCEPT.equalsIgnoreCase(key)) {
hasAccept = true;
}
requestBuilder = requestBuilder.header(key, val);
}
}
}
if (!hasContentType) {
requestBuilder = requestBuilder.type(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
}
if (!hasAccept) {
requestBuilder = requestBuilder.accept(MediaType.APPLICATION_JSON);
}
return requestBuilder;
}
private static WebResource.Builder copyHeaders(MultivaluedMap headers, WebResource.Builder requestBuilder) {
if (headers != null) {
for (Map.Entry> entry : headers.entrySet()) {
String key = entry.getKey();
for (String val : entry.getValue()) {
requestBuilder = requestBuilder.header(key, val);
}
}
}
return requestBuilder;
}
/**
* Adds the specified headers to the request builder. Provides default
* headers for multi-part submissions.
*
* @param headers the headers to add.
* @param requestBuilder the request builder. Cannot be null
.
* @return the resulting request builder. Guaranteed not null
.
*/
private static WebResource.Builder ensurePostMultipartHeaders(MultivaluedMap headers, WebResource.Builder requestBuilder) {
boolean hasContentType = false;
if (headers != null) {
for (Map.Entry> entry : headers.entrySet()) {
String key = entry.getKey();
for (String val : entry.getValue()) {
if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(key)) {
hasContentType = true;
}
requestBuilder = requestBuilder.header(key, val);
}
}
}
if (!hasContentType) {
requestBuilder = requestBuilder.type(Boundary.addBoundary(MediaType.MULTIPART_FORM_DATA_TYPE));
}
return requestBuilder;
}
/**
* Adds the specified headers to the request builder. Provides default
* headers for JSON request and/or response bodies.
*
* @param headers the headers to add.
* @param requestBuilder the request builder. Cannot be null
.
* @param contentType true
to add a default Content Type header
* if no Content Type header is provided.
* @param accept true
to add a default Accepts header if no
* Accepts header is provided.
* @return the resulting request builder. Guaranteed not null
.
*/
private static WebResource.Builder ensurePostCreateJsonHeaders(MultivaluedMap headers, WebResource.Builder requestBuilder, boolean contentType, boolean accept) {
boolean hasContentType = false;
boolean hasAccept = false;
if (headers != null) {
for (Map.Entry> entry : headers.entrySet()) {
String key = entry.getKey();
for (String val : entry.getValue()) {
if (contentType && HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(key)) {
hasContentType = true;
} else if (accept && HttpHeaders.ACCEPT.equalsIgnoreCase(key)) {
hasAccept = true;
}
requestBuilder = requestBuilder.header(key, val);
}
}
}
if (!hasContentType) {
requestBuilder = requestBuilder.type(MediaType.APPLICATION_JSON);
}
if (!hasAccept) {
requestBuilder = requestBuilder.accept(MediaType.TEXT_PLAIN);
}
return requestBuilder;
}
/**
* Adds the specified headers to the request builder. Provides default
* headers for multi-part submissions.
*
* @param headers the headers to add.
* @param requestBuilder the request builder. Cannot be null
.
* @return the resulting request builder. Guaranteed not null
.
*/
private static WebResource.Builder ensurePostCreateMultipartHeaders(MultivaluedMap headers, WebResource.Builder requestBuilder) {
boolean hasContentType = false;
boolean hasAccept = false;
if (headers != null) {
for (Map.Entry> entry : headers.entrySet()) {
String key = entry.getKey();
for (String val : entry.getValue()) {
if (HttpHeaders.CONTENT_TYPE.equalsIgnoreCase(key)) {
hasContentType = true;
} else if (HttpHeaders.ACCEPT.equalsIgnoreCase(key)) {
hasAccept = true;
}
requestBuilder = requestBuilder.header(key, val);
}
}
}
if (!hasContentType) {
requestBuilder = requestBuilder.type(Boundary.addBoundary(MediaType.MULTIPART_FORM_DATA_TYPE));
}
if (!hasAccept) {
requestBuilder = requestBuilder.accept(MediaType.TEXT_PLAIN);
}
return requestBuilder;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy