From 51f2ce1e37c24c4eb28775c208f35d5e3b26df4e Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 21 Nov 2014 11:39:21 -0500 Subject: [PATCH] Do not share interceptors between individual interface based client instances --- .../rest/client/ClientInvocationHandler.java | 77 ++---------- .../ClientInvocationHandlerFactory.java | 114 ++++++++++++++++++ .../rest/client/RestfulClientFactory.java | 8 +- .../rest/client/BasicAuthInterceptorTest.java | 17 ++- src/changes/changes.xml | 5 + 5 files changed, 149 insertions(+), 72 deletions(-) create mode 100644 hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandlerFactory.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java index 29c13b0ab56..cfe304f2bac 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandler.java @@ -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> myBindings = new HashMap>(); - - private final Map myMethodToLambda = new HashMap(); - - private final Map myMethodToReturnValue = new HashMap(); +class ClientInvocationHandler extends BaseClient implements InvocationHandler { + private final Map> myBindings; + private final Map myMethodToReturnValue; private FhirContext myContext; + private Map myMethodToLambda; - public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Class theClientType) { + public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Map theMethodToReturnValue, Map> theBindings, Map 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; - } - } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandlerFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandlerFactory.java new file mode 100644 index 00000000000..6b1705411b8 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocationHandlerFactory.java @@ -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> myBindings = new HashMap>(); + private final Map myMethodToReturnValue = new HashMap(); + private final Map myMethodToLambda = new HashMap(); + private final FhirContext myContext; + private final HttpClient myClient; + private final String myUrlBase; + + public ClientInvocationHandlerFactory(HttpClient theClient, FhirContext theContext, String theUrlBase, Class 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; + } + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index 18bedbdc61b..5a2a57f9c63 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -44,7 +44,7 @@ public class RestfulClientFactory implements IRestfulClientFactory { private int myConnectTimeout = 10000; private FhirContext myContext; private HttpClient myHttpClient; - private Map, ClientInvocationHandler> myInvocationHandlers = new HashMap, ClientInvocationHandler>(); + private Map, ClientInvocationHandlerFactory> myInvocationHandlers = new HashMap, 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; } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/BasicAuthInterceptorTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/BasicAuthInterceptorTest.java index 73efe686a28..1ecc337170e 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/BasicAuthInterceptorTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/BasicAuthInterceptorTest.java @@ -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 diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 4cdb9cd5690..9cb8a8510ed 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -151,6 +151,11 @@ OperationOutcome produced by a validation cycle cause the validation results to be incorrect. + + 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) +