Merge branch 'jaxrs-client'

This commit is contained in:
jamesagnew 2016-02-27 09:48:51 -05:00
commit a818ba199c
66 changed files with 2195 additions and 648 deletions

View File

@ -3,11 +3,11 @@ package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor;
import ca.uhn.fhir.rest.client.interceptor.GZipContentInterceptor;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.EncodingEnum;

View File

@ -47,7 +47,7 @@ import ca.uhn.fhir.parser.LenientErrorHandler;
import ca.uhn.fhir.parser.XmlParser;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.IRestfulClientFactory;
import ca.uhn.fhir.rest.client.RestfulClientFactory;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
@ -298,9 +298,22 @@ public class FhirContext {
return myIdToResourceDefinition.values();
}
/**
* Set the restful client factory
* @param theRestfulClientFactory
*/
public void setRestfulClientFactory(IRestfulClientFactory theRestfulClientFactory) {
this.myRestfulClientFactory = theRestfulClientFactory;
}
/**
* Get the restful client factory. If no factory has been set, this will be initialized with
* a new ApacheRestfulClientFactory.
* @return the factory used to create the restful clients
*/
public IRestfulClientFactory getRestfulClientFactory() {
if (myRestfulClientFactory == null) {
myRestfulClientFactory = new RestfulClientFactory(this);
myRestfulClientFactory = new ApacheRestfulClientFactory(this);
}
return myRestfulClientFactory;
}

View File

