create a jax-rs client

This commit is contained in:
petervanhoute 2016-01-07 16:35:57 +01:00
parent a54cb06c03
commit 1271c36d1b
50 changed files with 1924 additions and 604 deletions

View File

@ -0,0 +1,147 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>1.4-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-base</artifactId>
<packaging>jar</packaging>
<url>http://jamesagnew.github.io/hapi-fhir/</url>
<name>HAPI FHIR - Core Client</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hapi-fhir-base-core</artifactId>
<version>${project.version}</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
</dependency>
<!-- XML -->
<dependency>
<groupId>org.codehaus.woodstox</groupId>
<artifactId>woodstox-core-asl</artifactId>
</dependency>
<!-- Only required for narrative generator support -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<optional>true</optional>
</dependency>
<!-- Only required for CORS support -->
<dependency>
<groupId>org.ebaysf.web</groupId>
<artifactId>cors-filter</artifactId>
<optional>true</optional>
<!-- <exclusions> <exclusion> <artifactId>servlet-api</artifactId> <groupId>javax.servlet</groupId>
</exclusion> </exclusions> -->
</dependency>
<!-- Only required for Schematron Validator Support -->
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-schematron</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.phloc</groupId>
<artifactId>phloc-commons</artifactId>
<optional>true</optional>
</dependency>
<!-- <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId>
<version>2.11.0</version> <optional>true</optional> </dependency> -->
<!-- General -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<optional>true</optional>
</dependency>
<!-- Client -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
</dependency>
<!--
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
-->
<!-- Server -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>

View File

@ -0,0 +1,205 @@
package ca.uhn.fhir.rest.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.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.Header;
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.IHttpRequestBase;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.api.IHttpClient;
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 {
HttpClient myClient;
/**
* Get the myClient
* @return the myClient
*/
public HttpClient getMyClient() {
return myClient;
}
private List<Header> myHeaders;
private StringBuilder myUrl;
private Map<String, List<String>> myIfNoneExistParams;
private String myIfNoneExistString;
private RequestTypeEnum myRequestType;
public ApacheHttpClient(HttpClient myClient, StringBuilder url, Map<String, List<String>> myIfNoneExistParams, String myIfNoneExistString, EncodingEnum theEncoding, RequestTypeEnum theRequestType, List<Header> theHeaders) {
this.myHeaders = theHeaders;
this.myClient = myClient;
this.myUrl = url;
this.myIfNoneExistParams = myIfNoneExistParams;
this.myIfNoneExistString = myIfNoneExistString;
this.myRequestType = theRequestType;
}
@Override
public IHttpRequestBase createByteRequest(String contents, String contentType, EncodingEnum encoding) {
/*
* 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(contents.getBytes(Constants.CHARSET_UTF8));
HttpRequestBase retVal = createRequest(myUrl, entity);
addHeadersToRequest(retVal, encoding);
addMatchHeaders(retVal, myUrl);
retVal.addHeader(Constants.HEADER_CONTENT_TYPE, contentType + Constants.HEADER_SUFFIX_CT_UTF_8);
return new ApacheHttpRequestBase(myClient, retVal);
}
@Override
public IHttpRequestBase createParamRequest(Map<String, List<String>> myParams, EncodingEnum encoding) {
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 {
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(parameters, "UTF-8");
HttpRequestBase retVal = createRequest(myUrl, entity);
addMatchHeaders(retVal, myUrl);
return new ApacheHttpRequestBase(myClient, retVal);
} catch (UnsupportedEncodingException e) {
throw new InternalErrorException("Server does not support UTF-8 (should not happen)", e);
}
}
@Override
public IHttpRequestBase createBinaryRequest(IBaseBinary binary) {
/*
* 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());
entity.setContentType(binary.getContentType());
HttpRequestBase retVal = createRequest(myUrl, entity);
addMatchHeaders(retVal, myUrl);
return new ApacheHttpRequestBase(myClient, retVal);
}
@Override
public IHttpRequestBase createGetRequest(EncodingEnum theEncoding) {
HttpRequestBase retVal = createRequest(myUrl, null);
addHeadersToRequest(retVal, theEncoding);
addMatchHeaders(retVal, myUrl);
return new ApacheHttpRequestBase(myClient, retVal);
}
public void addHeadersToRequest(HttpRequestBase 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 void addMatchHeaders(HttpRequestBase theHttpRequest, StringBuilder theUrlBase) {
if (myIfNoneExistParams != null) {
StringBuilder b = newHeaderBuilder(theUrlBase);
BaseHttpClientInvocation.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());
}
}
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 createRequest(StringBuilder url2, HttpEntity entity) {
String uri = url2.toString();
switch(myRequestType) {
case DELETE :
return new HttpDelete(uri);
case OPTIONS :
return new HttpOptions(uri);
case POST :
HttpPost httpPost = new HttpPost(uri);
httpPost.setEntity(entity);
return httpPost;
case PUT :
HttpPut httpPut = new HttpPut(uri);
httpPut.setEntity(entity);
return httpPut;
case GET :
default:
return new HttpGet(uri);
}
}
}

View File

@ -0,0 +1,84 @@
package ca.uhn.fhir.rest.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.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.http.Header;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase;
import ca.uhn.fhir.rest.api.IHttpRequestBase;
/**
* 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 ApacheHttpRequestBase implements IHttpRequestBase {
private HttpRequestBase myRequest;
private HttpClient myClient;
public ApacheHttpRequestBase(HttpClient theClient, HttpRequestBase theApacheRequest) {
this.myClient = theClient;
this.myRequest = theApacheRequest;
}
@Override
public void addHeader(String theName, String theValue) {
getApacheRequest().addHeader(theName, theValue);
}
/**
* Get the myApacheRequest
* @return the myApacheRequest
*/
public HttpRequestBase getApacheRequest() {
return myRequest;
}
@Override
public IHttpResponse execute() throws IOException {
return new ApacheHttpResponse(myClient.execute(getApacheRequest()));
}
@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();
}
}

