Do not share interceptors between individual interface based client

instances
This commit is contained in:
James Agnew 2014-11-21 11:39:21 -05:00
parent 74ab9a779c
commit 51f2ce1e37
5 changed files with 149 additions and 72 deletions

View File

@ -22,47 +22,28 @@ package ca.uhn.fhir.rest.client;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.apache.http.client.HttpClient;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.client.ClientInvocationHandlerFactory.ILambda;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.EncodingEnum;
public class ClientInvocationHandler extends BaseClient implements InvocationHandler {
private final Map<Method, BaseMethodBinding<?>> myBindings = new HashMap<Method, BaseMethodBinding<?>>();
private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>();
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
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, Class<? extends IRestfulClient> theClientType) {
public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Map<Method, Object> theMethodToReturnValue, Map<Method, BaseMethodBinding<?>> theBindings, Map<Method, ILambda> theMethodToLambda) {
super(theClient, theUrlBase);
myContext =theContext;
try {
myMethodToReturnValue.put(theClientType.getMethod("getFhirContext"), theContext);
myMethodToReturnValue.put(theClientType.getMethod("getHttpClient"), theClient);
myMethodToReturnValue.put(theClientType.getMethod("getServerBase"), theUrlBase);
myMethodToLambda.put(theClientType.getMethod("setEncoding", EncodingEnum.class), new SetEncodingLambda());
myMethodToLambda.put(theClientType.getMethod("setPrettyPrint", boolean.class), new SetPrettyPrintLambda());
myMethodToLambda.put(theClientType.getMethod("registerInterceptor", IClientInterceptor.class), new RegisterInterceptorLambda());
myMethodToLambda.put(theClientType.getMethod("unregisterInterceptor", IClientInterceptor.class), new UnregisterInterceptorLambda());
} catch (NoSuchMethodException e) {
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
} catch (SecurityException e) {
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
}
myMethodToReturnValue = theMethodToReturnValue;
myBindings = theBindings;
myMethodToLambda=theMethodToLambda;
}
public void addBinding(Method theMethod, BaseMethodBinding<?> theBinding) {
@ -84,49 +65,11 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
ILambda lambda = myMethodToLambda.get(theMethod);
if (lambda != null) {
return lambda.handle(theArgs);
return lambda.handle(this, theArgs);
}
throw new UnsupportedOperationException("The method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getSimpleName() + " has no handler. Did you forget to annotate it with a RESTful method annotation?");
}
private interface ILambda {
Object handle(Object[] theArgs);
}
private class SetEncodingLambda implements ILambda {
@Override
public Object handle(Object[] theArgs) {
EncodingEnum encoding = (EncodingEnum) theArgs[0];
setEncoding(encoding);
return null;
}
}
private class SetPrettyPrintLambda implements ILambda {
@Override
public Object handle(Object[] theArgs) {
Boolean prettyPrint = (Boolean) theArgs[0];
setPrettyPrint(prettyPrint);
return null;
}
}
private class UnregisterInterceptorLambda implements ILambda {
@Override
public Object handle(Object[] theArgs) {
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
unregisterInterceptor(interceptor);
return null;
}
}
private class RegisterInterceptorLambda implements ILambda {
@Override
public Object handle(Object[] theArgs) {
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
registerInterceptor(interceptor);
return null;
}
}
}

View File