@ -25,10 +25,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -40,14 +38,6 @@ import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -62,6 +52,9 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
@ -79,19 +72,19 @@ public abstract class BaseClient implements IRestfulClient {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseClient.class);
private final HttpClient myClient;
private final IHttpClient myClient;
private boolean myDontValidateConformance;
private EncodingEnum myEncoding = null; // default unspecified (will be XML)
private final RestfulClientFactory myFactory;
private List<IClientInterceptor> myInterceptors = new ArrayList<IClientInterceptor>();
private boolean myKeepResponses = false;
private HttpResponse myLastResponse;
private IHttpResponse myLastResponse;
private String myLastResponseBody;
private Boolean myPrettyPrint = false;
private SummaryEnum mySummary;
private final String myUrlBase;
BaseClient(HttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) {
BaseClient(IHttpClient theClient, String theUrlBase, RestfulClientFactory theFactory) {
super();
myClient = theClient;
myUrlBase = theUrlBase;
@ -130,7 +123,7 @@ public abstract class BaseClient implements IRestfulClient {
* {@inheritDoc}
*/
@Override
public HttpClient getHttpClient() {
public IHttpClient getHttpClient() {
return myClient;
}
@ -141,7 +134,7 @@ public abstract class BaseClient implements IRestfulClient {
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public HttpResponse getLastResponse() {
public IHttpResponse getLastResponse() {
return myLastResponse;
}
@ -193,46 +186,43 @@ public abstract class BaseClient implements IRestfulClient {
// TODO: handle non 2xx status codes by throwing the correct exception,
// and ensure it's passed upwards
HttpRequestBase httpRequest;
HttpResponse response;
IHttpRequest httpRequest;
IHttpResponse response = null;
try {
Map<String, List<String>> params = createExtraParams();
Map<String, List<String>> params = createExtraParams();
if (theEncoding == EncodingEnum.XML) {
params.put(Constants.PARAM_FORMAT, Collections.singletonList("xml"));
} else if (theEncoding == EncodingEnum.JSON) {
params.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
}
if (theEncoding == EncodingEnum.XML) {
params.put(Constants.PARAM_FORMAT, Collections.singletonList("xml"));
} else if (theEncoding == EncodingEnum.JSON) {
params.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
}
if (theSummaryMode != null) {
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(theSummaryMode.getCode()));
} else if (mySummary != null) {
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(mySummary.getCode()));
}
if (theSummaryMode != null) {
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(theSummaryMode.getCode()));
} else if (mySummary != null) {
params.put(Constants.PARAM_SUMMARY, Collections.singletonList(mySummary.getCode()));
}
if (thePrettyPrint == Boolean.TRUE) {
params.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
}
if (thePrettyPrint == Boolean.TRUE) {
params.put(Constants.PARAM_PRETTY, Collections.singletonList(Constants.PARAM_PRETTY_VALUE_TRUE));
}
if (theSubsetElements != null && theSubsetElements.isEmpty() == false) {
params.put(Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ',')));
}
if (theSubsetElements != null && theSubsetElements.isEmpty() == false) {
params.put(Constants.PARAM_ELEMENTS, Collections.singletonList(StringUtils.join(theSubsetElements, ',')));
}
EncodingEnum encoding = getEncoding();
if (theEncoding != null) {
encoding = theEncoding;
}
EncodingEnum encoding = getEncoding();
if (theEncoding != null) {
encoding = theEncoding;
}
httpRequest = clientInvocation.asHttpRequest(myUrlBase, params, encoding, thePrettyPrint);
if (theLogRequestAndResponse) {
ourLog.info("Client invoking: {}", httpRequest);
if (httpRequest instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) httpRequest).getEntity();
if (entity.isRepeatable()) {
String content = IOUtils.toString(entity.getContent());
ourLog.info("Client request body: {}", content);
}
String body = httpRequest.getRequestBodyFromStream();
if(body != null) {
ourLog.info("Client request body: {}", body);
}
}
@ -240,45 +230,26 @@ public abstract class BaseClient implements IRestfulClient {
nextInterceptor.interceptRequest(httpRequest);
}
response = myClient.execute(httpRequest);
response = httpRequest.execute();
for (IClientInterceptor nextInterceptor : myInterceptors) {
nextInterceptor.interceptResponse(response);
}
} catch (DataFormatException e) {
throw new FhirClientConnectionException(e);
} catch (IOException e) {
throw new FhirClientConnectionException(e);
}
try {
String mimeType;
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatusLine().getStatusCode()) {
if (Constants.STATUS_HTTP_204_NO_CONTENT == response.getStatus()) {
mimeType = null;
} else {
ContentType ct = ContentType.get(response.getEntity());
mimeType = ct != null ? ct.getMimeType() : null;
mimeType = response.getMimeType();
}
Map<String, List<String>> headers = new HashMap<String, List<String>>();
if (response.getAllHeaders() != null) {
for (Header next : response.getAllHeaders()) {
String name = next.getName().toLowerCase();
List<String> list = headers.get(name);
if (list == null) {
list = new ArrayList<String>();
headers.put(name, list);
}
list.add(next.getValue());
}
}
Map<String, List<String>> headers = response.getAllHeaders();
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
if (response.getStatus() < 200 || response.getStatus() > 299) {
String body = null;
Reader reader = null;
try {
reader = createReaderFromResponse(response);
reader = response.createReader();
body = IOUtils.toString(reader);
} catch (Exception e) {
ourLog.debug("Failed to read input stream", e);
@ -286,7 +257,7 @@ public abstract class BaseClient implements IRestfulClient {
IOUtils.closeQuietly(reader);
}
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
String message = "HTTP " + response.getStatus() + " " + response.getStatusInfo();
IBaseOperationOutcome oo = null;
if (Constants.CT_TEXT.equals(mimeType)) {
message = message + ": " + body;
@ -309,7 +280,7 @@ public abstract class BaseClient implements IRestfulClient {
keepResponseAndLogIt(theLogRequestAndResponse, response, body);
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatus(), message);
exception.setOperationOutcome(oo);
if (body != null) {
@ -321,7 +292,7 @@ public abstract class BaseClient implements IRestfulClient {
if (binding instanceof IClientResponseHandlerHandlesBinary) {
IClientResponseHandlerHandlesBinary<T> handlesBinary = (IClientResponseHandlerHandlesBinary<T>) binding;
if (handlesBinary.isBinary()) {
InputStream reader = response.getEntity().getContent();
InputStream reader = response.readEntity();
try {
if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
@ -330,7 +301,7 @@ public abstract class BaseClient implements IRestfulClient {
myLastResponse = response;
myLastResponseBody = null;
}
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
String message = "HTTP " + response.getStatus() + " " + response.getStatusInfo();
if (theLogRequestAndResponse) {
ourLog.info("Client response: {} - {} bytes", message, responseBytes.length);
} else {
@ -339,14 +310,14 @@ public abstract class BaseClient implements IRestfulClient {
reader = new ByteArrayInputStream(responseBytes);
}
return handlesBinary.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
return handlesBinary.invokeClient(mimeType, reader, response.getStatus(), headers);
} finally {
IOUtils.closeQuietly(reader);
}
}
}
Reader reader = createReaderFromResponse(response);
Reader reader = response.createReader();
if (ourLog.isTraceEnabled() || myKeepResponses || theLogRequestAndResponse) {
String responseString = IOUtils.toString(reader);
@ -355,22 +326,24 @@ public abstract class BaseClient implements IRestfulClient {
}
try {
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
return binding.invokeClient(mimeType, reader, response.getStatus(), headers);
} finally {
IOUtils.closeQuietly(reader);
}
} catch (DataFormatException e) {
throw new FhirClientConnectionException(e);
} catch (IllegalStateException e) {
throw new FhirClientConnectionException(e);
} catch (IOException e) {
throw new FhirClientConnectionException(e);
} catch(RuntimeException e) {
throw e;
} catch (Exception e) {
throw new FhirClientConnectionException(e);
} finally {
if (response instanceof CloseableHttpResponse) {
try {
((CloseableHttpResponse) response).close();
} catch (IOException e) {
ourLog.debug("Failed to close response", e);
}
if (response != null) {
response.close();
}
}
}
@ -391,13 +364,13 @@ public abstract class BaseClient implements IRestfulClient {
return Boolean.TRUE.equals(myPrettyPrint);
}
private void keepResponseAndLogIt(boolean theLogRequestAndResponse, HttpResponse response, String responseString) {
private void keepResponseAndLogIt(boolean theLogRequestAndResponse, IHttpResponse response, String responseString) {
if (myKeepResponses) {
myLastResponse = response;
myLastResponseBody = responseString;
}
if (theLogRequestAndResponse) {
String message = "HTTP " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase();
String message = "HTTP " + response.getStatus() + " " + response.getStatusInfo();
if (StringUtils.isNotBlank(responseString)) {
ourLog.info("Client response: {}\n{}", message, responseString);
} else {
@ -444,7 +417,7 @@ public abstract class BaseClient implements IRestfulClient {
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setLastResponse(HttpResponse theLastResponse) {
public void setLastResponse(IHttpResponse theLastResponse) {
myLastResponse = theLastResponse;
}
@ -477,30 +450,9 @@ public abstract class BaseClient implements IRestfulClient {
myInterceptors.remove(theInterceptor);
}
public static Reader createReaderFromResponse(HttpResponse theResponse) throws IllegalStateException, IOException {
HttpEntity entity = theResponse.getEntity();
if (entity == null) {
return new StringReader("");
}
Charset charset = null;
if (entity.getContentType() != null && entity.getContentType().getElements() != null && entity.getContentType().getElements().length > 0) {
ContentType ct = ContentType.get(entity);
charset = ct.getCharset();
}
if (charset == null) {
if (Constants.STATUS_HTTP_204_NO_CONTENT != theResponse.getStatusLine().getStatusCode()) {
ourLog.warn("Response did not specify a charset.");
}
charset = Charset.forName("UTF-8");
}
Reader reader = new InputStreamReader(theResponse.getEntity().getContent(), charset);
return reader;
}
@Override
public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl) {
BaseHttpClientInvocation clientInvocation = new HttpGetClientInvocation(theUrl);
BaseHttpClientInvocation clientInvocation = new HttpGetClientInvocation(getFhirContext(), theUrl);
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theResourceType, null, false);
return invokeClient(getFhirContext(), binding, clientInvocation, null, false, false, null, null);
}

View File

@ -27,23 +27,25 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.Header;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.message.BasicHeader;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.VersionUtil;
public abstract class BaseHttpClientInvocation {
private List<Header> myHeaders;
private final List<Header> myHeaders;
private final FhirContext myContext;
public BaseHttpClientInvocation(FhirContext myContext) {
this.myContext = myContext;
this.myHeaders = new ArrayList<Header>();
}
public void addHeader(String theName, String theValue) {
if (myHeaders == null) {
myHeaders = new ArrayList<Header>();
}
myHeaders.add(new BasicHeader(theName, theValue));
myHeaders.add(new Header(theName, theValue));
}
/**
@ -57,15 +59,15 @@ public abstract class BaseHttpClientInvocation {
* The encoding to use for any serialized content sent to the
* server
*/
public abstract HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint);
public abstract IHttpRequest asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint);
protected static void appendExtraParamsWithQuestionMark(Map<String, List<String>> theExtraParams, StringBuilder theUrlBuilder, boolean theWithQuestionMark) {
public static void appendExtraParamsWithQuestionMark(Map<String, List<String>> theExtraParams, StringBuilder theUrlBuilder, boolean theWithQuestionMark) {
if (theExtraParams == null) {
return;
}
boolean first = theWithQuestionMark;
if (theExtraParams != null && theExtraParams.isEmpty() == false) {
if (theExtraParams.isEmpty() == false) {
for (Entry<String, List<String>> next : theExtraParams.entrySet()) {
for (String nextValue : next.getValue()) {
if (first) {
@ -86,25 +88,43 @@ public abstract class BaseHttpClientInvocation {
}
}
public void addHeadersToRequest(HttpRequestBase theHttpRequest, EncodingEnum theEncoding) {
if (myHeaders != null) {
for (Header next : myHeaders) {
theHttpRequest.addHeader(next);
}
}
String versionString = VersionUtil.getVersion();
theHttpRequest.addHeader("User-Agent", "HAPI-FHIR/" + versionString + " (FHIR Client)");
theHttpRequest.addHeader("Accept-Charset", "utf-8");
theHttpRequest.addHeader("Accept-Encoding", "gzip");
if (theEncoding == null) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON);
} else if (theEncoding == EncodingEnum.JSON) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
} else if (theEncoding == EncodingEnum.XML) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
}
/**
* Get the restfull client factory
* @return
*/
public IRestfulClientFactory getRestfulClientFactory() {
return myContext.getRestfulClientFactory();
}
/**
* Create an HTTP request for the given url, encoding and request-type
*
* @param theUrl
* The complete FHIR url to which the http request will be sent
* @param theEncoding
* The encoding to use for any serialized content sent to the
* server
* @param theRequestType
* the type of HTTP request (GET, DELETE, ..)
*/
protected IHttpRequest createHttpRequest(String theUrl, EncodingEnum theEncoding, RequestTypeEnum theRequestType) {
IHttpClient httpClient = getRestfulClientFactory().getHttpClient(new StringBuilder(theUrl), null, null, theRequestType, myHeaders);
return httpClient.createGetRequest(theEncoding);
}
/**
* Returns the http headers to be sent with the request
*/
public List<Header> getHeaders() {
return myHeaders;
}
/**
* Returns the FHIR context associated with this client
* @return the myContext
*/
public FhirContext getFhirContext() {
return myContext;
}
}

View File

@ -24,20 +24,19 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import org.apache.http.client.HttpClient;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.ClientInvocationHandlerFactory.ILambda;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
class ClientInvocationHandler extends BaseClient implements InvocationHandler {
public class ClientInvocationHandler extends BaseClient implements InvocationHandler {
private final Map<Method, BaseMethodBinding<?>> myBindings;
private final Map<Method, Object> myMethodToReturnValue;
private FhirContext myContext;
private Map<Method, ILambda> myMethodToLambda;
public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Map<Method, Object> theMethodToReturnValue, Map<Method, BaseMethodBinding<?>> theBindings, Map<Method, ILambda> theMethodToLambda, RestfulClientFactory theFactory) {
public ClientInvocationHandler(IHttpClient theClient, FhirContext theContext, String theUrlBase, Map<Method, Object> theMethodToReturnValue, Map<Method, BaseMethodBinding<?>> theBindings, Map<Method, ILambda> theMethodToLambda, RestfulClientFactory theFactory) {
super(theClient, theUrlBase, theFactory);
myContext = theContext;

View File

@ -24,26 +24,26 @@ import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.client.HttpClient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.EncodingEnum;
class ClientInvocationHandlerFactory {
public class ClientInvocationHandlerFactory {
private final Map<Method, BaseMethodBinding<?>> myBindings = new HashMap<Method, BaseMethodBinding<?>>();
private final HttpClient myClient;
private final IHttpClient myClient;
private final FhirContext myContext;
private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>();
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
private final String myUrlBase;
public ClientInvocationHandlerFactory(HttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
public ClientInvocationHandlerFactory(IHttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
myClient = theClient;
myUrlBase = theUrlBase;
myContext = theContext;
@ -75,7 +75,7 @@ class ClientInvocationHandlerFactory {
return new ClientInvocationHandler(myClient, myContext, myUrlBase, myMethodToReturnValue, myBindings, myMethodToLambda, theRestfulClientFactory);
}
interface ILambda {
public interface ILambda {
Object handle(ClientInvocationHandler theTarget, Object[] theArgs);
}

View File

@ -38,8 +38,6 @@ import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseConformance;
@ -72,6 +70,8 @@ import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.gclient.ICreate;
@ -157,13 +157,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
private static final String I18N_NO_VERSION_ID_FOR_VREAD = "ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class);
private FhirContext myContext;
private HttpRequestBase myLastRequest;
private IHttpRequest myLastRequest;
private boolean myLogRequestAndResponse;
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public GenericClient(FhirContext theContext, HttpClient theHttpClient, String theServerBase, RestfulClientFactory theFactory) {
public GenericClient(FhirContext theContext, IHttpClient theHttpClient, String theServerBase, RestfulClientFactory theFactory) {
super(theHttpClient, theServerBase, theFactory);
myContext = theContext;
}
@ -174,7 +174,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
throw new IllegalArgumentException("Must call conformance(" + IBaseConformance.class.getSimpleName() + ") instead of conformance() for HL7.org structures");
}
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext());
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
@ -216,7 +216,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public MethodOutcome delete(final Class<? extends IBaseResource> theType, IdDt theId) {
HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(theId.withResourceType(toResourceName(theType)));
HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), theId.withResourceType(toResourceName(theType)));
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
@ -243,15 +243,15 @@ public class GenericClient extends BaseClient implements IGenericClient {
HttpGetClientInvocation invocation;
if (id.hasBaseUrl()) {
if (theVRead) {
invocation = ReadMethodBinding.createAbsoluteVReadInvocation(id);
invocation = ReadMethodBinding.createAbsoluteVReadInvocation(getFhirContext(), id);
} else {
invocation = ReadMethodBinding.createAbsoluteReadInvocation(id);
invocation = ReadMethodBinding.createAbsoluteReadInvocation(getFhirContext(), id);
}
} else {
if (theVRead) {
invocation = ReadMethodBinding.createVReadInvocation(id, resName);
invocation = ReadMethodBinding.createVReadInvocation(getFhirContext(), id, resName);
} else {
invocation = ReadMethodBinding.createReadInvocation(id, resName);
invocation = ReadMethodBinding.createReadInvocation(getFhirContext(), id, resName);
}
}
if (isKeepResponses()) {
@ -305,7 +305,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
return myContext;
}
public HttpRequestBase getLastRequest() {
public IHttpRequest getLastRequest() {
return myLastRequest;
}
@ -330,7 +330,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
public <T extends IBaseResource> Bundle history(final Class<T> theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit) {
String resourceName = theType != null ? toResourceName(theType) : null;
String id = theIdDt != null && theIdDt.isEmpty() == false ? theIdDt.getValue() : null;
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(resourceName, id, theSince, theLimit);
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, theSince, theLimit);
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
}
@ -468,7 +468,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public <T extends IBaseResource> Bundle search(final Class<T> theType, UriDt theUrl) {
BaseHttpClientInvocation invocation = new HttpGetClientInvocation(theUrl.getValueAsString());
BaseHttpClientInvocation invocation = new HttpGetClientInvocation(getFhirContext(), theUrl.getValueAsString());
return invokeClient(myContext, new BundleResponseHandler(theType), invocation);
}
@ -480,7 +480,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
public void setLastRequest(HttpRequestBase theLastRequest) {
public void setLastRequest(IHttpRequest theLastRequest) {
myLastRequest = theLastRequest;
}
@ -839,12 +839,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
public BaseOperationOutcome execute() {
HttpDeleteClientInvocation invocation;
if (myId != null) {
invocation = DeleteMethodBinding.createDeleteInvocation(myId);
invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myId);
} else if (myCriterionList != null) {
Map<String, List<String>> params = myCriterionList.toParamList();
invocation = DeleteMethodBinding.createDeleteInvocation(myResourceType, params);
invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), myResourceType, params);
} else {
invocation = DeleteMethodBinding.createDeleteInvocation(mySearchUrl);
invocation = DeleteMethodBinding.createDeleteInvocation(getFhirContext(), mySearchUrl);
}
OperationOutcomeResponseHandler binding = new OperationOutcomeResponseHandler();
Map<String, List<String>> params = new HashMap<String, List<String>>();
@ -926,7 +926,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
@Override
public Object execute() {
ResourceResponseHandler binding = new ResourceResponseHandler(myType.getImplementingClass(), null);
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation();
HttpGetClientInvocation invocation = MethodUtil.createConformanceInvocation(getFhirContext());
return super.invoke(null, binding, invocation);
}
@ -965,7 +965,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
} else {
binding = new ResourceResponseHandler(myBundleType, null);
}
HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myUrl);
HttpSimpleGetClientInvocation invocation = new HttpSimpleGetClientInvocation(myContext, myUrl);
Map<String, List<String>> params = null;
return invoke(params, binding, invocation);
@ -1002,7 +1002,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
urlFragments.add(Constants.PARAM_TAGS);
HttpGetClientInvocation invocation = new HttpGetClientInvocation(params, urlFragments);
HttpGetClientInvocation invocation = new HttpGetClientInvocation(myContext, params, urlFragments);
return invoke(params, binding, invocation);
@ -1083,7 +1083,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
id = null;
}
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(resourceName, id, mySince, myCount);
HttpGetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(myContext, resourceName, id, mySince, myCount);
IClientResponseHandler handler;
if (myReturnType != null) {
@ -1813,7 +1813,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
BaseHttpClientInvocation invocation;
if (mySearchUrl != null) {
invocation = SearchMethodBinding.createSearchInvocation(mySearchUrl, params);
invocation = SearchMethodBinding.createSearchInvocation(myContext, mySearchUrl, params);
} else {
invocation = SearchMethodBinding.createSearchInvocation(myContext, myResourceName, params, resourceId, myCompartmentName, mySearchStyle);
}

View File

@ -22,19 +22,19 @@ package ca.uhn.fhir.rest.client;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
public interface IClientInterceptor {
/**
* Fired by the client just before invoking the HTTP client request
*/
void interceptRequest(HttpRequestBase theRequest);
void interceptRequest(IHttpRequest theRequest);
/**
* Fired by the client upon receiving an HTTP response, prior to processing that response
*/
void interceptResponse(HttpResponse theResponse) throws IOException;
void interceptResponse(IHttpResponse theResponse) throws IOException;
}

View File

@ -20,9 +20,13 @@ package ca.uhn.fhir.rest.client;
* #L%
*/
import org.apache.http.client.HttpClient;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
public interface IRestfulClientFactory {
@ -76,11 +80,20 @@ public interface IRestfulClientFactory {
int getConnectTimeout();
/**
* Returns the Apache HTTP client instance. This method will not return null.
*
* @see #setHttpClient(HttpClient)
* Returns the HTTP client instance. This method will not return null.
* @param theUrl
* The complete FHIR url to which the http request will be sent
* @param theIfNoneExistParams
* The params for header "If-None-Exist" as a hashmap
* @param theIfNoneExistString
* The param for header "If-None-Exist" as a string
* @param theRequestType
* the type of HTTP request (GET, DELETE, ..)
* @param theHeaders
* the headers to be sent together with the http request
* @return the HTTP client instance
*/
HttpClient getHttpClient();
IHttpClient getHttpClient(StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams, String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders);
/**
* @deprecated Use {@link #getServerValidationMode()} instead
@ -172,7 +185,7 @@ public interface IRestfulClientFactory {
* @param theHttpClient
* An HTTP client instance to use, or <code>null</code>
*/
void setHttpClient(HttpClient theHttpClient);
<T> void setHttpClient(T theHttpClient);
/**
* Sets the HTTP proxy to use for outgoing connections
@ -234,4 +247,12 @@ public interface IRestfulClientFactory {
* </p>
*/
void setPoolMaxPerRoute(int thePoolMaxPerRoute);
void validateServerBase(String theServerBase, IHttpClient theHttpClient, BaseClient theClient);
/**
* This method is internal to HAPI - It may change in future versions, use with caution.
*/
void validateServerBaseIfConfiguredToDoSo(String theServerBase, IHttpClient theHttpClient, BaseClient theClient);
}

View File

@ -28,27 +28,16 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.FhirClientInappropriateForServerException;
@ -56,18 +45,18 @@ import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.FhirTerser;
public class RestfulClientFactory implements IRestfulClientFactory {
public abstract class RestfulClientFactory implements IRestfulClientFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulClientFactory.class);
private int myConnectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
private int myConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
private FhirContext myContext;
private HttpClient myHttpClient;
private Map<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory> myInvocationHandlers = new HashMap<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory>();
private HttpHost myProxy;
private ServerValidationModeEnum myServerValidationMode = DEFAULT_SERVER_VALIDATION_MODE;
private int mySocketTimeout = DEFAULT_SOCKET_TIMEOUT;
private Set<String> myValidatedServerBaseUrls = Collections.synchronizedSet(new HashSet<String>());
private String myProxyUsername;
private String myProxyPassword;
private int myPoolMaxTotal = DEFAULT_POOL_MAX;
private int myPoolMaxPerRoute = DEFAULT_POOL_MAX_PER_ROUTE;
@ -97,45 +86,21 @@ public class RestfulClientFactory implements IRestfulClientFactory {
return myConnectTimeout;
}
@Override
public synchronized HttpClient getHttpClient() {
if (myHttpClient == null) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
connectionManager.setMaxTotal(myPoolMaxTotal);
connectionManager.setDefaultMaxPerRoute(myPoolMaxPerRoute);
//@formatter:off
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setSocketTimeout(mySocketTimeout)
.setConnectTimeout(myConnectTimeout)
.setConnectionRequestTimeout(myConnectionRequestTimeout)
.setStaleConnectionCheckEnabled(true)
.setProxy(myProxy)
.build();
HttpClientBuilder builder = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(defaultRequestConfig)
.disableCookieManagement();
if (myProxy != null && StringUtils.isNotBlank(myProxyUsername) && StringUtils.isNotBlank(myProxyPassword)) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(myProxy.getHostName(), myProxy.getPort()), new UsernamePasswordCredentials(myProxyUsername, myProxyPassword));
builder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
builder.setDefaultCredentialsProvider(credsProvider);
}
myHttpClient = builder.build();
//@formatter:on
}
return myHttpClient;
/**
* Return the proxy username to authenticate with the HTTP proxy
* @param The proxy username
*/
protected String getProxyUsername() {
return myProxyUsername;
}
private String myProxyUsername;
private String myProxyPassword;
/**
* Return the proxy password to authenticate with the HTTP proxy
* @param The proxy password
*/
protected String getProxyPassword() {
return myProxyPassword;
}
@Override
public void setProxyCredentials(String theUsername, String thePassword) {
@ -189,7 +154,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
ClientInvocationHandlerFactory invocationHandler = myInvocationHandlers.get(theClientType);
if (invocationHandler == null) {
HttpClient httpClient = getHttpClient();
IHttpClient httpClient = getHttpClient(theServerBase);
invocationHandler = new ClientInvocationHandlerFactory(httpClient, myContext, theServerBase, theClientType);
for (Method nextMethod : theClientType.getMethods()) {
BaseMethodBinding<?> binding = BaseMethodBinding.bindMethod(nextMethod, myContext, null);
@ -205,14 +170,12 @@ public class RestfulClientFactory implements IRestfulClientFactory {
@Override
public synchronized IGenericClient newGenericClient(String theServerBase) {
HttpClient httpClient = getHttpClient();
IHttpClient httpClient = getHttpClient(theServerBase);
return new GenericClient(myContext, httpClient, theServerBase, this);
}
/**
* This method is internal to HAPI - It may change in future versions, use with caution.
*/
public void validateServerBaseIfConfiguredToDoSo(String theServerBase, HttpClient theHttpClient, BaseClient theClient) {
@Override
public void validateServerBaseIfConfiguredToDoSo(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) {
String serverBase = normalizeBaseUrlForMap(theServerBase);
switch (myServerValidationMode) {
@ -238,13 +201,13 @@ public class RestfulClientFactory implements IRestfulClientFactory {
@Override
public synchronized void setConnectionRequestTimeout(int theConnectionRequestTimeout) {
myConnectionRequestTimeout = theConnectionRequestTimeout;
myHttpClient = null;
resetHttpClient();
}
@Override
public synchronized void setConnectTimeout(int theConnectTimeout) {
myConnectTimeout = theConnectTimeout;
myHttpClient = null;
resetHttpClient();
}
/**
@ -256,26 +219,13 @@ public class RestfulClientFactory implements IRestfulClientFactory {
}
myContext = theContext;
}
/**
* Sets the Apache HTTP client instance to be used by any new restful clients created by this factory. If set to
* <code>null</code>, which is the default, a new HTTP client with default settings will be created.
*
* @param theHttpClient
* An HTTP client instance to use, or <code>null</code>
* Return the fhir context
* @return the fhir context
*/
@Override
public synchronized void setHttpClient(HttpClient theHttpClient) {
myHttpClient = theHttpClient;
}
@Override
public void setProxy(String theHost, Integer thePort) {
if (theHost != null) {
myProxy = new HttpHost(theHost, thePort, "http");
} else {
myProxy = null;
}
public FhirContext getFhirContext() {
return myContext;
}
@Override
@ -287,24 +237,39 @@ public class RestfulClientFactory implements IRestfulClientFactory {
@Override
public synchronized void setSocketTimeout(int theSocketTimeout) {
mySocketTimeout = theSocketTimeout;
myHttpClient = null;
resetHttpClient();
}
@Override
public synchronized void setPoolMaxTotal(int thePoolMaxTotal) {
myPoolMaxTotal = thePoolMaxTotal;
myHttpClient = null;
resetHttpClient();
}
@Override
public synchronized void setPoolMaxPerRoute(int thePoolMaxPerRoute) {
myPoolMaxPerRoute = thePoolMaxPerRoute;
myHttpClient = null;
resetHttpClient();
}
/**
* Instantiates a new client invocation handler
* @param theClient
* the client which will invoke the call
* @param theUrlBase
* the url base
* @param theMethodToReturnValue
* @param theBindings
* @param theMethodToLambda
* @return a newly created client invocation handler
*/
ClientInvocationHandler newInvocationHandler(IHttpClient theClient, String theUrlBase, Map<Method, Object> theMethodToReturnValue, Map<Method, BaseMethodBinding<?>> theBindings, Map<Method, ClientInvocationHandlerFactory.ILambda> theMethodToLambda) {
return new ClientInvocationHandler(theClient, getFhirContext(), theUrlBase.toString(), theMethodToReturnValue,
theBindings, theMethodToLambda, this);
}
@SuppressWarnings("unchecked")
void validateServerBase(String theServerBase, HttpClient theHttpClient, BaseClient theClient) {
@Override
public void validateServerBase(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) {
GenericClient client = new GenericClient(myContext, theHttpClient, theServerBase, this);
client.setEncoding(theClient.getEncoding());
for (IClientInterceptor interceptor : theClient.getInterceptors()) {
@ -363,5 +328,18 @@ public class RestfulClientFactory implements IRestfulClientFactory {
public void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) {
setServerValidationMode(theServerValidationMode);
}
/**
* Get the http client for the given server base
* @param theServerBase the server base
* @return the http client
*/
protected abstract IHttpClient getHttpClient(String theServerBase);
/**
* Reset the http client. This method is used when parameters have been set and a
* new http client needs to be created
*/
protected abstract void resetHttpClient();
}

View File

@ -0,0 +1,202 @@
package ca.uhn.fhir.rest.client.apache;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicNameValuePair;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.VersionUtil;
/**
* A Http Client based on Apache. This is an adapter around the class
* {@link org.apache.http.client.HttpClient HttpClient}
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class ApacheHttpClient implements IHttpClient {
private HttpClient myClient;
private List<Header> myHeaders;
private StringBuilder myUrl;
private Map<String, List<String>> myIfNoneExistParams;
private String myIfNoneExistString;
private RequestTypeEnum myRequestType;
public ApacheHttpClient(HttpClient theClient, StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
this.myClient = theClient;
this.myUrl = theUrl;
this.myIfNoneExistParams = theIfNoneExistParams;
this.myIfNoneExistString = theIfNoneExistString;
this.myRequestType = theRequestType;
this.myHeaders = theHeaders;
}
@Override
public IHttpRequest createByteRequest(String theContents, String theContentType, EncodingEnum theEncoding) {
/*
* We aren't using a StringEntity here because the constructors
* supported by Android aren't available in non-Android, and vice versa.
* Since we add the content type header manually, it makes no difference
* which one we use anyhow.
*/
ByteArrayEntity entity = new ByteArrayEntity(theContents.getBytes(Constants.CHARSET_UTF8));
ApacheHttpRequest retVal = createHttpRequest(entity);
addHeadersToRequest(retVal, theEncoding);
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, theContentType + Constants.HEADER_SUFFIX_CT_UTF_8);
return retVal;
}
@Override
public IHttpRequest createParamRequest(Map<String, List<String>> theParams, EncodingEnum theEncoding) {
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
for (Entry<String, List<String>> nextParam : theParams.entrySet()) {
List<String> value = nextParam.getValue();
for (String s : value) {
parameters.add(new BasicNameValuePair(nextParam.getKey(), s));
}
}
try {
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "UTF-8");
return createHttpRequest(entity);
} catch (UnsupportedEncodingException e) {
throw new InternalErrorException("Server does not support UTF-8 (should not happen)", e);
}
}
@Override
public IHttpRequest createBinaryRequest(IBaseBinary theBinary) {
/*
* Note: Be careful about changing which constructor we use for
* ByteArrayEntity, as Android's version of HTTPClient doesn't support
* the newer ones for whatever reason.
*/
ByteArrayEntity entity = new ByteArrayEntity(theBinary.getContent());
entity.setContentType(theBinary.getContentType());
return createHttpRequest(entity);
}
@Override
public IHttpRequest createGetRequest(EncodingEnum theEncoding) {
ApacheHttpRequest retVal = createHttpRequest(null);
addHeadersToRequest(retVal, theEncoding);
return retVal;
}
public void addHeadersToRequest(ApacheHttpRequest theHttpRequest, EncodingEnum theEncoding) {
if (myHeaders != null) {
for (Header next : myHeaders) {
theHttpRequest.addHeader(next.getName(), next.getValue());
}
}
theHttpRequest.addHeader("User-Agent", "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client)");
theHttpRequest.addHeader("Accept-Charset", "utf-8");
theHttpRequest.addHeader("Accept-Encoding", "gzip");
if (theEncoding == null) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_ALL);
} else if (theEncoding == EncodingEnum.JSON) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
} else if (theEncoding == EncodingEnum.XML) {
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
}
}
private ApacheHttpRequest createHttpRequest(HttpEntity theEntity) {
HttpRequestBase request = constructRequestBase(theEntity);
ApacheHttpRequest result = new ApacheHttpRequest(myClient, request);
addHeaderIfNoneExist(result);
return result;
}
private void addHeaderIfNoneExist(IHttpRequest result) {
if (myIfNoneExistParams != null) {
StringBuilder b = newHeaderBuilder(myUrl);
BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(myIfNoneExistParams, b, b.indexOf("?") == -1);
result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
if (myIfNoneExistString != null) {
StringBuilder b = newHeaderBuilder(myUrl);
b.append(b.indexOf("?") == -1 ? '?' : '&');
b.append(myIfNoneExistString.substring(myIfNoneExistString.indexOf('?') + 1));
result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
}
private StringBuilder newHeaderBuilder(StringBuilder theUrlBase) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (theUrlBase.length() > 0 && theUrlBase.charAt(theUrlBase.length() - 1) == '/') {
b.deleteCharAt(b.length() - 1);
}
return b;
}
private HttpRequestBase constructRequestBase(HttpEntity theEntity) {
String url = myUrl.toString();
switch (myRequestType) {
case DELETE:
return new HttpDelete(url);
case OPTIONS:
return new HttpOptions(url);
case POST:
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(theEntity);
return httpPost;
case PUT:
HttpPut httpPut = new HttpPut(url);
httpPut.setEntity(theEntity);
return httpPut;
case GET:
default:
return new HttpGet(url);
}
}
}

View File

@ -0,0 +1,101 @@
package ca.uhn.fhir.rest.client.apache;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
/**
* A Http Request based on Apache. This is an adapter around the class
* {@link org.apache.http.client.methods.HttpRequestBase HttpRequestBase}
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class ApacheHttpRequest implements IHttpRequest {
private HttpRequestBase myRequest;
private HttpClient myClient;
public ApacheHttpRequest(HttpClient theClient, HttpRequestBase theApacheRequest) {
this.myClient = theClient;
this.myRequest = theApacheRequest;
}
@Override
public void addHeader(String theName, String theValue) {
myRequest.addHeader(theName, theValue);
}
/**
* Get the ApacheRequest
* @return the ApacheRequest
*/
public HttpRequestBase getApacheRequest() {
return myRequest;
}
@Override
public IHttpResponse execute() throws IOException {
return new ApacheHttpResponse(myClient.execute(myRequest));
}
@Override
public Map<String, List<String>> getAllHeaders() {
Map<String, List<String>> result = new HashMap<String, List<String>>();
for (Header header : myRequest.getAllHeaders()) {
if (!result.containsKey(header.getName())) {
result.put(header.getName(), new LinkedList<String>());
}
result.get(header.getName()).add(header.getValue());
}
return result;
}
@Override
public String toString() {
return myRequest.toString();
}
@Override
public String getRequestBodyFromStream() throws IOException {
if (myRequest instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) myRequest).getEntity();
if (entity.isRepeatable()) {
return IOUtils.toString(entity.getContent());
}
}
return null;
}
}

View File

@ -0,0 +1,163 @@
package ca.uhn.fhir.rest.client.apache;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
* A Http Response based on Apache. This is an adapter around the class
* {@link org.apache.http.HttpResponse HttpResponse}
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class ApacheHttpResponse implements IHttpResponse {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ApacheHttpResponse.class);
private boolean myEntityBuffered = false;
private final HttpResponse myResponse;
private byte[] myEntityBytes;
public ApacheHttpResponse(HttpResponse theResponse) {
this.myResponse = theResponse;
}
@Override
public HttpResponse getResponse() {
return myResponse;
}
@Override
public int getStatus() {
return myResponse.getStatusLine().getStatusCode();
}
@Override
public String getMimeType() {
ContentType ct = ContentType.get(myResponse.getEntity());
return ct != null ? ct.getMimeType() : null;
}
@Override
public Map<String, List<String>> getAllHeaders() {
Map<String, List<String>> headers = new HashMap<String, List<String>>();
if (myResponse.getAllHeaders() != null) {
for (Header next : myResponse.getAllHeaders()) {
String name = next.getName().toLowerCase();
List<String> list = headers.get(name);
if (list == null) {
list = new ArrayList<String>();
headers.put(name, list);
}
list.add(next.getValue());
}
}
return headers;
}
@Override
public String getStatusInfo() {
return myResponse.getStatusLine().getReasonPhrase();
}
@Override
public Reader createReader() throws IOException {
HttpEntity entity = myResponse.getEntity();
if (entity == null) {
return new StringReader("");
}
Charset charset = null;
if (entity.getContentType() != null && entity.getContentType().getElements() != null
&& entity.getContentType().getElements().length > 0) {
ContentType ct = ContentType.get(entity);
charset = ct.getCharset();
}
if (charset == null) {
if (Constants.STATUS_HTTP_204_NO_CONTENT != myResponse.getStatusLine().getStatusCode()) {
ourLog.warn("Response did not specify a charset.");
}
charset = Charset.forName("UTF-8");
}
Reader reader = new InputStreamReader(readEntity(), charset);
return reader;
}
@Override
public InputStream readEntity() throws IOException {
if (this.myEntityBuffered) {
return new ByteArrayInputStream(myEntityBytes);
} else if (myResponse.getEntity() != null) {
return myResponse.getEntity().getContent();
} else {
return null;
}
}
@Override
public void close() {
if (myResponse instanceof CloseableHttpResponse) {
try {
((CloseableHttpResponse) myResponse).close();
} catch (IOException e) {
ourLog.debug("Failed to close response", e);
}
}
}
@Override
public void bufferEntitity() throws IOException {
if (myEntityBuffered) {
return;
}
InputStream respEntity = readEntity();
if (respEntity != null) {
this.myEntityBuffered = true;
try {
this.myEntityBytes = IOUtils.toByteArray(respEntity);
} catch (IllegalStateException e) {
throw new InternalErrorException(e);
}
}
}
}

View File

@ -0,0 +1,139 @@
package ca.uhn.fhir.rest.client.apache;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.ProxyAuthenticationStrategy;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.RestfulClientFactory;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
/**
* A Restful Factory to create clients, requests and responses based on the Apache httpclient library.
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class ApacheRestfulClientFactory extends RestfulClientFactory {
private HttpClient myHttpClient;
private HttpHost myProxy;
/**
* Constructor
*/
public ApacheRestfulClientFactory() {
}
/**
* Constructor
*
* @param theContext
* The context
*/
public ApacheRestfulClientFactory(FhirContext theContext) {
super(theContext);
}
public synchronized HttpClient getNativeHttpClient() {
if (myHttpClient == null) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000,
TimeUnit.MILLISECONDS);
connectionManager.setMaxTotal(getPoolMaxTotal());
connectionManager.setDefaultMaxPerRoute(getPoolMaxPerRoute());
// @formatter:off
RequestConfig defaultRequestConfig = RequestConfig.custom().setSocketTimeout(getSocketTimeout())
.setConnectTimeout(getConnectTimeout()).setConnectionRequestTimeout(getConnectionRequestTimeout())
.setStaleConnectionCheckEnabled(true).setProxy(myProxy).build();
HttpClientBuilder builder = HttpClients.custom().setConnectionManager(connectionManager)
.setDefaultRequestConfig(defaultRequestConfig).disableCookieManagement();
if (myProxy != null && StringUtils.isNotBlank(getProxyUsername()) && StringUtils.isNotBlank(getProxyPassword())) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(myProxy.getHostName(), myProxy.getPort()),
new UsernamePasswordCredentials(getProxyUsername(), getProxyPassword()));
builder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy());
builder.setDefaultCredentialsProvider(credsProvider);
}
myHttpClient = builder.build();
// @formatter:on
}
return myHttpClient;
}
@Override
public IHttpClient getHttpClient(StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
return new ApacheHttpClient(getNativeHttpClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType,
theHeaders);
}
@Override
public void setProxy(String theHost, Integer thePort) {
if (theHost != null) {
myProxy = new HttpHost(theHost, thePort, "http");
} else {
myProxy = null;
}
}
/**
* Only allows to set an instance of type org.apache.http.client.HttpClient
* @see ca.uhn.fhir.rest.client.IRestfulClientFactory#setHttpClient(ca.uhn.fhir.rest.client.api.IHttpClient)
*/
@Override
public synchronized void setHttpClient(Object theHttpClient) {
this.myHttpClient = (HttpClient) theHttpClient;
}
@Override
protected ApacheHttpClient getHttpClient(String theServerBase) {
return new ApacheHttpClient(getNativeHttpClient(), new StringBuilder(theServerBase), null, null, null, null);
}
@Override
protected void resetHttpClient() {
this.myHttpClient = null;
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.rest.client.interceptor;
package ca.uhn.fhir.rest.client.apache;
/*
* #%L
@ -26,11 +26,12 @@ import java.util.zip.GZIPOutputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ByteArrayEntity;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.Constants;
/**
@ -42,7 +43,8 @@ public class GZipContentInterceptor implements IClientInterceptor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GZipContentInterceptor.class);
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequestInterface) {
HttpRequestBase theRequest = ((ApacheHttpRequest) theRequestInterface).getApacheRequest();
if (theRequest instanceof HttpEntityEnclosingRequest) {
Header[] encodingHeaders = theRequest.getHeaders(Constants.HEADER_CONTENT_ENCODING);
if (encodingHeaders == null || encodingHeaders.length == 0) {
@ -69,7 +71,7 @@ public class GZipContentInterceptor implements IClientInterceptor {
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
public void interceptResponse(IHttpResponse theResponse) throws IOException {
// nothing
}

View File

@ -0,0 +1,54 @@
package ca.uhn.fhir.rest.client.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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%
*/
/**
* Represents an HTTP header field.
*/
public class Header {
public final String myName;
public final String myValue;
public Header(String myName, String myValue) {
this.myName = myName;
this.myValue = myValue;
}
/**
* Get the name of the Header.
*
* @return the name of the Header, never {@code null}
*/
public String getName() {
return myName;
}
/**
* Get the value of the Header.
*
* @return the value of the Header, may be {@code null}
*/
public String getValue() {
return myValue;
}
}

View File

@ -0,0 +1,66 @@
package ca.uhn.fhir.rest.client.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.util.List;
import java.util.Map;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import ca.uhn.fhir.rest.server.EncodingEnum;
/**
* A HTTP Client interface.
*/
public interface IHttpClient {
/**
* Create a byte request
* @param theContents the contents
* @param theContentType the contentType
* @param theEncoding the encoding
* @return the http request to be executed
*/
IHttpRequest createByteRequest(String theContents, String theContentType, EncodingEnum theEncoding);
/**
* Create a parameter request
* @param theParams the parameters
* @param theEncoding the encoding
* @return the http request to be executed
*/
IHttpRequest createParamRequest(Map<String, List<String>> theParams, EncodingEnum theEncoding);
/**
* Create a binary request
* @param theBinary the binary
* @return the http request to be executed
*/
IHttpRequest createBinaryRequest(IBaseBinary theBinary);
/**
* Create a normal http get request
* @param theEncoding the request encoding
* @return the http request to be executed
*/
IHttpRequest createGetRequest(EncodingEnum theEncoding);
}

View File

@ -0,0 +1,59 @@
package ca.uhn.fhir.rest.client.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.io.IOException;
import java.util.List;
import java.util.Map;
/**
* Http Request. Allows addition of headers and execution of the request.
*/
public interface IHttpRequest {
/**
* Add a header to the request
* @param theName the header name
* @param theValue the header value
*/
public void addHeader(String theName, String theValue);
/**
* Execute the request
* @return the response
* @throws IOException
*/
public IHttpResponse execute() throws IOException;
/**
* @return all request headers in lower case
*/
public Map<String, List<String>> getAllHeaders();
/**
* Return the requestbody as a string.
* If this is not supported by the underlying technology, null is returned
* @return a string representation of the request or null if not supported or empty.
* @throws IOException
*/
public String getRequestBodyFromStream() throws IOException;
}

View File

@ -0,0 +1,102 @@
package ca.uhn.fhir.rest.client.api;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Map;
/**
* An interface around the HTTP Response.
*/
public interface IHttpResponse {
/**
* Get the status code associated with the response.
* @return the response status code.
*/
public int getStatus();
/**
* @return the native response, depending on the client library used
*/
Object getResponse();
/**
* Extracts {@code Content-Type} value from the response exactly as
* specified by the {@code Content-Type} header. Returns {@code null}
* if not specified.
*/
public String getMimeType();
/**
* Get map of the response headers and corresponding string values.
* @return response headers as a map header keys and they values.
*/
public Map<String, List<String>> getAllHeaders();
/**
* Get the response status information reason phrase associated with the response.
* @return the reason phrase.
*/
public String getStatusInfo();
/**
* Returna reader for the response entity
*/
public Reader createReader() throws IOException;
/**
* Read the message entity input stream as an InputStream.
*/
public InputStream readEntity() throws IOException;
/**
* Close the response
*/
public void close();
/**
* Buffer the message entity data.
* <p>
* In case the message entity is backed by an unconsumed entity input stream,
* all the bytes of the original entity input stream are read and stored in a
* local buffer. The original entity input stream is consumed.
* </p>
* <p>
* In case the response entity instance is not backed by an unconsumed input stream
* an invocation of {@code bufferEntity} method is ignored and the method returns.
* </p>
* <p>
* This operation is idempotent, i.e. it can be invoked multiple times with
* the same effect which also means that calling the {@code bufferEntity()}
* method on an already buffered (and thus closed) message instance is legal
* and has no further effect.
* </p>
* <p>
* Buffering the message entity data allows for multiple invocations of
* {@code readEntity(...)} methods on the response instance.
*/
void bufferEntitity() throws IOException;
}

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.rest.client.api;
*/
import org.apache.http.client.HttpClient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
@ -51,7 +50,7 @@ public interface IRestfulClient {
* Do not call this method in client code. It is a part of the internal HAPI API and
* is subject to change!
*/
HttpClient getHttpClient();
IHttpClient getHttpClient();
/**
* Base URL for the server, with no trailing "/"

View File

@ -25,10 +25,10 @@ import java.io.UnsupportedEncodingException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -52,7 +52,7 @@ public class BasicAuthInterceptor implements IClientInterceptor {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
String authorizationUnescaped = StringUtils.defaultString(myUsername) + ":" + StringUtils.defaultString(myPassword);
String encoded;
try {
@ -64,7 +64,7 @@ public class BasicAuthInterceptor implements IClientInterceptor {
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
public void interceptResponse(IHttpResponse theResponse) throws IOException {
// nothing
}

View File

@ -21,10 +21,10 @@ package ca.uhn.fhir.rest.client.interceptor;
*/
import org.apache.commons.lang3.Validate;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.CoverageIgnore;
@ -71,12 +71,12 @@ public class BearerTokenAuthInterceptor implements IClientInterceptor {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
theRequest.addHeader(Constants.HEADER_AUTHORIZATION, (Constants.HEADER_AUTHORIZATION_VALPREFIX_BEARER + myToken));
}
@Override
public void interceptResponse(HttpResponse theResponse) {
public void interceptResponse(IHttpResponse theResponse) {
// nothing
}

View File

@ -20,10 +20,9 @@ package ca.uhn.fhir.rest.client.interceptor;
* #L%
*/
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
/**
* Client interceptor which simply captures request and response objects and stores them so that they can be inspected after a client
@ -31,8 +30,8 @@ import ca.uhn.fhir.rest.client.IClientInterceptor;
*/
public class CapturingInterceptor implements IClientInterceptor {
private HttpRequestBase myLastRequest;
private HttpResponse myLastResponse;
private IHttpRequest myLastRequest;
private IHttpResponse myLastResponse;
/**
* Clear the last request and response values
@ -42,21 +41,21 @@ public class CapturingInterceptor implements IClientInterceptor {
myLastResponse = null;
}
public HttpRequestBase getLastRequest() {
public IHttpRequest getLastRequest() {
return myLastRequest;
}
public HttpResponse getLastResponse() {
public IHttpResponse getLastResponse() {
return myLastResponse;
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
myLastRequest = theRequest;
}
@Override
public void interceptResponse(HttpResponse theRequest) {
public void interceptResponse(IHttpResponse theRequest) {
myLastResponse = theRequest;
}

View File

@ -20,10 +20,9 @@ package ca.uhn.fhir.rest.client.interceptor;
* #L%
*/
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.Constants;
/**
@ -42,12 +41,12 @@ public class CookieInterceptor implements IClientInterceptor {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
theRequest.addHeader(Constants.HEADER_COOKIE, sessionCookie); //$NON-NLS-1$
}
@Override
public void interceptResponse(HttpResponse theResponse) {
public void interceptResponse(IHttpResponse theResponse) {
// nothing
}
}

View File

@ -20,22 +20,19 @@ package ca.uhn.fhir.rest.client.interceptor;
* #L%
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.HttpEntityWrapper;
import org.slf4j.Logger;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class LoggingInterceptor implements IClientInterceptor {
@ -74,59 +71,39 @@ public class LoggingInterceptor implements IClientInterceptor {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
if (myLogRequestSummary) {
myLog.info("Client request: {}", theRequest);
}
if (myLogRequestHeaders) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < theRequest.getAllHeaders().length; i++) {
Header next = theRequest.getAllHeaders()[i];
b.append(next.getName() + ": " + next.getValue());
if (i + 1 < theRequest.getAllHeaders().length) {
b.append('\n');
}
}
StringBuilder b = headersToString(theRequest.getAllHeaders());
myLog.info("Client request headers:\n{}", b.toString());
}
if (myLogRequestBody) {
if (theRequest instanceof HttpEntityEnclosingRequest) {
HttpEntity entity = ((HttpEntityEnclosingRequest) theRequest).getEntity();
if (entity.isRepeatable()) {
try {
String content = IOUtils.toString(entity.getContent());
myLog.info("Client request body:\n{}", content);
} catch (IllegalStateException e) {
myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
} catch (IOException e) {
myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
}
try {
String content = theRequest.getRequestBodyFromStream();
if (content != null) {
myLog.info("Client request body:\n{}", content);
}
} catch (IllegalStateException e) {
myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
} catch (IOException e) {
myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
}
}
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
public void interceptResponse(IHttpResponse theResponse) throws IOException {
if (myLogResponseSummary) {
String message = "HTTP " + theResponse.getStatusLine().getStatusCode() + " " + theResponse.getStatusLine().getReasonPhrase();
String message = "HTTP " + theResponse.getStatus() + " " + theResponse.getStatusInfo();
myLog.info("Client response: {}", message);
}
if (myLogResponseHeaders) {
StringBuilder b = new StringBuilder();
if (theResponse.getAllHeaders() != null) {
for (int i = 0; i < theResponse.getAllHeaders().length; i++) {
Header next = theResponse.getAllHeaders()[i];
b.append(next.getName() + ": " + next.getValue());
if (i + 1 < theResponse.getAllHeaders().length) {
b.append('\n');
}
}
}
StringBuilder b = headersToString(theResponse.getAllHeaders());
// if (theResponse.getEntity() != null && theResponse.getEntity().getContentEncoding() != null) {
// Header next = theResponse.getEntity().getContentEncoding();
// b.append(next.getName() + ": " + next.getValue());
@ -143,23 +120,46 @@ public class LoggingInterceptor implements IClientInterceptor {
}
if (myLogResponseBody) {
HttpEntity respEntity = theResponse.getEntity();
if (respEntity != null) {
final byte[] bytes;
try {
bytes = IOUtils.toByteArray(respEntity.getContent());
} catch (IllegalStateException e) {
throw new InternalErrorException(e);
}
myLog.info("Client response body:\n{}", new String(bytes, "UTF-8"));
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
} else {
myLog.info("Client response body: (none)");
theResponse.bufferEntitity();
InputStream respEntity = null;
try {
respEntity = theResponse.readEntity();
if (respEntity != null) {
final byte[] bytes;
try {
bytes = IOUtils.toByteArray(respEntity);
} catch (IllegalStateException e) {
throw new InternalErrorException(e);
}
myLog.info("Client response body:\n{}", new String(bytes, "UTF-8"));
} else {
myLog.info("Client response body: (none)");
}
} finally {
IOUtils.closeQuietly(respEntity);
}
}
}
private StringBuilder headersToString(Map<String, List<String>> theHeaders) {
StringBuilder b = new StringBuilder();
if (theHeaders != null && !theHeaders.isEmpty()) {
Iterator<String> nameEntries = theHeaders.keySet().iterator();
while(nameEntries.hasNext()) {
String key = nameEntries.next();
Iterator<String> values = theHeaders.get(key).iterator();
while(values.hasNext()) {
String value = values.next();
b.append(key + ": " + value);
if (nameEntries.hasNext() || values.hasNext()) {
b.append('\n');
}
}
}
}
return b;
}
/**
* Sets a logger to use to log messages (default is a logger with this class' name). This can be used to redirect
* logs to a differently named logger instead.
@ -214,25 +214,4 @@ public class LoggingInterceptor implements IClientInterceptor {
myLogResponseSummary = theValue;
}
private static class MyEntityWrapper extends HttpEntityWrapper {
private byte[] myBytes;
public MyEntityWrapper(HttpEntity theWrappedEntity, byte[] theBytes) {
super(theWrappedEntity);
myBytes = theBytes;
}
@Override
public InputStream getContent() throws IOException {
return new ByteArrayInputStream(myBytes);
}
@Override
public void writeTo(OutputStream theOutstream) throws IOException {
theOutstream.write(myBytes);
}
}
}

View File

@ -22,10 +22,9 @@ package ca.uhn.fhir.rest.client.interceptor;
import java.io.IOException;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
/**
* HTTP interceptor to be used for adding HTTP headers containing user identifying info for auditing purposes to the request
@ -48,14 +47,14 @@ public class UserInfoInterceptor implements IClientInterceptor {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
if(myUserId != null) theRequest.addHeader(HEADER_USER_ID, myUserId);
if(myUserName != null) theRequest.addHeader(HEADER_USER_NAME, myUserName);
if(myAppName != null) theRequest.addHeader(HEADER_APPLICATION_NAME, myAppName);
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
public void interceptResponse(IHttpResponse theResponse) throws IOException {
// nothing
}

View File

@ -28,8 +28,8 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
class AddTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
public AddTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, AddTags theAnnotation) {
super(theMethod, theConetxt, theProvider, theAnnotation.type());
public AddTagsMethodBinding(Method theMethod, FhirContext theContext, Object theProvider, AddTags theAnnotation) {
super(theMethod, theContext, theProvider, theAnnotation.type());
}
@Override

View File

@ -56,8 +56,8 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
private String myResourceName;
private Integer myTagListParamIndex;
public BaseAddOrDeleteTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, Class<? extends IResource> theTypeFromMethodAnnotation) {
super(theMethod, theConetxt, theProvider);
public BaseAddOrDeleteTagsMethodBinding(Method theMethod, FhirContext theContext, Object theProvider, Class<? extends IResource> theTypeFromMethodAnnotation) {
super(theMethod, theContext, theProvider);
if (theProvider instanceof IResourceProvider) {
myType = ((IResourceProvider) theProvider).getResourceType();
@ -69,7 +69,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
throw new ConfigurationException("Method '" + theMethod.getName() + "' does not specify a resource type, but has an @" + IdParam.class.getSimpleName() + " parameter. Please specity a resource type in the method annotation on this method");
}
myResourceName = theConetxt.getResourceDefinition(myType).getName();
myResourceName = theContext.getResourceDefinition(myType).getName();
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext());
myVersionIdParamIndex = MethodUtil.findVersionIdParameterIndex(theMethod);

View File

@ -23,19 +23,10 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* #L%
*/
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.message.BasicNameValuePair;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -47,10 +38,11 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
* @author James Agnew
@ -63,7 +55,6 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
private final BundleTypeEnum myBundleType;
private final String myContents;
private boolean myContentsIsBundle;
private final FhirContext myContext;
private Map<String, List<String>> myIfNoneExistParams;
private String myIfNoneExistString;
private boolean myOmitResourceId = false;
@ -74,7 +65,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
private final String myUrlPath;
public BaseHttpClientInvocationWithContents(FhirContext theContext, Bundle theBundle) {
myContext = theContext;
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = null;
@ -84,8 +75,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundleType = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, IBaseResource theResource, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
public BaseHttpClientInvocationWithContents(FhirContext theContext, IBaseResource theResource,
Map<String, List<String>> theParams, String... theUrlPath) {
super(theContext);
myResource = theResource;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
@ -98,8 +90,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, IBaseResource theResource, String theUrlPath) {
super();
myContext = theContext;
super(theContext);
myResource = theResource;
myUrlPath = theUrlPath;
myTagList = null;
@ -109,8 +100,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundleType = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, List<? extends IBaseResource> theResources, BundleTypeEnum theBundleType) {
myContext = theContext;
public BaseHttpClientInvocationWithContents(FhirContext theContext, List<? extends IBaseResource> theResources,
BundleTypeEnum theBundleType) {
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = null;
@ -120,8 +112,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundleType = theBundleType;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
public BaseHttpClientInvocationWithContents(FhirContext theContext, Map<String, List<String>> theParams,
String... theUrlPath) {
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
@ -133,8 +126,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundleType = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlPath) {
myContext = theContext;
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, boolean theIsBundle,
String theUrlPath) {
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = theUrlPath;
@ -145,8 +139,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundleType = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents,
Map<String, List<String>> theParams, String... theUrlPath) {
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
@ -159,13 +154,12 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlPath) {
super();
super(theContext);
if (theTagList == null) {
throw new NullPointerException("Tag list must not be null");
}
myResource = null;
myContext = theContext;
myTagList = theTagList;
myResources = null;
myBundle = null;
@ -175,23 +169,8 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myUrlPath = StringUtils.join(theUrlPath, '/');
}
private void addMatchHeaders(HttpRequestBase theHttpRequest, StringBuilder theUrlBase) {
if (myIfNoneExistParams != null) {
StringBuilder b = newHeaderBuilder(theUrlBase);
appendExtraParamsWithQuestionMark(myIfNoneExistParams, b, b.indexOf("?") == -1);
theHttpRequest.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
if (myIfNoneExistString != null) {
StringBuilder b = newHeaderBuilder(theUrlBase);
b.append(b.indexOf("?") == -1 ? '?' : '&');
b.append(myIfNoneExistString.substring(myIfNoneExistString.indexOf('?') + 1));
theHttpRequest.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
}
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) throws DataFormatException {
public IHttpRequest asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) throws DataFormatException {
StringBuilder url = new StringBuilder();
if (myUrlPath == null) {
@ -207,43 +186,46 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
appendExtraParamsWithQuestionMark(theExtraParams, url, url.indexOf("?") == -1);
IHttpClient httpClient = getRestfulClientFactory().getHttpClient(url, myIfNoneExistParams, myIfNoneExistString, getRequestType(), getHeaders());
if (myResource != null && IBaseBinary.class.isAssignableFrom(myResource.getClass())) {
IBaseBinary binary = (IBaseBinary) myResource;
if (isBlank(binary.getContentType()) || EncodingEnum.forContentTypeStrict(binary.getContentType()) != null) {
ourLog.trace("Binary has Content-Type {}, encoding as a FHIR resource instead of raw", binary.getContentType());
return httpClient.createBinaryRequest((IBaseBinary) myResource);
} else {
EncodingEnum encoding = theEncoding;
if (myContents != null) {
encoding = MethodUtil.detectEncoding(myContents);
}
if (encoding == null) {
encoding = EncodingEnum.XML;
}
if (myParams != null) {
return httpClient.createParamRequest(myParams, encoding);
} else {
/*
* Note: Be careful about changing which constructor we use for ByteArrayEntity,
* as Android's version of HTTPClient doesn't support the newer ones for
* whatever reason.
*/
ByteArrayEntity entity = new ByteArrayEntity(binary.getContent());
HttpRequestBase retVal = createRequest(url, entity);
addMatchHeaders(retVal, url);
super.addHeadersToRequest(retVal, null);
if (isNotBlank(binary.getContentType())) {
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, binary.getContentType());
}
return retVal;
String contents = parseContents(thePrettyPrint, encoding);
String contentType = getContentType(encoding);
return httpClient.createByteRequest(contents, contentType, encoding);
}
}
}
IParser parser;
String contentType;
EncodingEnum encoding = null;
encoding = theEncoding;
if (myContents != null) {
encoding = MethodUtil.detectEncoding(myContents);
private String getContentType(EncodingEnum encoding) {
if (myBundle != null || (getFhirContext().getVersion().getVersion() == FhirVersionEnum.DSTU1
&& ((myContents != null && myContentsIsBundle) || myResources != null))) {
return encoding.getBundleContentType();
} else {
return encoding.getResourceContentType();
}
}
private String parseContents(Boolean thePrettyPrint, EncodingEnum encoding) {
IParser parser;
if (encoding == EncodingEnum.JSON) {
parser = myContext.newJsonParser();
parser = getFhirContext().newJsonParser();
} else {
encoding = EncodingEnum.XML;
parser = myContext.newXmlParser();
parser = getFhirContext().newXmlParser();
}
if (thePrettyPrint != null) {
@ -251,84 +233,27 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
parser.setOmitResourceId(myOmitResourceId);
AbstractHttpEntity entity;
if (myParams != null) {
contentType = null;
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
for (Entry<String, List<String>> nextParam : myParams.entrySet()) {
List<String> value = nextParam.getValue();
for (String s : value) {
parameters.add(new BasicNameValuePair(nextParam.getKey(), s));
}
}
try {
entity = new UrlEncodedFormEntity(parameters, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InternalErrorException("Server does not support UTF-8 (should not happen)", e);
}
} else {
String contents;
if (myTagList != null) {
contents = parser.encodeTagListToString(myTagList);
contentType = encoding.getResourceContentType();
} else if (myBundle != null) {
contents = parser.encodeBundleToString(myBundle);
contentType = encoding.getBundleContentType();
} else if (myResources != null) {
IVersionSpecificBundleFactory bundleFactory = myContext.newBundleFactory();
bundleFactory.initializeBundleFromResourceList("", myResources, "", "", myResources.size(), myBundleType);
Bundle bundle = bundleFactory.getDstu1Bundle();
if (bundle != null) {
contents = parser.encodeBundleToString(bundle);
contentType = encoding.getBundleContentType();
} else {
IBaseResource bundleRes = bundleFactory.getResourceBundle();
contents = parser.encodeResourceToString(bundleRes);
contentType = encoding.getResourceContentType();
}
} else if (myContents != null) {
contents = myContents;
if (myContentsIsBundle && myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
contentType = encoding.getBundleContentType();
} else {
contentType = encoding.getResourceContentType();
}
if (myTagList != null) {
return parser.encodeTagListToString(myTagList);
} else if (myBundle != null) {
return parser.encodeBundleToString(myBundle);
} else if (myResources != null) {
IVersionSpecificBundleFactory bundleFactory = getFhirContext().newBundleFactory();
bundleFactory.initializeBundleFromResourceList("", myResources, "", "", myResources.size(), myBundleType);
Bundle bundle = bundleFactory.getDstu1Bundle();
if (bundle != null) {
return parser.encodeBundleToString(bundle);
} else {
contents = parser.encodeResourceToString(myResource);
contentType = encoding.getResourceContentType();
IBaseResource bundleRes = bundleFactory.getResourceBundle();
return parser.encodeResourceToString(bundleRes);
}
/*
* We aren't using a StringEntity here because the constructors supported by
* Android aren't available in non-Android, and vice versa. Since we add the
* content type header manually, it makes no difference which one
* we use anyhow.
*/
entity = new ByteArrayEntity(contents.getBytes(Constants.CHARSET_UTF8));
} else if (myContents != null) {
return myContents;
} else {
return parser.encodeResourceToString(myResource);
}
HttpRequestBase retVal = createRequest(url, entity);
super.addHeadersToRequest(retVal, encoding);
addMatchHeaders(retVal, url);
if (contentType != null) {
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, contentType + Constants.HEADER_SUFFIX_CT_UTF_8);
}
return retVal;
}
protected abstract HttpRequestBase createRequest(StringBuilder theUrl, AbstractHttpEntity theEntity);
private StringBuilder newHeaderBuilder(StringBuilder theUrlBase) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (theUrlBase.length() > 0 && theUrlBase.charAt(theUrlBase.length() - 1) == '/') {
b.deleteCharAt(b.length() - 1);
}
return b;
}
public void setIfNoneExistParams(Map<String, List<String>> theIfNoneExist) {
myIfNoneExistParams = theIfNoneExist;
}
@ -341,4 +266,9 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myOmitResourceId = theOmitResourceId;
}
/**
* Get the HTTP request type.
*/
protected abstract RequestTypeEnum getRequestType();
}

View File

@ -60,7 +60,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
@Override
public HttpGetClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
HttpGetClientInvocation retVal = MethodUtil.createConformanceInvocation();
HttpGetClientInvocation retVal = MethodUtil.createConformanceInvocation(getContext());
if (theArgs != null) {
for (int idx = 0; idx < theArgs.length; idx++) {

View File

@ -119,7 +119,7 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
throw new InvalidRequestException("ID parameter has the wrong resource type, expected '" + getResourceName() + "', found: " + idDt.getResourceType());
}
HttpDeleteClientInvocation retVal = createDeleteInvocation(idDt);
HttpDeleteClientInvocation retVal = createDeleteInvocation(getContext(), idDt);
for (int idx = 0; idx < theArgs.length; idx++) {
IParameter nextParam = getParameters().get(idx);
@ -129,8 +129,8 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
return retVal;
}
public static HttpDeleteClientInvocation createDeleteInvocation(IIdType theId) {
HttpDeleteClientInvocation retVal = new HttpDeleteClientInvocation(theId);
public static HttpDeleteClientInvocation createDeleteInvocation(FhirContext theContext, IIdType theId) {
HttpDeleteClientInvocation retVal = new HttpDeleteClientInvocation(theContext, theId);
return retVal;
}
@ -144,13 +144,13 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
return null;
}
public static HttpDeleteClientInvocation createDeleteInvocation(String theSearchUrl) {
HttpDeleteClientInvocation retVal = new HttpDeleteClientInvocation(theSearchUrl);
public static HttpDeleteClientInvocation createDeleteInvocation(FhirContext theContext, String theSearchUrl) {
HttpDeleteClientInvocation retVal = new HttpDeleteClientInvocation(theContext, theSearchUrl);
return retVal;
}
public static HttpDeleteClientInvocation createDeleteInvocation(String theResourceType, Map<String, List<String>> theParams) {
return new HttpDeleteClientInvocation(theResourceType, theParams);
public static HttpDeleteClientInvocation createDeleteInvocation(FhirContext theContext, String theResourceType, Map<String, List<String>> theParams) {
return new HttpDeleteClientInvocation(theContext, theResourceType, theParams);
}
}

View File

@ -28,8 +28,8 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
public class DeleteTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
public DeleteTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, DeleteTags theDeleteTags) {
super(theMethod, theConetxt, theProvider, theDeleteTags.type());
public DeleteTagsMethodBinding(Method theMethod, FhirContext theContext, Object theProvider, DeleteTags theDeleteTags) {
super(theMethod, theContext, theProvider, theDeleteTags.type());
}
@Override

View File

@ -48,8 +48,8 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi
private HashSet<String> myParamNames;
private Integer myIdParamIndex;
public DynamicSearchMethodBinding(Class<? extends IBaseResource> theReturnResourceType, Method theMethod, FhirContext theConetxt, IDynamicSearchResourceProvider theProvider) {
super(theReturnResourceType, theMethod, theConetxt, theProvider);
public DynamicSearchMethodBinding(Class<? extends IBaseResource> theReturnResourceType, Method theMethod, FhirContext theContext, IDynamicSearchResourceProvider theProvider) {
super(theReturnResourceType, theMethod, theContext, theProvider);
myProvider = theProvider;
mySearchParameters = myProvider.getSearchParameters();

View File

@ -54,8 +54,8 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
private Class<? extends IBaseResource> myType;
private Integer myVersionIdParamIndex;
public GetTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, GetTags theAnnotation) {
super(theMethod, theConetxt, theProvider);
public GetTagsMethodBinding(Method theMethod, FhirContext theContext, Object theProvider, GetTags theAnnotation) {
super(theMethod, theContext, theProvider);
if (theProvider instanceof IResourceProvider) {
myType = ((IResourceProvider) theProvider).getResourceType();
@ -64,7 +64,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
}
if (!Modifier.isInterface(myType.getModifiers())) {
myResourceName = theConetxt.getResourceDefinition(myType).getName();
myResourceName = theContext.getResourceDefinition(myType).getName();
}
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext());
@ -127,17 +127,17 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
if (myType != IResource.class) {
if (id != null) {
if (versionId != null) {
retVal = new HttpGetClientInvocation(getResourceName(), id.getIdPart(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getContext(), getResourceName(), id.getIdPart(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
} else if (id.hasVersionIdPart()) {
retVal = new HttpGetClientInvocation(getResourceName(), id.getIdPart(), Constants.PARAM_HISTORY, id.getVersionIdPart(), Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getContext(), getResourceName(), id.getIdPart(), Constants.PARAM_HISTORY, id.getVersionIdPart(), Constants.PARAM_TAGS);
} else {
retVal = new HttpGetClientInvocation(getResourceName(), id.getIdPart(), Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getContext(), getResourceName(), id.getIdPart(), Constants.PARAM_TAGS);
}
} else {
retVal = new HttpGetClientInvocation(getResourceName(), Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getContext(), getResourceName(), Constants.PARAM_TAGS);
}
} else {
retVal = new HttpGetClientInvocation(Constants.PARAM_TAGS);
retVal = new HttpGetClientInvocation(getContext(), Constants.PARAM_TAGS);
}
if (theArgs != null) {

View File

@ -53,8 +53,8 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
private String myResourceName;
private final RestOperationTypeEnum myResourceOperationType;
public HistoryMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
super(toReturnType(theMethod, theProvider), theMethod, theConetxt, theProvider);
public HistoryMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
super(toReturnType(theMethod, theProvider), theMethod, theContext, theProvider);
myIdParamIndex = MethodUtil.findIdParameterIndex(theMethod, getContext());
@ -80,7 +80,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
if (type != IResource.class) {
myResourceName = theConetxt.getResourceDefinition(type).getName();
myResourceName = theContext.getResourceDefinition(type).getName();
} else {
myResourceName = null;
}
@ -142,7 +142,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
}
String historyId = id != null ? id.getIdPart() : null;
HttpGetClientInvocation retVal = createHistoryInvocation(resourceName, historyId, null, null);
HttpGetClientInvocation retVal = createHistoryInvocation(getContext(), resourceName, historyId, null, null);
if (theArgs != null) {
for (int idx = 0; idx < theArgs.length; idx++) {
@ -206,7 +206,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
};
}
public static HttpGetClientInvocation createHistoryInvocation(String theResourceName, String theId, IPrimitiveType<Date> theSince, Integer theLimit) {
public static HttpGetClientInvocation createHistoryInvocation(FhirContext theContext, String theResourceName, String theId, IPrimitiveType<Date> theSince, Integer theLimit) {
StringBuilder b = new StringBuilder();
if (theResourceName != null) {
b.append(theResourceName);
@ -230,7 +230,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
b.append(Constants.PARAM_COUNT).append('=').append(theLimit);
}
HttpGetClientInvocation retVal = new HttpGetClientInvocation(b.toString());
HttpGetClientInvocation retVal = new HttpGetClientInvocation(theContext, b.toString());
return retVal;
}

View File

@ -23,11 +23,12 @@ package ca.uhn.fhir.rest.method;
import java.util.List;
import java.util.Map;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpRequestBase;
import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.server.EncodingEnum;
public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
@ -35,22 +36,24 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
private String myUrlPath;
private Map<String, List<String>> myParams;
public HttpDeleteClientInvocation(IIdType theId) {
super();
public HttpDeleteClientInvocation(FhirContext theContext, IIdType theId) {
super(theContext);
myUrlPath = theId.toUnqualifiedVersionless().getValue();
}
public HttpDeleteClientInvocation(String theSearchUrl) {
public HttpDeleteClientInvocation(FhirContext theContext, String theSearchUrl) {
super(theContext);
myUrlPath = theSearchUrl;
}
public HttpDeleteClientInvocation(String theResourceType, Map<String, List<String>> theParams) {
public HttpDeleteClientInvocation(FhirContext theContext, String theResourceType, Map<String, List<String>> theParams) {
super(theContext);
myUrlPath = theResourceType;
myParams = theParams;
}
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
public IHttpRequest asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
@ -61,9 +64,7 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
appendExtraParamsWithQuestionMark(myParams, b, b.indexOf("?") == -1);
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);
HttpDelete retVal = new HttpDelete(b.toString());
super.addHeadersToRequest(retVal, theEncoding);
return retVal;
return createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.DELETE);
}
}

View File

@ -28,10 +28,12 @@ import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.HttpGet;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.server.EncodingEnum;
/**
@ -43,28 +45,33 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
private final Map<String, List<String>> myParameters;
private final String myUrlPath;
public HttpGetClientInvocation(Map<String, List<String>> theParameters, String... theUrlFragments) {
public HttpGetClientInvocation(FhirContext theContext, Map<String, List<String>> theParameters, String... theUrlFragments) {
super(theContext);
myParameters = theParameters;
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
public HttpGetClientInvocation(Map<String, List<String>> theParameters, List<String> theUrlFragments) {
public HttpGetClientInvocation(FhirContext theContext, Map<String, List<String>> theParameters, List<String> theUrlFragments) {
super(theContext);
myParameters = theParameters;
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
public HttpGetClientInvocation(String theUrlPath) {
public HttpGetClientInvocation(FhirContext theContext, String theUrlPath) {
super(theContext);
myParameters = new HashMap<String, List<String>>();
myUrlPath = theUrlPath;
}
public HttpGetClientInvocation(String... theUrlFragments) {
public HttpGetClientInvocation(FhirContext theContext, String... theUrlFragments) {
super(theContext);
myParameters = new HashMap<String, List<String>>();
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
public HttpGetClientInvocation(List<String> theUrlFragments) {
public HttpGetClientInvocation(FhirContext theContext, List<String> theUrlFragments) {
super(theContext);
myParameters = new HashMap<String, List<String>>();
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
@ -78,7 +85,7 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
}
@Override
public HttpGet asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
public IHttpRequest asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
StringBuilder b = new StringBuilder();
if (!myUrlPath.contains("://")) {
@ -102,10 +109,7 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
appendExtraParamsWithQuestionMark(theExtraParams, b, first);
HttpGet retVal = new HttpGet(b.toString());
super.addHeadersToRequest(retVal, theEncoding);
return retVal;
return super.createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.GET);
}
private boolean addQueryParameter(StringBuilder b, boolean first, String nextKey, String nextValue) {

View File

@ -23,14 +23,13 @@ package ca.uhn.fhir.rest.method;
import java.util.List;
import java.util.Map;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.AbstractHttpEntity;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
public class HttpPostClientInvocation extends BaseHttpClientInvocationWithContents {
@ -63,11 +62,8 @@ public class HttpPostClientInvocation extends BaseHttpClientInvocationWithConten
super(theContext, theParams, theUrlExtension);
}
@Override
protected HttpPost createRequest(StringBuilder theUrlBase, AbstractHttpEntity theEntity) {
HttpPost retVal = new HttpPost(theUrlBase.toString());
retVal.setEntity(theEntity);
return retVal;
protected RequestTypeEnum getRequestType() {
return RequestTypeEnum.POST;
}
}

View File

@ -20,12 +20,10 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.AbstractHttpEntity;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
public class HttpPutClientInvocation extends BaseHttpClientInvocationWithContents {
@ -38,10 +36,8 @@ public class HttpPutClientInvocation extends BaseHttpClientInvocationWithContent
}
@Override
protected HttpRequestBase createRequest(StringBuilder theUrl, AbstractHttpEntity theEntity) {
HttpPut retVal = new HttpPut(theUrl.toString());
retVal.setEntity(theEntity);
return retVal;
protected RequestTypeEnum getRequestType() {
return RequestTypeEnum.PUT;
}
}

View File

@ -23,25 +23,24 @@ package ca.uhn.fhir.rest.method;
import java.util.List;
import java.util.Map;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.server.EncodingEnum;
public class HttpSimpleGetClientInvocation extends BaseHttpClientInvocation {
private final String myUrl;
public HttpSimpleGetClientInvocation(String theUrlPath) {
public HttpSimpleGetClientInvocation(FhirContext theContext, String theUrlPath) {
super(theContext);
myUrl = theUrlPath;
}
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
HttpGet retVal = new HttpGet(myUrl);
super.addHeadersToRequest(retVal, theEncoding);
return retVal;
public IHttpRequest asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
return createHttpRequest(myUrl, theEncoding, RequestTypeEnum.GET);
}
}

View File

@ -24,7 +24,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.DateUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -89,6 +88,7 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
import ca.uhn.fhir.rest.server.SearchParameterMap;
import ca.uhn.fhir.util.DateUtils;
import ca.uhn.fhir.util.ReflectionUtil;
/*
@ -146,8 +146,8 @@ public class MethodUtil {
return value;
}
public static HttpGetClientInvocation createConformanceInvocation() {
return new HttpGetClientInvocation("metadata");
public static HttpGetClientInvocation createConformanceInvocation(FhirContext theContext) {
return new HttpGetClientInvocation(theContext, "metadata");
}
public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, FhirContext theContext) {

View File

@ -358,7 +358,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
IPrimitiveType<?> primitive = (IPrimitiveType<?>) value;
params.get(nextName).add(primitive.getValueAsString());
}
return new HttpGetClientInvocation(params, b.toString());
return new HttpGetClientInvocation(theContext, params, b.toString());
}
}

View File

@ -167,15 +167,15 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
if (myVersionIdIndex == null) {
String resourceName = getResourceName();
if (id.hasVersionIdPart()) {
retVal = createVReadInvocation(new IdDt(resourceName, id.getIdPart(), id.getVersionIdPart()), resourceName);
retVal = createVReadInvocation(getContext(), new IdDt(resourceName, id.getIdPart(), id.getVersionIdPart()), resourceName);
} else {
retVal = createReadInvocation(id, resourceName);
retVal = createReadInvocation(getContext(), id, resourceName);
}
} else {
IdDt vid = ((IdDt) theArgs[myVersionIdIndex]);
String resourceName = getResourceName();
retVal = createVReadInvocation(new IdDt(resourceName, id.getIdPart(), vid.getVersionIdPart()), resourceName);
retVal = createVReadInvocation(getContext(), new IdDt(resourceName, id.getIdPart(), vid.getVersionIdPart()), resourceName);
}
for (int idx = 0; idx < theArgs.length; idx++) {
@ -249,20 +249,20 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
return mySupportsVersion || myVersionIdIndex != null;
}
public static HttpGetClientInvocation createAbsoluteReadInvocation(IIdType theId) {
return new HttpGetClientInvocation(theId.toVersionless().getValue());
public static HttpGetClientInvocation createAbsoluteReadInvocation(FhirContext theContext, IIdType theId) {
return new HttpGetClientInvocation(theContext, theId.toVersionless().getValue());
}
public static HttpGetClientInvocation createAbsoluteVReadInvocation(IIdType theId) {
return new HttpGetClientInvocation(theId.getValue());
public static HttpGetClientInvocation createAbsoluteVReadInvocation(FhirContext theContext, IIdType theId) {
return new HttpGetClientInvocation(theContext, theId.getValue());
}
public static HttpGetClientInvocation createReadInvocation(IIdType theId, String theResourceName) {
return new HttpGetClientInvocation(new IdDt(theResourceName, theId.getIdPart()).getValue());
public static HttpGetClientInvocation createReadInvocation(FhirContext theContext, IIdType theId, String theResourceName) {
return new HttpGetClientInvocation(theContext, new IdDt(theResourceName, theId.getIdPart()).getValue());
}
public static HttpGetClientInvocation createVReadInvocation(IIdType theId, String theResourceName) {
return new HttpGetClientInvocation(new IdDt(theResourceName, theId.getIdPart(), theId.getVersionIdPart()).getValue());
public static HttpGetClientInvocation createVReadInvocation(FhirContext theContext, IIdType theId, String theResourceName) {
return new HttpGetClientInvocation(theContext, new IdDt(theResourceName, theId.getIdPart(), theId.getVersionIdPart()).getValue());
}
@Override

View File

@ -4,6 +4,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
/*
* #%L
@ -274,4 +275,9 @@ public abstract class RequestDetails {
mySecondaryOperation = theSecondaryOperation;
}
/**
* Return the charset as defined by the header contenttype. Return null if it is not set.
*/
public abstract Charset getCharset();
}

View File

@ -340,16 +340,16 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
case GET:
default:
if (compartmentSearch) {
invocation = new HttpGetClientInvocation(theParameters, theResourceName, theId.getIdPart(), theCompartmentName);
invocation = new HttpGetClientInvocation(theContext, theParameters, theResourceName, theId.getIdPart(), theCompartmentName);
} else {
invocation = new HttpGetClientInvocation(theParameters, theResourceName);
invocation = new HttpGetClientInvocation(theContext, theParameters, theResourceName);
}
break;
case GET_WITH_SEARCH:
if (compartmentSearch) {
invocation = new HttpGetClientInvocation(theParameters, theResourceName, theId.getIdPart(), theCompartmentName, Constants.PARAM_SEARCH);
invocation = new HttpGetClientInvocation(theContext, theParameters, theResourceName, theId.getIdPart(), theCompartmentName, Constants.PARAM_SEARCH);
} else {
invocation = new HttpGetClientInvocation(theParameters, theResourceName, Constants.PARAM_SEARCH);
invocation = new HttpGetClientInvocation(theContext, theParameters, theResourceName, Constants.PARAM_SEARCH);
}
break;
case POST:
@ -456,8 +456,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
}
public static BaseHttpClientInvocation createSearchInvocation(String theSearchUrl, Map<String, List<String>> theParams) {
return new HttpGetClientInvocation(theParams, theSearchUrl);
public static BaseHttpClientInvocation createSearchInvocation(FhirContext theContext, String theSearchUrl, Map<String, List<String>> theParams) {
return new HttpGetClientInvocation(theContext, theParams, theSearchUrl);
}
}

View File

@ -53,8 +53,8 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
private int myTransactionParamIndex;
private ParamStyle myTransactionParamStyle;
public TransactionMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
super(null, theMethod, theConetxt, theProvider);
public TransactionMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
super(null, theMethod, theContext, theProvider);
myTransactionParamIndex = -1;
int index = 0;

View File

@ -20,7 +20,6 @@ package ca.uhn.fhir.rest.param;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -35,7 +34,6 @@ import java.util.Map;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.apache.http.entity.ContentType;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -130,13 +128,7 @@ public class ResourceParameter implements IParameter {
}
public static Charset determineRequestCharset(RequestDetails theRequest) {
String ct = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
Charset charset = null;
if (isNotBlank(ct)) {
ContentType parsedCt = ContentType.parse(ct);
charset = parsedCt.getCharset();
}
Charset charset = theRequest.getCharset();
if (charset == null) {
charset = Charset.forName("UTF-8");
}

View File

@ -43,7 +43,6 @@ import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.utils.DateUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -69,6 +68,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.method.SummaryEnumParameter;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.DateUtils;
public class RestfulServerUtils {
static final Pattern ACCEPT_HEADER_PATTERN = Pattern.compile("\\s*([a-zA-Z0-9+.*/-]+)\\s*(;\\s*([a-zA-Z]+)\\s*=\\s*([a-zA-Z0-9.]+)\\s*)?(,?)");

View File

@ -1,7 +1,5 @@
package ca.uhn.fhir.rest.server.servlet;
import java.io.ByteArrayInputStream;
/*
* #%L
* HAPI FHIR - Core Library
@ -21,10 +19,13 @@ import java.io.ByteArrayInputStream;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
@ -37,6 +38,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.http.entity.ContentType;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
@ -176,4 +178,16 @@ public class ServletRequestDetails extends RequestDetails {
return retVal;
}
@Override
public Charset getCharset() {
String ct = getHeader(Constants.HEADER_CONTENT_TYPE);
Charset charset = null;
if (isNotBlank(ct)) {
ContentType parsedCt = ContentType.parse(ct);
charset = parsedCt.getCharset();
}
return charset;
}
}

View File

@ -0,0 +1,254 @@
package ca.uhn.fhir.util;
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
import java.lang.ref.SoftReference;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
/**
* A utility class for parsing and formatting HTTP dates as used in cookies and
* other headers. This class handles dates as defined by RFC 2616 section
* 3.3.1 as well as some other common non-standard formats.
*
* @since 4.3
*/
public final class DateUtils {
/**
* Date format pattern used to parse HTTP date headers in RFC 1123 format.
*/
public static final String PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
/**
* Date format pattern used to parse HTTP date headers in RFC 1036 format.
*/
public static final String PATTERN_RFC1036 = "EEE, dd-MMM-yy HH:mm:ss zzz";
/**
* Date format pattern used to parse HTTP date headers in ANSI C
* {@code asctime()} format.
*/
public static final String PATTERN_ASCTIME = "EEE MMM d HH:mm:ss yyyy";
private static final String[] DEFAULT_PATTERNS = new String[] {
PATTERN_RFC1123,
PATTERN_RFC1036,
PATTERN_ASCTIME
};
private static final Date DEFAULT_TWO_DIGIT_YEAR_START;
public static final TimeZone GMT = TimeZone.getTimeZone("GMT");
static {
final Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(GMT);
calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
DEFAULT_TWO_DIGIT_YEAR_START = calendar.getTime();
}
/**
* Parses a date value. The formats used for parsing the date value are retrieved from
* the default http params.
*
* @param dateValue the date value to parse
*
* @return the parsed date or null if input could not be parsed
*/
public static Date parseDate(final String dateValue) {
return parseDate(dateValue, null, null);
}
/**
* Parses the date value using the given date formats.
*
* @param dateValue the date value to parse
* @param dateFormats the date formats to use
*
* @return the parsed date or null if input could not be parsed
*/
public static Date parseDate(final String dateValue, final String[] dateFormats) {
return parseDate(dateValue, dateFormats, null);
}
/**
* Parses the date value using the given date formats.
*
* @param dateValue the date value to parse
* @param dateFormats the date formats to use
* @param startDate During parsing, two digit years will be placed in the range
* {@code startDate} to {@code startDate + 100 years}. This value may
* be {@code null}. When {@code null} is given as a parameter, year
* {@code 2000} will be used.
*
* @return the parsed date or null if input could not be parsed
*/
public static Date parseDate(
final String dateValue,
final String[] dateFormats,
final Date startDate) {
notNull(dateValue, "Date value");
final String[] localDateFormats = dateFormats != null ? dateFormats : DEFAULT_PATTERNS;
final Date localStartDate = startDate != null ? startDate : DEFAULT_TWO_DIGIT_YEAR_START;
String v = dateValue;
// trim single quotes around date if present
// see issue #5279
if (v.length() > 1 && v.startsWith("'") && v.endsWith("'")) {
v = v.substring (1, v.length() - 1);
}
for (final String dateFormat : localDateFormats) {
final SimpleDateFormat dateParser = DateFormatHolder.formatFor(dateFormat);
dateParser.set2DigitYearStart(localStartDate);
final ParsePosition pos = new ParsePosition(0);
final Date result = dateParser.parse(v, pos);
if (pos.getIndex() != 0) {
return result;
}
}
return null;
}
/**
* Formats the given date according to the RFC 1123 pattern.
*
* @param date The date to format.
* @return An RFC 1123 formatted date string.
*
* @see #PATTERN_RFC1123
*/
public static String formatDate(final Date date) {
return formatDate(date, PATTERN_RFC1123);
}
/**
* Formats the given date according to the specified pattern. The pattern
* must conform to that used by the {@link SimpleDateFormat simple date
* format} class.
*
* @param date The date to format.
* @param pattern The pattern to use for formatting the date.
* @return A formatted date string.
*
* @throws IllegalArgumentException If the given date pattern is invalid.
*
* @see SimpleDateFormat
*/
public static String formatDate(final Date date, final String pattern) {
notNull(date, "Date");
notNull(pattern, "Pattern");
final SimpleDateFormat formatter = DateFormatHolder.formatFor(pattern);
return formatter.format(date);
}
public static <T> T notNull(final T argument, final String name) {
if (argument == null) {
throw new IllegalArgumentException(name + " may not be null");
}
return argument;
}
/**
* Clears thread-local variable containing {@link java.text.DateFormat} cache.
*
* @since 4.3
*/
public static void clearThreadLocal() {
DateFormatHolder.clearThreadLocal();
}
/** This class should not be instantiated. */
private DateUtils() {
}
/**
* A factory for {@link SimpleDateFormat}s. The instances are stored in a
* threadlocal way because SimpleDateFormat is not threadsafe as noted in
* {@link SimpleDateFormat its javadoc}.
*
*/
final static class DateFormatHolder {
private static final ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>>
THREADLOCAL_FORMATS = new ThreadLocal<SoftReference<Map<String, SimpleDateFormat>>>() {
@Override
protected SoftReference<Map<String, SimpleDateFormat>> initialValue() {
return new SoftReference<Map<String, SimpleDateFormat>>(
new HashMap<String, SimpleDateFormat>());
}
};
/**
* creates a {@link SimpleDateFormat} for the requested format string.
*
* @param pattern
* a non-{@code null} format String according to
* {@link SimpleDateFormat}. The format is not checked against
* {@code null} since all paths go through
* {@link DateUtils}.
* @return the requested format. This simple dateformat should not be used
* to {@link SimpleDateFormat#applyPattern(String) apply} to a
* different pattern.
*/
public static SimpleDateFormat formatFor(final String pattern) {
final SoftReference<Map<String, SimpleDateFormat>> ref = THREADLOCAL_FORMATS.get();
Map<String, SimpleDateFormat> formats = ref.get();
if (formats == null) {
formats = new HashMap<String, SimpleDateFormat>();
THREADLOCAL_FORMATS.set(
new SoftReference<Map<String, SimpleDateFormat>>(formats));
}
SimpleDateFormat format = formats.get(pattern);
if (format == null) {
format = new SimpleDateFormat(pattern, Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
formats.put(pattern, format);
}
return format;
}
public static void clearThreadLocal() {
THREADLOCAL_FORMATS.remove();
}
}
}

View File

@ -208,7 +208,7 @@
<artifactItem>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli-jpaserver</artifactId>
<version>1.4-SNAPSHOT</version>
<version>1.5-SNAPSHOT</version>
<type>war</type>
<overWrite>true</overWrite>
<outputDirectory>target/classes</outputDirectory>

View File

@ -53,7 +53,7 @@ import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.GZipContentInterceptor;
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
import ca.uhn.fhir.util.ResourceReferenceInfo;
public class ExampleDataUploader extends BaseCommand {

View File

@ -24,6 +24,14 @@
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- conformance profile -->

View File

@ -0,0 +1,160 @@
package ca.uhn.fhir.jaxrs.client;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.util.List;
import java.util.Map;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.VersionUtil;
/**
* A Http Request based on JaxRs. This is an adapter around the class
* {@link javax.ws.rs.client.Client Client}
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class JaxRsHttpClient implements IHttpClient {
private Client myClient;
private List<Header> myHeaders;
private StringBuilder myUrl;
private Map<String, List<String>> myIfNoneExistParams;
private String myIfNoneExistString;
private RequestTypeEnum myRequestType;
public JaxRsHttpClient(Client theClient, StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams, String theIfNoneExistString,
RequestTypeEnum theRequestType, List<Header> theHeaders) {
this.myClient = theClient;
this.myUrl = theUrl;
this.myIfNoneExistParams = theIfNoneExistParams;
this.myIfNoneExistString = theIfNoneExistString;
this.myRequestType = theRequestType;
this.myHeaders = theHeaders;
}
@Override
public IHttpRequest createByteRequest(String theContents, String theContentType, EncodingEnum theEncoding) {
Entity<String> entity = Entity.entity(theContents, theContentType + Constants.HEADER_SUFFIX_CT_UTF_8);
JaxRsHttpRequest retVal = createHttpRequest(entity);
addHeadersToRequest(retVal, theEncoding);
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, theContentType + Constants.HEADER_SUFFIX_CT_UTF_8);
return retVal;
}
@Override
public IHttpRequest createParamRequest(Map<String, List<String>> theParams, EncodingEnum theEncoding) {
MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
for (Map.Entry<String, List<String>> nextParam : theParams.entrySet()) {
List<String> value = nextParam.getValue();
for (String s : value) {
map.add(nextParam.getKey(), s);
}
}
Entity<Form> entity = Entity.form(map);
JaxRsHttpRequest retVal = createHttpRequest(entity);
// addHeadersToRequest(retVal, encoding);
return retVal;
}
@Override
public IHttpRequest createBinaryRequest(IBaseBinary theBinary) {
Entity<String> entity = Entity.entity(theBinary.getContentAsBase64(), theBinary.getContentType());
JaxRsHttpRequest retVal = createHttpRequest(entity);
return retVal;
}
@Override
public IHttpRequest createGetRequest(EncodingEnum theEncoding) {
JaxRsHttpRequest result = createHttpRequest(null);
addHeadersToRequest(result, theEncoding);
return result;
}
public void addHeadersToRequest(JaxRsHttpRequest theHttpRequest, EncodingEnum theEncoding) {
if (myHeaders != null) {
for (Header next : myHeaders) {
theHttpRequest.addHeader(next.getName(), next.getValue());
}
}
theHttpRequest.addHeader("User-Agent", "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client)");
theHttpRequest.addHeader("Accept-Charset", "utf-8");
Builder request = theHttpRequest.getRequest();
request.acceptEncoding("gzip");
if (theEncoding == null) {
request.accept(Constants.HEADER_ACCEPT_VALUE_ALL);
} else if (theEncoding == EncodingEnum.JSON) {
request.accept(Constants.CT_FHIR_JSON);
} else if (theEncoding == EncodingEnum.XML) {
request.accept(Constants.CT_FHIR_XML);
}
}
private JaxRsHttpRequest createHttpRequest(Entity<?> entity) {
Builder request = myClient.target(myUrl.toString()).request();
JaxRsHttpRequest result = new JaxRsHttpRequest(request, myRequestType, entity);
addHeaderIfNoneExist(result);
return result;
}
private void addHeaderIfNoneExist(IHttpRequest result) {
if (myIfNoneExistParams != null) {
StringBuilder b = newHeaderBuilder(myUrl);
BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(myIfNoneExistParams, b, b.indexOf("?") == -1);
result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
if (myIfNoneExistString != null) {
StringBuilder b = newHeaderBuilder(myUrl);
b.append(b.indexOf("?") == -1 ? '?' : '&');
b.append(myIfNoneExistString.substring(myIfNoneExistString.indexOf('?') + 1));
result.addHeader(Constants.HEADER_IF_NONE_EXIST, b.toString());
}
}
private StringBuilder newHeaderBuilder(StringBuilder theUrlBase) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (theUrlBase.length() > 0 && theUrlBase.charAt(theUrlBase.length() - 1) == '/') {
b.deleteCharAt(b.length() - 1);
}
return b;
}
}

View File

@ -0,0 +1,106 @@
package ca.uhn.fhir.jaxrs.client;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
/**
* A Http Request based on JaxRs. This is an adapter around the class
* {@link javax.ws.rs.client.Invocation Invocation}
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class JaxRsHttpRequest implements IHttpRequest {
private Invocation.Builder myRequest;
private RequestTypeEnum myRequestType;
private Entity<?> myEntity;
private final Map<String, List<String>> myHeaders = new HashMap<String, List<String>>();
public JaxRsHttpRequest(Invocation.Builder theRequest, RequestTypeEnum theRequestType, Entity<?> theEntity) {
this.myRequest = theRequest;
this.myRequestType = theRequestType;
this.myEntity = theEntity;
}
@Override
public void addHeader(String theName, String theValue) {
if (!myHeaders.containsKey(theName)) {
myHeaders.put(theName, new LinkedList<String>());
}
myHeaders.get(theName).add(theValue);
getRequest().header(theName, theValue);
}
/**
* Get the Request
* @return the Request
*/
public Invocation.Builder getRequest() {
return myRequest;
}
/**
* Get the Request Type
* @return the request type
*/
public RequestTypeEnum getRequestType() {
return myRequestType == null ? RequestTypeEnum.GET : myRequestType;
}
/**
* Get the Entity
* @return the entity
*/
public Entity<?> getEntity() {
return myEntity;
}
@Override
public IHttpResponse execute() {
Invocation invocation = getRequest().build(getRequestType().name(), getEntity());
Response response = invocation.invoke();
return new JaxRsHttpResponse(response);
}
@Override
public Map<String, List<String>> getAllHeaders() {
return this.myHeaders;
}
@Override
public String getRequestBodyFromStream() {
// not supported
return null;
}
}

View File

@ -0,0 +1,111 @@
package ca.uhn.fhir.jaxrs.client;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
/**
* A Http Response based on JaxRs. This is an adapter around the class {@link javax.ws.rs.core.Response Response}
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class JaxRsHttpResponse implements IHttpResponse {
private final Response myResponse;
private boolean myBufferedEntity = false;
public JaxRsHttpResponse(Response theResponse) {
this.myResponse = theResponse;
}
@Override
public Response getResponse() {
return myResponse;
}
@Override
public int getStatus() {
return myResponse.getStatus();
}
@Override
public String getMimeType() {
MediaType mediaType = myResponse.getMediaType();
//Keep only type and subtype and do not include the parameters such as charset
return new MediaType(mediaType.getType(), mediaType.getSubtype()).toString();
}
@Override
public Map<String, List<String>> getAllHeaders() {
Map<String, List<String>> theHeaders = new ConcurrentHashMap<String, List<String>>();
for (Entry<String, List<String>> iterable_element : myResponse.getStringHeaders().entrySet()) {
theHeaders.put(iterable_element.getKey().toLowerCase(), iterable_element.getValue());
}
return theHeaders;
}
@Override
public String getStatusInfo() {
return myResponse.getStatusInfo().getReasonPhrase();
}
@Override
public Reader createReader() {
if (!myBufferedEntity && !myResponse.hasEntity()) {
return new StringReader("");
} else {
return new StringReader(myResponse.readEntity(String.class));
}
}
@Override
public InputStream readEntity() {
return myResponse.readEntity(java.io.InputStream.class);
}
@Override
public void bufferEntitity() {
if(!myBufferedEntity && myResponse.hasEntity()) {
myBufferedEntity = true;
myResponse.bufferEntity();
} else {
myResponse.bufferEntity();
}
}
@Override
public void close() {
// automatically done by jax-rs
}
}

View File

@ -0,0 +1,97 @@
package ca.uhn.fhir.jaxrs.client;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2016 University Health Network
* %%
* 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 ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.RestfulClientFactory;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import java.util.List;
import java.util.Map;
/**
* A Restful Client Factory, based on Jax Rs
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class JaxRsRestfulClientFactory extends RestfulClientFactory {
private Client myNativeClient;
/**
* Constructor
*/
public JaxRsRestfulClientFactory() {
}
/**
* Constructor
* @param theFhirContext The context
*/
public JaxRsRestfulClientFactory(FhirContext theFhirContext) {
super(theFhirContext);
}
public synchronized Client getNativeClientClient() {
if (myNativeClient == null) {
ClientBuilder builder = ClientBuilder.newBuilder();
myNativeClient = builder.build();
}
return myNativeClient;
}
@Override
public IHttpClient getHttpClient(StringBuilder url, Map<String, List<String>> theIfNoneExistParams,
String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
return new JaxRsHttpClient(getNativeClientClient(), url, theIfNoneExistParams, theIfNoneExistString, theRequestType,
theHeaders);
}
@Override
public void setProxy(String theHost, Integer thePort) {
throw new UnsupportedOperationException("Proxies are not supported yet");
}
/**
* Only accept clients of type javax.ws.rs.client.Client
* @param theHttpClient
*/
@Override
public synchronized void setHttpClient(Object theHttpClient) {
this.myNativeClient = (Client) theHttpClient;
}
@Override
protected JaxRsHttpClient getHttpClient(String theServerBase) {
return new JaxRsHttpClient(getNativeClientClient(), new StringBuilder(theServerBase), null, null, null, null);
}
@Override
protected void resetHttpClient() {
this.myNativeClient = null;
}
}

View File

@ -23,10 +23,12 @@ package ca.uhn.fhir.jaxrs.server.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.apache.commons.lang3.StringUtils;
@ -149,8 +151,8 @@ public class JaxRsRequest extends RequestDetails {
}
}
private String theResourceString;
private HttpHeaders headers;
private String myResourceString;
private HttpHeaders myHeaders;
private AbstractJaxRsProvider myServer;
/**
@ -162,8 +164,8 @@ public class JaxRsRequest extends RequestDetails {
*/
public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType,
RestOperationTypeEnum restOperation) {
this.headers = server.getHeaders();
this.theResourceString = resourceString;
this.myHeaders = server.getHeaders();
this.myResourceString = resourceString;
this.setRestOperationType(restOperation);
setServer(server);
setFhirServerBase(server.getBaseForServer());
@ -192,7 +194,7 @@ public class JaxRsRequest extends RequestDetails {
@Override
public List<String> getHeaders(String name) {
List<String> requestHeader = headers.getRequestHeader(name);
List<String> requestHeader = myHeaders.getRequestHeader(name);
return requestHeader == null ? Collections.<String> emptyList() : requestHeader;
}
@ -203,7 +205,7 @@ public class JaxRsRequest extends RequestDetails {
@Override
protected byte[] getByteStreamRequestContents() {
return StringUtils.defaultIfEmpty(theResourceString, "")
return StringUtils.defaultIfEmpty(myResourceString, "")
.getBytes(ResourceParameter.determineRequestCharset(this));
}
@ -226,4 +228,18 @@ public class JaxRsRequest extends RequestDetails {
// not yet implemented
throw new UnsupportedOperationException();
}
@Override
public Charset getCharset() {
String charset = null;
if(myHeaders.getMediaType() != null && myHeaders.getMediaType().getParameters() != null) {
charset = myHeaders.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER);
}
if(charset != null) {
return Charset.forName(charset);
} else {
return null;
}
}
}

View File

@ -38,6 +38,7 @@ import ca.uhn.fhir.jaxrs.server.test.RandomServerPortProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsConformanceRestProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPageProvider;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProvider;
import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
@ -104,6 +105,7 @@ public class AbstractJaxRsResourceProviderTest {
jettyServer.start();
ourCtx.setRestfulClientFactory(new JaxRsRestfulClientFactory(ourCtx));
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
serverBase = "http://localhost:" + ourPort + "/";

View File

@ -1,7 +1,6 @@
package ca.uhn.fhir.jaxrs.server.example;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;

View File

@ -18,6 +18,7 @@ import org.junit.Ignore;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.client.JaxRsRestfulClientFactory;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
@ -66,6 +67,7 @@ public class JaxRsPatientProviderTest {
//@formatter:on
jettyServer.start();
ourCtx.setRestfulClientFactory(new JaxRsRestfulClientFactory(ourCtx));
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");

View File

@ -24,12 +24,14 @@ import java.util.Set;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
@ -66,6 +68,7 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.apache.ApacheHttpRequest;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.client.interceptor.CapturingInterceptor;
import ca.uhn.fhir.rest.param.CompositeParam;
@ -150,7 +153,7 @@ public class ClientDstu1Test {
MethodOutcome response = client.createPatient(patient);
assertEquals(interceptor.getLastRequest().getURI().toASCIIString(), "http://foo/Patient");
assertEquals(((ApacheHttpRequest) interceptor.getLastRequest()).getApacheRequest().getURI().toASCIIString(), "http://foo/Patient");
assertEquals(HttpPost.class, capt.getValue().getClass());
HttpPost post = (HttpPost) capt.getValue();

View File

@ -82,7 +82,7 @@ public class LoggingInterceptorTest {
public boolean matches(final Object argument) {
String formattedMessage = ((LoggingEvent) argument).getFormattedMessage();
System.out.println("Verifying: " + formattedMessage);
return formattedMessage.replace("; ", ";").replace("UTF", "utf").contains("Content-Type: application/xml+fhir;charset=utf-8");
return formattedMessage.replace("; ", ";").toLowerCase().contains("Content-Type: application/xml+fhir;charset=utf-8".toLowerCase());
}
}));
}

View File

@ -20,6 +20,7 @@ import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.apache.GZipContentInterceptor;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;

View File

@ -52,6 +52,8 @@ import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.to.model.HomeRequest;
@ -290,7 +292,7 @@ public class BaseController {
returnsResource = ResultType.NONE;
ourLog.warn("Failed to invoke server", e);
if (theClient.getLastResponse() == null) {
if (e != null) {
theModel.put("errorMsg", "Error: " + e.getMessage());
}
@ -653,17 +655,17 @@ public class BaseController {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
assert myLastRequest == null;
myLastRequest = theRequest;
myLastRequest = (HttpRequestBase) theRequest;
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
public void interceptResponse(IHttpResponse theResponse) throws IOException {
assert myLastResponse == null;
myLastResponse = theResponse;
myLastResponse = (HttpResponse) theResponse;
HttpEntity respEntity = theResponse.getEntity();
HttpEntity respEntity = myLastResponse.getEntity();
if (respEntity != null) {
final byte[] bytes;
try {
@ -673,7 +675,7 @@ public class BaseController {
}
myResponseBody = new String(bytes, "UTF-8");
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
myLastResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
}
}

View File

@ -15,6 +15,8 @@ import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy;
import ca.uhn.fhir.to.Controller;
@ -163,12 +165,12 @@ public class HomeRequest {
retVal.registerInterceptor(new IClientInterceptor() {
@Override
public void interceptResponse(HttpResponse theRequest) {
public void interceptResponse(IHttpResponse theRequest) {
// nothing
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequest theRequest) {
if (isNotBlank(remoteAddr)) {
theRequest.addHeader("x-forwarded-for", remoteAddr);
}