View File

@ -0,0 +1,159 @@
package ca.uhn.fhir.rest.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.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.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 UnsupportedOperationException, 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,147 @@
package ca.uhn.fhir.rest.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.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
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.api.IHttpClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.EncodingEnum;
/**
* A Restful Factory to create clients, requests and responses based on Apache.
* @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 theFhirContext
* The context
*/
public ApacheRestfulClientFactory(FhirContext theFhirContext) {
super(theFhirContext);
}
public synchronized HttpClient getNativeHttpClient() {
if (myHttpClient == null) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
//@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;
}
@Override
public ClientInvocationHandler newInvocationHandler(IHttpClient theClient, String myUrlBase, Map<Method, Object> myMethodToReturnValue, Map<Method, BaseMethodBinding<?>> myBindings, Map<Method, ClientInvocationHandlerFactory.ILambda> myMethodToLambda) {
ApacheHttpClient apacheHttpClient = (ApacheHttpClient) theClient;
return new ClientInvocationHandler(apacheHttpClient, myContext, myUrlBase.toString(), myMethodToReturnValue, myBindings, myMethodToLambda, this);
}
@Override
public IHttpClient getHttpClient(StringBuilder url, Map<String, List<String>> myIfNoneExistParams, String myIfNoneExistString,
EncodingEnum theEncoding, RequestTypeEnum theRequestType, List<Header> myHeaders) {
return new ApacheHttpClient(getNativeHttpClient(), url, myIfNoneExistParams, myIfNoneExistString, theEncoding, theRequestType, myHeaders);
}
@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, null);
}
@Override
protected void resetHttpClient() {
this.myHttpClient = null;
}
}

View File

@ -26,11 +26,13 @@ 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.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.ApacheHttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.IHttpResponse;
import ca.uhn.fhir.rest.server.Constants;
/**
@ -42,7 +44,9 @@ 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(IHttpRequestBase theRequestInterface) {
HttpRequestBase theRequest = ((ApacheHttpRequestBase) theRequestInterface).getApacheRequest();
if (theRequest instanceof HttpEntityEnclosingRequest) {
Header[] encodingHeaders = theRequest.getHeaders(Constants.HEADER_CONTENT_ENCODING);
if (encodingHeaders == null || encodingHeaders.length == 0) {
@ -69,7 +73,7 @@ public class GZipContentInterceptor implements IClientInterceptor {
}
@Override
public void interceptResponse(HttpResponse theResponse) throws IOException {
public void interceptResponse(IHttpResponse theResponse) throws IOException {
// nothing
}

View File

@ -9,7 +9,7 @@
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-base</artifactId>
<artifactId>hapi-fhir-base-core</artifactId>
<packaging>jar</packaging>
<url>http://jamesagnew.github.io/hapi-fhir/</url>

View File

@ -294,9 +294,26 @@ 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. The default method will create this based upon the apache restful client factory
* @return the factory used to create the restful clients
*/
public IRestfulClientFactory getRestfulClientFactory() {
if (myRestfulClientFactory == null) {
myRestfulClientFactory = new RestfulClientFactory(this);
try {
myRestfulClientFactory = (IRestfulClientFactory) Class.forName("ca.uhn.fhir.rest.client.ApacheRestfulClientFactory").getConstructor(getClass()).newInstance(this);
}
catch (Exception e) {
throw new RuntimeException("Could not create the RestfulClientFactory", e);
}
}
return myRestfulClientFactory;
}