@ -0,0 +1,114 @@
package ca.uhn.fhir.rest.client;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 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.HashMap;
import java.util.Map;
import org.apache.http.client.HttpClient;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.EncodingEnum;
class ClientInvocationHandlerFactory {
private final Map<Method, BaseMethodBinding<?>> myBindings = new HashMap<Method, BaseMethodBinding<?>>();
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>();
private final FhirContext myContext;
private final HttpClient myClient;
private final String myUrlBase;
public ClientInvocationHandlerFactory(HttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
myClient = theClient;
myUrlBase = theUrlBase;
myContext = theContext;
try {
myMethodToReturnValue.put(theClientType.getMethod("getFhirContext"), theContext);
myMethodToReturnValue.put(theClientType.getMethod("getHttpClient"), theClient);
myMethodToReturnValue.put(theClientType.getMethod("getServerBase"), theUrlBase);
myMethodToLambda.put(theClientType.getMethod("setEncoding", EncodingEnum.class), new SetEncodingLambda());
myMethodToLambda.put(theClientType.getMethod("setPrettyPrint", boolean.class), new SetPrettyPrintLambda());
myMethodToLambda.put(theClientType.getMethod("registerInterceptor", IClientInterceptor.class), new RegisterInterceptorLambda());
myMethodToLambda.put(theClientType.getMethod("unregisterInterceptor", IClientInterceptor.class), new UnregisterInterceptorLambda());
} catch (NoSuchMethodException e) {
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
} catch (SecurityException e) {
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
}
}
public void addBinding(Method theMethod, BaseMethodBinding<?> theBinding) {
myBindings.put(theMethod, theBinding);
}
ClientInvocationHandler newInvocationHandler() {
return new ClientInvocationHandler(myClient, myContext, myUrlBase, myMethodToReturnValue, myBindings, myMethodToLambda);
}
interface ILambda {
Object handle(ClientInvocationHandler theTarget, Object[] theArgs);
}
class SetEncodingLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
EncodingEnum encoding = (EncodingEnum) theArgs[0];
theTarget.setEncoding(encoding);
return null;
}
}
class SetPrettyPrintLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
Boolean prettyPrint = (Boolean) theArgs[0];
theTarget.setPrettyPrint(prettyPrint);
return null;
}
}
class UnregisterInterceptorLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
theTarget.unregisterInterceptor(interceptor);
return null;
}
}
class RegisterInterceptorLambda implements ILambda {
@Override
public Object handle(ClientInvocationHandler theTarget, Object[] theArgs) {
IClientInterceptor interceptor = (IClientInterceptor) theArgs[0];
theTarget.registerInterceptor(interceptor);
return null;
}
}
}

View File

@ -44,7 +44,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
private int myConnectTimeout = 10000;
private FhirContext myContext;
private HttpClient myHttpClient;
private Map<Class<? extends IRestfulClient>, ClientInvocationHandler> myInvocationHandlers = new HashMap<Class<? extends IRestfulClient>, ClientInvocationHandler>();
private Map<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory> myInvocationHandlers = new HashMap<Class<? extends IRestfulClient>, ClientInvocationHandlerFactory>();
private int mySocketTimeout = 10000;
private HttpHost myProxy;
@ -146,9 +146,9 @@ public class RestfulClientFactory implements IRestfulClientFactory {
serverBase = serverBase + "/";
}
ClientInvocationHandler invocationHandler = myInvocationHandlers.get(theClientType);
ClientInvocationHandlerFactory invocationHandler = myInvocationHandlers.get(theClientType);
if (invocationHandler == null) {
invocationHandler = new ClientInvocationHandler(client, myContext, serverBase, theClientType);
invocationHandler = new ClientInvocationHandlerFactory(client, myContext, serverBase, theClientType);
for (Method nextMethod : theClientType.getMethods()) {
BaseMethodBinding<?> binding = BaseMethodBinding.bindMethod(nextMethod, myContext, null);
invocationHandler.addBinding(nextMethod, binding);
@ -156,7 +156,7 @@ public class RestfulClientFactory implements IRestfulClientFactory {
myInvocationHandlers.put(theClientType, invocationHandler);
}
T proxy = instantiateProxy(theClientType, invocationHandler);
T proxy = instantiateProxy(theClientType, invocationHandler.newInvocationHandler());
return proxy;
}

View File

@ -75,8 +75,23 @@ public class BasicAuthInterceptorTest {
client.registerInterceptor(new BasicAuthInterceptor("myuser", "mypass"));
client.getPatientById(new IdDt("111"));
HttpUriRequest req = capt.getValue();
assertEquals(1, capt.getAllValues().size());
HttpUriRequest req = capt.getAllValues().get(0);
assertEquals(1, req.getHeaders("Authorization").length);
assertEquals("Basic bXl1c2VyOm15cGFzcw==", req.getFirstHeader("Authorization").getValue());
// Create a second client and make sure we get the same results
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
client = ourCtx.newRestfulClient(ITestClient.class, "http://foo");
client.registerInterceptor(new BasicAuthInterceptor("myuser", "mypass"));
client.getPatientById(new IdDt("111"));
assertEquals(2, capt.getAllValues().size());
req = capt.getAllValues().get(1);
assertEquals(1, req.getHeaders("Authorization").length);
assertEquals("Basic bXl1c2VyOm15cGFzcw==", req.getFirstHeader("Authorization").getValue());
}
@BeforeClass

View File

@ -151,6 +151,11 @@
OperationOutcome produced by a validation cycle cause the validation
results to be incorrect.
</action>
<action type="fix">
Client interceptors registered to an interface based client instance
were applied to other client instances for the same client interface as well. (Issue
did not affect generic/fluent clients)
</action>
</release>
<release version="0.7" date="2014-Oct-23">
<action type="add" issue="30">