View File

@ -0,0 +1,53 @@
package ca.uhn.fhir.rest.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;
import ca.uhn.fhir.rest.client.IHttpResponse;
/**
* Http Base Request. Allows addition of headers and execution of the request.
*/
public interface IHttpRequestBase {
/**
* 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();
}

View File

@ -2,6 +2,10 @@ package ca.uhn.fhir.rest.client;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
/*
* #%L
* HAPI FHIR - Core Library
@ -22,13 +26,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* #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.Collections;
import java.util.HashMap;
@ -40,14 +39,8 @@ 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;
@ -61,7 +54,9 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.IHttpRequestBase;
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.client.exceptions.FhirClientConnectionException;
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
@ -79,19 +74,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 +125,7 @@ public abstract class BaseClient implements IRestfulClient {
* {@inheritDoc}
*/
@Override
public HttpClient getHttpClient() {
public IHttpClient getHttpClient() {
return myClient;
}
@ -141,7 +136,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,35 +188,35 @@ 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;
IHttpRequestBase 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);
@ -235,50 +230,31 @@ public abstract class BaseClient implements IRestfulClient {
}
}
}
for (IClientInterceptor nextInterceptor : myInterceptors) {
nextInterceptor.interceptRequest(httpRequest);
nextInterceptor.interceptRequest(httpRequest);
}
response = httpRequest.execute();
for (IClientInterceptor nextInterceptor : myInterceptors) {
nextInterceptor.interceptResponse(response);
}
response = myClient.execute(httpRequest);
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 +262,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 +285,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 +297,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 +306,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 +315,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 +331,23 @@ 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 +368,14 @@ 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 {
@ -406,7 +384,7 @@ public abstract class BaseClient implements IRestfulClient {
} else {
ourLog.trace("FHIR response:\n{}\n{}", response, responseString);
}
}
}
@Override
public void registerInterceptor(IClientInterceptor theInterceptor) {
@ -444,7 +422,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 +455,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

@ -28,16 +28,23 @@ 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.IHttpRequestBase;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.util.VersionUtil;
public abstract class BaseHttpClientInvocation {
private List<Header> myHeaders;
protected List<Header> myHeaders;
private final FhirContext myContext;
public BaseHttpClientInvocation(FhirContext myContext) {
this.myContext = myContext;
}
public void addHeader(String theName, String theValue) {
if (myHeaders == null) {
@ -57,15 +64,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 IHttpRequestBase 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,24 +93,25 @@ public abstract class BaseHttpClientInvocation {
}
}
public void addHeadersToRequest(HttpRequestBase theHttpRequest, EncodingEnum theEncoding) {
if (myHeaders != null) {
for (Header next : myHeaders) {
theHttpRequest.addHeader(next);
}
}
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);
}
/**
* Get thre restful client factory
* @return
*/
public IRestfulClientFactory getRestfulClientFactory() {
return myContext.getRestfulClientFactory();
}
public IHttpRequestBase createHttpRequest(String theUrl, EncodingEnum theEncoding, RequestTypeEnum theRequestType) {
IHttpClient httpClient = getRestfulClientFactory().getHttpClient(new StringBuilder(theUrl), null, null, theEncoding, theRequestType, myHeaders);
return httpClient.createGetRequest(theEncoding);
}
/**
* Get the myContext
* @return the myContext
*/
public FhirContext getContext() {
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,29 +24,27 @@ 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 FhirContext myContext;
private final IHttpClient myClient;
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;
try {
myMethodToReturnValue.put(theClientType.getMethod("getFhirContext"), theContext);
@ -72,10 +70,10 @@ class ClientInvocationHandlerFactory {
}
ClientInvocationHandler newInvocationHandler(RestfulClientFactory theRestfulClientFactory) {
return new ClientInvocationHandler(myClient, myContext, myUrlBase, myMethodToReturnValue, myBindings, myMethodToLambda, theRestfulClientFactory);
return theRestfulClientFactory.newInvocationHandler(myClient, myUrlBase, myMethodToReturnValue, myBindings, myMethodToLambda);
}
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;
@ -69,9 +67,11 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.IHttpRequestBase;
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.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 IHttpRequestBase 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 IHttpRequestBase 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(IHttpRequestBase 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,18 @@ 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.api.IHttpRequestBase;
public interface IClientInterceptor {
/**
* Fired by the client just before invoking the HTTP client request
*/
void interceptRequest(HttpRequestBase theRequest);
void interceptRequest(IHttpRequestBase 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

@ -0,0 +1,109 @@
package ca.uhn.fhir.rest.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.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import java.util.Map;
public interface IHttpResponse {
/**
* @return the response status number
*/
public int getStatus();
/**
* @return the raw response underlying response object
*/
Object getResponse();
/**
* @return the response mime type
*/
public String getMimeType();
/**
* @return all the headers
*/
public Map<String, List<String>> getAllHeaders();
/**
* @return the status info
*/
public String getStatusInfo();
/**
* @return
* @throws IOException
*/
public Reader createReader() throws IOException;
/**
* Read the entity as inputstream
* @return the inputstream
* @throws IOException
*/
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 and automatically
* closed as part of the operation and the method returns {@code true}.
* </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
* {@code false}.
* </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. Also, the result returned by the {@code bufferEntity()}
* method is consistent across all invocations of the method on the same
* {@code Response} instance.
* </p>
* <p>
* Buffering the message entity data allows for multiple invocations of
* {@code readEntity(...)} methods on the response instance. Note however, that
* once the response instance itself is {@link #close() closed}, the implementations
* are expected to release the buffered message entity data too. Therefore any subsequent
* attempts to read a message entity stream on such closed response will result in an
* {@link IllegalStateException} being thrown.
* </p>
* @throws IOException
* @throws IllegalStateException in case the response has been {@link #close() closed}.
*/
void bufferEntitity() throws IOException;
}

View File

@ -1,5 +1,11 @@
package ca.uhn.fhir.rest.client;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import org.apache.http.Header;
/*
* #%L
* HAPI FHIR - Core Library
@ -23,7 +29,11 @@ package ca.uhn.fhir.rest.client;
import org.apache.http.client.HttpClient;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
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;
public interface IRestfulClientFactory {
@ -70,7 +80,7 @@ public interface IRestfulClientFactory {
*
* @see #setHttpClient(HttpClient)
*/
HttpClient getHttpClient();
IHttpClient getHttpClient(StringBuilder url, Map<String, List<String>> myIfNoneExistParams, String myIfNoneExistString, EncodingEnum theEncoding, RequestTypeEnum theRequestType, List<Header> myHeaders);
/**
* @deprecated Use {@link #getServerValidationMode()} instead
@ -120,6 +130,19 @@ public interface IRestfulClientFactory {
* @return A newly created client
*/
IGenericClient newGenericClient(String theServerBase);
/**
* Instantiates a new client invocation handler
* @param theClient
* the client which will invoke the call
* @param myUrlBase
* the url base
* @param myMethodToReturnValue
* @param myBindings
* @param myMethodToLambda
* @return a newly created client invocation handler
*/
ClientInvocationHandler newInvocationHandler(IHttpClient theClient, String myUrlBase, Map<Method, Object> myMethodToReturnValue, Map<Method, BaseMethodBinding<?>> myBindings, Map<Method, ClientInvocationHandlerFactory.ILambda> myMethodToLambda);
/**
* Sets the connection request timeout, in milliseconds. This is the amount of time that the HTTPClient connection
@ -146,7 +169,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
@ -193,4 +216,8 @@ public interface IRestfulClientFactory {
*/
void setSocketTimeout(int theSocketTimeout);
void validateServerBase(String theServerBase, IHttpClient theHttpClient, BaseClient theClient);
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>());
protected int myConnectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
protected int myConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
protected FhirContext myContext;
protected Map<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory> myInvocationHandlers = new HashMap<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory>();
protected ServerValidationModeEnum myServerValidationMode = DEFAULT_SERVER_VALIDATION_MODE;
protected int mySocketTimeout = DEFAULT_SOCKET_TIMEOUT;
protected Set<String> myValidatedServerBaseUrls = Collections.synchronizedSet(new HashSet<String>());
protected String myProxyUsername;
protected String myProxyPassword;
/**
* Constructor
@ -95,44 +84,6 @@ public class RestfulClientFactory implements IRestfulClientFactory {
return myConnectTimeout;
}
@Override
public synchronized HttpClient getHttpClient() {
if (myHttpClient == null) {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
//@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;
}
private String myProxyUsername;
private String myProxyPassword;
@Override
public void setProxyCredentials(String theUsername, String thePassword) {
myProxyUsername=theUsername;
@ -175,7 +126,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);
@ -191,14 +142,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) {
@ -224,13 +173,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();
}
/**
@ -243,27 +192,6 @@ 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>
*/
@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;
}
}
@Override
public void setServerValidationMode(ServerValidationModeEnum theServerValidationMode) {
Validate.notNull(theServerValidationMode, "theServerValidationMode may not be null");
@ -273,11 +201,11 @@ public class RestfulClientFactory implements IRestfulClientFactory {
@Override
public synchronized void setSocketTimeout(int theSocketTimeout) {
mySocketTimeout = theSocketTimeout;
myHttpClient = null;
resetHttpClient();
}
@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());
@ -337,5 +265,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,67 @@
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.api.IHttpRequestBase;
import ca.uhn.fhir.rest.server.EncodingEnum;
/**
* A HTTP Client
*/
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
*/
IHttpRequestBase 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
*/
IHttpRequestBase createParamRequest(Map<String, List<String>> theParams, EncodingEnum theEncoding);
/**
* Create a binary request
* @param theBinary the binary
* @return the http request to be executed
*/
IHttpRequestBase createBinaryRequest(IBaseBinary theBinary);
/**
* Create a normal http get request
* @param theEncoding the request encoding
* @return the http request to be executed
*/
IHttpRequestBase createGetRequest(EncodingEnum theEncoding);
}

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.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.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(IHttpRequestBase 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.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.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(IHttpRequestBase 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.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.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 IHttpRequestBase 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 IHttpRequestBase getLastRequest() {
return myLastRequest;
}
public HttpResponse getLastResponse() {
public IHttpResponse getLastResponse() {
return myLastResponse;
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequestBase 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.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.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(IHttpRequestBase theRequest) {
theRequest.addHeader(Constants.HEADER_COOKIE, sessionCookie); //$NON-NLS-1$
}
@Override
public void interceptResponse(HttpResponse theResponse) {
public void interceptResponse(IHttpResponse theResponse) {
// nothing
}
}

View File

@ -24,6 +24,10 @@ 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 java.util.Map.Entry;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
@ -35,7 +39,9 @@ import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.HttpEntityWrapper;
import org.slf4j.Logger;
import ca.uhn.fhir.rest.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.IHttpResponse;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class LoggingInterceptor implements IClientInterceptor {
@ -74,20 +80,13 @@ public class LoggingInterceptor implements IClientInterceptor {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequestBase 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 = getHeaderString(theRequest.getAllHeaders());
myLog.info("Client request headers:\n{}", b.toString());
}
@ -110,23 +109,14 @@ public class LoggingInterceptor implements IClientInterceptor {
}
@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 = getHeaderString(theResponse.getAllHeaders());
// if (theResponse.getEntity() != null && theResponse.getEntity().getContentEncoding() != null) {
// Header next = theResponse.getEntity().getContentEncoding();
// b.append(next.getName() + ": " + next.getValue());
@ -143,23 +133,41 @@ public class LoggingInterceptor implements IClientInterceptor {
}
if (myLogResponseBody) {
HttpEntity respEntity = theResponse.getEntity();
theResponse.bufferEntitity();
InputStream respEntity = theResponse.readEntity();
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));
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)");
}
}
}
private StringBuilder getHeaderString(Map<String, List<String>> allHeaders) {
StringBuilder b = new StringBuilder();
if (allHeaders != null && !allHeaders.isEmpty()) {
Iterator<String> nameEntries = allHeaders.keySet().iterator();
while(nameEntries.hasNext()) {
String key = nameEntries.next();
Iterator<String> values = allHeaders.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 +222,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.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.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(IHttpRequestBase 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

@ -20,19 +20,11 @@ package ca.uhn.fhir.rest.method;
* #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.apache.http.Header;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -44,10 +36,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.api.IHttpRequestBase;
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
@ -59,7 +52,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;
@ -70,7 +62,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;
@ -81,7 +73,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, IBaseResource theResource, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
super(theContext);
myResource = theResource;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
@ -94,8 +86,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;
@ -106,7 +97,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, List<? extends IBaseResource> theResources, BundleTypeEnum theBundleType) {
myContext = theContext;
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = null;
@ -117,7 +108,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
@ -130,7 +121,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlPath) {
myContext = theContext;
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = theUrlPath;
@ -142,7 +133,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
super(theContext);
myResource = null;
myTagList = null;
myUrlPath = StringUtils.join(theUrlPath, '/');
@ -155,13 +146,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;
@ -171,153 +161,76 @@ 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 {
StringBuilder url = new StringBuilder();
if (myUrlPath == null) {
url.append(theUrlBase);
public IHttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) throws DataFormatException {
IHttpClient httpClient = getHttpClient(theUrlBase, theEncoding, theExtraParams, myHeaders);
if (myResource != null && IBaseBinary.class.isAssignableFrom(myResource.getClass())) {
return httpClient.createBinaryRequest((IBaseBinary) myResource);
} else {
if (!myUrlPath.contains("://")) {
url.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
url.append('/');
}
}
url.append(myUrlPath);
}
EncodingEnum encoding = theEncoding;
if (myContents != null) {
encoding = MethodUtil.detectEncoding(myContents);
}
appendExtraParamsWithQuestionMark(theExtraParams, url, url.indexOf("?") == -1);
if (myResource != null && IBaseBinary.class.isAssignableFrom(myResource.getClass())) {
IBaseBinary binary = (IBaseBinary) myResource;
/*
* 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());
entity.setContentType(binary.getContentType());
HttpRequestBase retVal = createRequest(url, entity);
addMatchHeaders(retVal, url);
return retVal;
}
IParser parser;
String contentType;
EncodingEnum encoding = null;
encoding = theEncoding;
if (myContents != null) {
encoding = MethodUtil.detectEncoding(myContents);
}
if (encoding == EncodingEnum.JSON) {
parser = myContext.newJsonParser();
} else {
encoding = EncodingEnum.XML;
parser = myContext.newXmlParser();
}
if (thePrettyPrint != null) {
parser.setPrettyPrint(thePrettyPrint);
}
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();
}
} else {
contents = parser.encodeResourceToString(myResource);
contentType = encoding.getResourceContentType();
}
/*
* 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));
}
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;
if (encoding == null) {
encoding = EncodingEnum.XML;
}
if (myParams != null) {
return httpClient.createParamRequest(myParams, encoding);
} else {
String contents = parseContents(thePrettyPrint, encoding);
String contentType = getContentType(encoding);
return httpClient.createByteRequest(contents, contentType, encoding);
}
}
}
protected abstract HttpRequestBase createRequest(StringBuilder theUrl, AbstractHttpEntity theEntity);
private String getContentType(EncodingEnum encoding) {
if(myBundle != null ||
(getContext().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 = getContext().newJsonParser();
} else {
parser = getContext().newXmlParser();
}
if (thePrettyPrint != null) {
parser.setPrettyPrint(thePrettyPrint);
}
parser.setOmitResourceId(myOmitResourceId);
if (myTagList != null) {
return parser.encodeTagListToString(myTagList);
} else if (myBundle != null) {
return parser.encodeBundleToString(myBundle);
} else if (myResources != null) {
IVersionSpecificBundleFactory bundleFactory = getContext().newBundleFactory();
bundleFactory.initializeBundleFromResourceList("", myResources, "", "", myResources.size(), myBundleType);
Bundle bundle = bundleFactory.getDstu1Bundle();
if (bundle != null) {
return parser.encodeBundleToString(bundle);
} else {
IBaseResource bundleRes = bundleFactory.getResourceBundle();
return parser.encodeResourceToString(bundleRes);
}
} else if (myContents != null) {
return myContents;
} else {
return parser.encodeResourceToString(myResource);
}
}
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;
}
@ -329,5 +242,27 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
public void setOmitResourceId(boolean theOmitResourceId) {
myOmitResourceId = theOmitResourceId;
}
public IHttpClient getHttpClient(String theUrlBase, EncodingEnum theEncoding, Map<String, List<String>> theExtraParams, List<Header> myHeaders) {
//TODO move this to the factory
StringBuilder url = new StringBuilder();
if (myUrlPath == null) {
url.append(theUrlBase);
} else {
if (!myUrlPath.contains("://")) {
url.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
url.append('/');
}
}
url.append(myUrlPath);
}
appendExtraParamsWithQuestionMark(theExtraParams, url, url.indexOf("?") == -1);
return getRestfulClientFactory().getHttpClient(url, myIfNoneExistParams, myIfNoneExistString, theEncoding, getRequestType(), myHeaders);
}
protected abstract RequestTypeEnum getRequestType();
}

View File

@ -59,7 +59,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

@ -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

@ -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 theFhirContext, 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(theFhirContext, b.toString());
return retVal;
}

View File

@ -23,10 +23,11 @@ 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.IHttpRequestBase;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -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 IHttpRequestBase 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,9 +28,11 @@ 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.IHttpRequestBase;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
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 IHttpRequestBase 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

@ -26,6 +26,9 @@ 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.IHttpRequestBase;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.EncodingEnum;
@ -33,15 +36,14 @@ 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 IHttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
return createHttpRequest(myUrl, theEncoding, RequestTypeEnum.GET);
}
}

View File

@ -146,8 +146,8 @@ public class MethodUtil {
return value;
}
public static HttpGetClientInvocation createConformanceInvocation() {
return new HttpGetClientInvocation("metadata");
public static HttpGetClientInvocation createConformanceInvocation(FhirContext theFhirContext) {
return new HttpGetClientInvocation(theFhirContext, "metadata");
}
public static HttpPostClientInvocation createCreateInvocation(IBaseResource theResource, FhirContext theContext) {

View File

@ -334,7 +334,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

@ -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

@ -0,0 +1,156 @@
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.apache.http.Header;
import org.hl7.fhir.instance.model.api.IBaseBinary;
import ca.uhn.fhir.rest.api.IHttpRequestBase;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.client.api.IHttpClient;
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 myClient, StringBuilder url, Map<String, List<String>> myIfNoneExistParams, String myIfNoneExistString, EncodingEnum theEncoding, RequestTypeEnum theRequestType, List<Header> theHeaders) {
this.myHeaders = theHeaders;
this.myClient = myClient;
this.myUrl = url;
this.myIfNoneExistParams = myIfNoneExistParams;
this.myIfNoneExistString = myIfNoneExistString;
this.myRequestType = theRequestType;
}
@Override
public IHttpRequestBase createByteRequest(String contents, String contentType, EncodingEnum encoding) {
Builder retVal = createRequest();
addHeadersToRequest(retVal, encoding);
addMatchHeaders(retVal, myUrl);
Entity<String> entity = Entity.entity(contents, contentType + Constants.HEADER_SUFFIX_CT_UTF_8);
retVal.header(Constants.HEADER_CONTENT_TYPE, contentType + Constants.HEADER_SUFFIX_CT_UTF_8);
return new JaxRsHttpRequestBase(retVal, myRequestType, entity);
}
@Override
public IHttpRequestBase createParamRequest(Map<String, List<String>> myParams, EncodingEnum encoding) {
MultivaluedMap<String, String> map = new MultivaluedHashMap<String, String>();
for (Map.Entry<String, List<String>> nextParam : myParams.entrySet()) {
List<String> value = nextParam.getValue();
for (String s : value) {
map.add(nextParam.getKey(), s);
}
}
Builder retVal = createRequest();
//addHeadersToRequest(retVal, encoding);
addMatchHeaders(retVal, myUrl);
Entity<Form> entity = Entity.form(map);
return new JaxRsHttpRequestBase(retVal, myRequestType, entity);
}
@Override
public IHttpRequestBase createBinaryRequest(IBaseBinary binary) {
Entity<String> entity = Entity.entity(binary.getContentAsBase64(), binary.getContentType());
Builder retVal = createRequest();
addMatchHeaders(retVal, myUrl);
return new JaxRsHttpRequestBase(retVal, myRequestType, entity);
}
@Override
public IHttpRequestBase createGetRequest(EncodingEnum theEncoding) {
Builder builder = createRequest();
addHeadersToRequest(builder, theEncoding);
addMatchHeaders(builder, myUrl);
return new JaxRsHttpRequestBase(builder, myRequestType, null);
}
public void addHeadersToRequest(Builder builder, EncodingEnum theEncoding) {
if (myHeaders != null) {
for (Header next : myHeaders) {
builder.header(next.getName(), next.getValue());
}
}
builder.header("User-Agent", "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client)");
builder.header("Accept-Charset", "utf-8");
builder.acceptEncoding("gzip");
if (theEncoding == null) {
builder.accept(Constants.HEADER_ACCEPT_VALUE_ALL);
} else if (theEncoding == EncodingEnum.JSON) {
builder.accept(Constants.CT_FHIR_JSON);
} else if (theEncoding == EncodingEnum.XML) {
builder.accept(Constants.CT_FHIR_XML);
}
}
private void addMatchHeaders(Builder theHttpRequest, StringBuilder theUrlBase) {
if (myIfNoneExistParams != null) {
StringBuilder b = newHeaderBuilder(theUrlBase);
BaseHttpClientInvocation.appendExtraParamsWithQuestionMark(myIfNoneExistParams, b, b.indexOf("?") == -1);
theHttpRequest.header(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.header(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 Builder createRequest() {
return myClient.target(myUrl.toString()).request();
}
}

View File

@ -0,0 +1,95 @@
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 ca.uhn.fhir.rest.api.IHttpRequestBase;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.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 JaxRsHttpRequestBase implements IHttpRequestBase {
private Invocation.Builder myRequest;
private RequestTypeEnum myRequestType;
private Entity<?> myEntity;
private Map<String, List<String>> myHeaders = new HashMap<String, List<String>>();
public JaxRsHttpRequestBase(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() {
return new JaxRsHttpResponse(getRequest().build(getRequestType().name(), getEntity()).invoke());
}
@Override
public Map<String, List<String>> getAllHeaders() {
return this.myHeaders;
}
}

View File

@ -0,0 +1,113 @@
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.IOException;
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.HttpHeaders;
import javax.ws.rs.core.Response;
import org.apache.http.entity.ContentType;
import ca.uhn.fhir.rest.client.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() {
ContentType ct = ContentType.parse(myResponse.getHeaderString(HttpHeaders.CONTENT_TYPE));
return ct != null ? ct.getMimeType() : null;
}
@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() throws UnsupportedOperationException, IOException {
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(myResponse.hasEntity()) {
myBufferedEntity = true;
myResponse.bufferEntity();
} else {
myResponse.bufferEntity();
}
}
@Override
public void close() {
// do nothing
}
}

View File

@ -0,0 +1,108 @@
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.ClientInvocationHandler;
import ca.uhn.fhir.rest.client.ClientInvocationHandlerFactory;
import ca.uhn.fhir.rest.client.RestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.EncodingEnum;
import org.apache.http.Header;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import java.lang.reflect.Method;
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 JaxRsHttpClient myHttpClient;
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 ClientInvocationHandler newInvocationHandler(IHttpClient theClient, String myUrlBase, Map<Method, Object> myMethodToReturnValue, Map<Method, BaseMethodBinding<?>> myBindings, Map<Method, ClientInvocationHandlerFactory.ILambda> myMethodToLambda) {
return new ClientInvocationHandler(myHttpClient, myContext, myUrlBase.toString(), myMethodToReturnValue, myBindings, myMethodToLambda, this);
}
@Override
public IHttpClient getHttpClient(StringBuilder url, Map<String, List<String>> myIfNoneExistParams, String myIfNoneExistString,
EncodingEnum theEncoding, RequestTypeEnum theRequestType, List<Header> myHeaders) {
return new JaxRsHttpClient(getNativeClientClient(), url, myIfNoneExistParams, myIfNoneExistString, theEncoding, theRequestType, myHeaders);
}
@Override
public void setProxy(String theHost, Integer thePort) {
throw new UnsupportedOperationException("Proxy setting is 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, null);
}
@Override
protected void resetHttpClient() {
this.myHttpClient = 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;
@ -106,6 +107,7 @@ public class AbstractJaxRsResourceProviderTest {
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
ourCtx.setRestfulClientFactory(new JaxRsRestfulClientFactory(ourCtx));
serverBase = "http://localhost:" + ourPort + "/";
client = ourCtx.newRestfulGenericClient(serverBase);
client.setEncoding(EncodingEnum.JSON);

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;
@ -150,7 +152,7 @@ public class ClientDstu1Test {
MethodOutcome response = client.createPatient(patient);
assertEquals(interceptor.getLastRequest().getURI().toASCIIString(), "http://foo/Patient");
assertEquals(((ApacheHttpRequestBase) 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

@ -48,8 +48,10 @@ import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.api.IHttpRequestBase;
import ca.uhn.fhir.rest.client.GenericClient;
import ca.uhn.fhir.rest.client.IClientInterceptor;
import ca.uhn.fhir.rest.client.IHttpResponse;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.to.model.HomeRequest;
@ -288,7 +290,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());
}
@ -647,17 +649,17 @@ public class BaseController {
}
@Override
public void interceptRequest(HttpRequestBase theRequest) {
public void interceptRequest(IHttpRequestBase 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 {
@ -667,7 +669,7 @@ public class BaseController {
}
myResponseBody = new String(bytes, "UTF-8");
theResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
myLastResponse.setEntity(new MyEntityWrapper(respEntity, bytes));
}
}

View File

@ -11,9 +11,11 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.IHttpRequestBase;
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.IHttpResponse;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy;
@ -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(IHttpRequestBase theRequest) {
if (isNotBlank(remoteAddr)) {
theRequest.addHeader("x-forwarded-for", remoteAddr);
}

View File

@ -1396,6 +1396,7 @@
<id>SITE</id>
<modules>
<module>hapi-fhir-base</module>
<module>hapi-fhir-base-client</module>
<module>hapi-fhir-structures-dstu</module>
<module>hapi-fhir-structures-dstu2</module>
<module>hapi-fhir-structures-hl7org-dstu2</module>
@ -1432,6 +1433,7 @@
<modules>
<module>hapi-deployable-pom</module>
<module>hapi-fhir-base</module>
<module>hapi-fhir-base-client</module>
<!--<module>hapi-fhir-oauth2</module> -->
<module>hapi-fhir-base-test-mindeps-client</module>
<module>hapi-fhir-base-test-mindeps-server</module>