diff --git a/hapi-fhir-base/.classpath b/hapi-fhir-base/.classpath
index 8a933cf4164..42f0d694287 100644
--- a/hapi-fhir-base/.classpath
+++ b/hapi-fhir-base/.classpath
@@ -3,7 +3,7 @@
-
+
@@ -80,8 +80,7 @@
-
+
-
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java
index b5ae7f3c5e9..19ace2234b1 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Read.java
@@ -5,10 +5,18 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
+import ca.uhn.fhir.model.api.IResource;
+
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Read {
+ /**
+ * Returns the resource type that is returned by the method annotated
+ * with this annotation
+ */
+ Class extends IResource> value();
+
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface IdParam {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocation.java
new file mode 100644
index 00000000000..b3c257c1757
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/ClientInvocation.java
@@ -0,0 +1,5 @@
+package ca.uhn.fhir.rest.client;
+
+public class ClientInvocation {
+
+}
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 eed8e841f0b..6793e99d446 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
@@ -2,21 +2,34 @@ 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.FhirContext;
+import ca.uhn.fhir.rest.common.BaseMethodBinding;
+
public class ClientInvocationHandler implements InvocationHandler {
private HttpClient myClient;
+ private Map myBindings = new HashMap();
+ private FhirContext myContext;
- public ClientInvocationHandler(HttpClient theClient) {
+ public ClientInvocationHandler(HttpClient theClient, FhirContext theContext) {
myClient = theClient;
+ myContext = theContext;
}
@Override
public Object invoke(Object theProxy, Method theMethod, Object[] theArgs) throws Throwable {
- // TODO Auto-generated method stub
+ BaseMethodBinding binding = myBindings.get(theMethod);
+ ClientInvocation clientInvocation = binding.invokeClient(theArgs);
return null;
}
+ public void addBinding(Method theMethod, BaseMethodBinding theBinding) {
+ myBindings.put(theMethod, theBinding);
+ }
+
}
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 68f28df3f5f..d1590313cdf 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
@@ -1,6 +1,7 @@
package ca.uhn.fhir.rest.client;
import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.TimeUnit;
@@ -11,7 +12,9 @@ import org.apache.http.impl.conn.SchemeRegistryFactory;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
+import ca.uhn.fhir.rest.common.BaseMethodBinding;
public class RestfulClientFactory {
@@ -20,41 +23,50 @@ public class RestfulClientFactory {
/**
* Constructor
*
- * @param theContext The context
+ * @param theContext
+ * The context
*/
public RestfulClientFactory(FhirContext theContext) {
myContext = theContext;
}
-
-
+
/**
* Instantiates a new client instance
*
- * @param theClientType The client type, which is an interface type to be instantiated
- * @param theServerBase The URL of the base for the restful FHIR server to connect to
+ * @param theClientType
+ * The client type, which is an interface type to be instantiated
+ * @param theServerBase
+ * The URL of the base for the restful FHIR server to connect to
* @return A newly created client
- * @throws ConfigurationException If the interface type is not an interface
+ * @throws ConfigurationException
+ * If the interface type is not an interface
*/
- public T newClient(Class theClientType, String theServerBase) {
+ public T newClient(Class theClientType, String theServerBase){
if (!theClientType.isInterface()) {
throw new ConfigurationException(theClientType.getCanonicalName() + " is not an interface");
}
-
+
PoolingClientConnectionManager connectionManager = new PoolingClientConnectionManager(SchemeRegistryFactory.createDefault(), 5000, TimeUnit.MILLISECONDS);
HttpClient client = new DefaultHttpClient(connectionManager);
- ClientInvocationHandler theInvocationHandler = new ClientInvocationHandler(client);
+ ClientInvocationHandler theInvocationHandler = new ClientInvocationHandler(client, myContext);
+
+ for (Method nextMethod : theClientType.getMethods()) {
+ BaseMethodBinding binding = BaseMethodBinding.bindMethod(nextMethod);
+ theInvocationHandler.addBinding(nextMethod, binding);
+ }
T proxy = instantiateProxy(theClientType, theInvocationHandler);
-
+
return proxy;
}
+
@SuppressWarnings("unchecked")
private T instantiateProxy(Class theClientType, InvocationHandler theInvocationHandler) {
- T proxy = (T) Proxy.newProxyInstance(RestfulClientFactory.class.getClassLoader(), new Class[] {theClientType}, theInvocationHandler);
+ T proxy = (T) Proxy.newProxyInstance(RestfulClientFactory.class.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);
return proxy;
}
-
+
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/BaseMethodBinding.java
new file mode 100644
index 00000000000..86fb07ee9cd
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/BaseMethodBinding.java
@@ -0,0 +1,131 @@
+package ca.uhn.fhir.rest.common;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import ca.uhn.fhir.context.ConfigurationException;
+import ca.uhn.fhir.model.api.IResource;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.annotation.Read;
+import ca.uhn.fhir.rest.client.ClientInvocation;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.Resource;
+import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.operations.Search;
+
+public abstract class BaseMethodBinding {
+
+ private String myResourceName;
+
+ public BaseMethodBinding(Class extends IResource> theAnnotatedResourceType) {
+ ResourceDef resourceDefAnnotation = theAnnotatedResourceType.getAnnotation(ResourceDef.class);
+ if (resourceDefAnnotation == null) {
+ throw new ConfigurationException(theAnnotatedResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName()+ " annotation");
+ }
+ myResourceName = resourceDefAnnotation.name();
+ }
+
+ public abstract ReturnTypeEnum getReturnType();
+
+ public ClientInvocation invokeClient(Object[] theArgs) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public abstract List invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map theParameterValues) throws InvalidRequestException, InternalErrorException;
+
+ public abstract boolean matches(String theResourceName, IdDt theId, IdDt theVersion, Set theParameterNames);
+
+ public String getResourceName() {
+ return myResourceName;
+ }
+
+ public static BaseMethodBinding bindMethod(Method theMethod) {
+ Read read = theMethod.getAnnotation(Read.class);
+ Search search = theMethod.getAnnotation(Search.class);
+ verifyExactlyOneValued(theMethod, read, search);
+
+ Class extends IResource> annotatedResourceType;
+ if (read != null) {
+ annotatedResourceType = read.value();
+ } else {
+ annotatedResourceType = search.value();
+ }
+
+ Class> methodReturnType = theMethod.getReturnType();
+
+ if (read != null) {
+ return new ReadMethodBinding(annotatedResourceType, theMethod);
+ } else if (search != null) {
+ return new SearchMethodBinding(annotatedResourceType, theMethod);
+ } else {
+ throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
+ }
+
+ // // each operation name must have a request type annotation and be
+ // unique
+ // if (null != read) {
+ // return rm;
+ // }
+ //
+ // SearchMethodBinding sm = new SearchMethodBinding();
+ // if (null != search) {
+ // sm.setRequestType(SearchMethodBinding.RequestType.GET);
+ // } else if (null != theMethod.getAnnotation(PUT.class)) {
+ // sm.setRequestType(SearchMethodBinding.RequestType.PUT);
+ // } else if (null != theMethod.getAnnotation(POST.class)) {
+ // sm.setRequestType(SearchMethodBinding.RequestType.POST);
+ // } else if (null != theMethod.getAnnotation(DELETE.class)) {
+ // sm.setRequestType(SearchMethodBinding.RequestType.DELETE);
+ // } else {
+ // return null;
+ // }
+ //
+ // return sm;
+ }
+
+ public static void verifyExactlyOneValued(Method theNextMethod, Object... theAnnotations) {
+ Object obj1 = null;
+ for (Object object : theAnnotations) {
+ if (object != null) {
+ if (obj1 == null) {
+ obj1 = object;
+ } else {
+ throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName()
+ + ". Can not have both.");
+ }
+
+ }
+ }
+ if (obj1 == null) {
+ throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has no FHIR method annotations.");
+ }
+ }
+
+ protected static List toResourceList(Object response) throws InternalErrorException {
+ if (response == null) {
+ return Collections.emptyList();
+ } else if (response instanceof IResource) {
+ return Collections.singletonList((IResource) response);
+ } else if (response instanceof Collection) {
+ List retVal = new ArrayList();
+ for (Object next : ((Collection>) response)) {
+ retVal.add((IResource) next);
+ }
+ return retVal;
+ } else {
+ throw new InternalErrorException("Unexpected return type: " + response.getClass().getCanonicalName());
+ }
+ }
+
+ public enum ReturnTypeEnum {
+ BUNDLE, RESOURCE
+ }
+}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ReadMethod.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/ReadMethodBinding.java
similarity index 73%
rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ReadMethod.java
rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/ReadMethodBinding.java
index 7ae2dd2e3a9..465c78fc471 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ReadMethod.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/ReadMethodBinding.java
@@ -1,4 +1,4 @@
-package ca.uhn.fhir.rest.server;
+package ca.uhn.fhir.rest.common;
import java.lang.reflect.Method;
import java.util.List;
@@ -10,23 +10,29 @@ import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.Util;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
-class ReadMethod extends BaseMethod {
+public class ReadMethodBinding extends BaseMethodBinding {
private Method myMethod;
private Integer myIdIndex;
private Integer myVersionIdIndex;
private int myParameterCount;
- ReadMethod(Method theMethod, Integer theIdIndex, Integer theVersionIdIndex) {
- Validate.notNull(theMethod, "Method must not be null");
- Validate.notNull(theIdIndex, "ID Index must not be null");
+ public ReadMethodBinding(Class extends IResource> theAnnotatedResourceType, Method theMethod) {
+ super(theAnnotatedResourceType);
+ Validate.notNull(theMethod, "Method must not be null");
+
+ Integer idIndex = Util.findReadIdParameterIndex(theMethod);
+ Integer versionIdIndex = Util.findReadVersionIdParameterIndex(theMethod);
+
myMethod = theMethod;
- myIdIndex = theIdIndex;
- myVersionIdIndex = theVersionIdIndex;
+ myIdIndex = idIndex;
+ myVersionIdIndex = versionIdIndex;
myParameterCount = myMethod.getParameterTypes().length;
Class>[] parameterTypes = theMethod.getParameterTypes();
@@ -41,7 +47,7 @@ class ReadMethod extends BaseMethod {
@Override
public boolean matches(String theResourceName, IdDt theId, IdDt theVersion, Set theParameterNames) {
- if (!theResourceName.equals(getResource().getResourceName())) {
+ if (!theResourceName.equals(getResourceName())) {
return false;
}
if (theParameterNames.isEmpty() == false) {
@@ -62,7 +68,7 @@ class ReadMethod extends BaseMethod {
}
@Override
- public List invoke(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map theParameterValues) throws InvalidRequestException,
+ public List invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map theParameterValues) throws InvalidRequestException,
InternalErrorException {
Object[] params = new Object[myParameterCount];
params[myIdIndex] = theId;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/SearchMethod.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/SearchMethodBinding.java
similarity index 66%
rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/SearchMethod.java
rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/SearchMethodBinding.java
index a5843676028..9ccb161e7b0 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/SearchMethod.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/common/SearchMethodBinding.java
@@ -1,41 +1,41 @@
-package ca.uhn.fhir.rest.server;
+package ca.uhn.fhir.rest.common;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
-import org.junit.internal.MethodSorter;
+import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.Parameter;
+import ca.uhn.fhir.rest.server.Util;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
/**
* Created by dsotnikov on 2/25/2014.
*/
-public class SearchMethod extends BaseMethod {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchMethod.class);
+public class SearchMethodBinding extends BaseMethodBinding {
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchMethodBinding.class);
private Method method;
private List parameters;
private RequestType requestType;
- private Class> resourceType;
+ private Class> myDeclaredResourceType;
- public SearchMethod() {
- }
-
- public SearchMethod(Method method, List parameters) {
- this.method = method;
- this.parameters = parameters;
+ public SearchMethodBinding(Class extends IResource> theAnnotatedResourceType, Method theMethod) {
+ super(theAnnotatedResourceType);
+ this.method = theMethod;
+ this.parameters = Util.getResourceParameters(theMethod);
+
+ this.myDeclaredResourceType = theMethod.getReturnType();
}
public Method getMethod() {
@@ -50,8 +50,8 @@ public class SearchMethod extends BaseMethod {
return requestType;
}
- public Class getResourceType() {
- return resourceType.getClass();
+ public Class getDeclaredResourceType() {
+ return myDeclaredResourceType.getClass();
}
@Override
@@ -60,7 +60,7 @@ public class SearchMethod extends BaseMethod {
}
@Override
- public List invoke(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map parameterValues) throws InvalidRequestException, InternalErrorException {
+ public List invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map parameterValues) throws InvalidRequestException, InternalErrorException {
assert theId == null;
assert theVersionId == null;
@@ -94,12 +94,12 @@ public class SearchMethod extends BaseMethod {
@Override
public boolean matches(String theResourceName, IdDt theId, IdDt theVersion, Set theParameterNames) {
- if (!theResourceName.equals(getResource().getResourceName())) {
- ourLog.info("Method {} doesn't match because resource name {} != {}", method.getName(), theResourceName, getResource().getResourceName());
+ if (!theResourceName.equals(getResourceName())) {
+ ourLog.trace("Method {} doesn't match because resource name {} != {}", method.getName(), theResourceName, getResourceName());
return false;
}
if (theId != null || theVersion != null) {
- ourLog.info("Method {} doesn't match because ID or Version are not null: {} - {}", theId, theVersion);
+ ourLog.trace("Method {} doesn't match because ID or Version are not null: {} - {}", theId, theVersion);
return false;
}
@@ -108,13 +108,13 @@ public class SearchMethod extends BaseMethod {
Parameter temp = this.parameters.get(i);
methodParamsTemp.add(temp.getName());
if (temp.isRequired() && !theParameterNames.contains(temp.getName())) {
- ourLog.info("Method {} doesn't match param '{}' is not present", temp.getName());
+ ourLog.trace("Method {} doesn't match param '{}' is not present", method.getName(), temp.getName());
return false;
}
}
boolean retVal = methodParamsTemp.containsAll(theParameterNames);
- ourLog.info("Method {} matches: {}", method.getName(), retVal);
+ ourLog.trace("Method {} matches: {}", method.getName(), retVal);
return retVal;
}
@@ -132,7 +132,7 @@ public class SearchMethod extends BaseMethod {
}
public void setResourceType(Class> resourceType) {
- this.resourceType = resourceType;
+ this.myDeclaredResourceType = resourceType;
}
public static enum RequestType {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BaseMethod.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BaseMethod.java
deleted file mode 100644
index b952ad51547..00000000000
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/BaseMethod.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package ca.uhn.fhir.rest.server;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import ca.uhn.fhir.model.api.IResource;
-import ca.uhn.fhir.model.primitive.IdDt;
-import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
-import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
-
-abstract class BaseMethod {
-
- public enum ReturnTypeEnum {
- RESOURCE, BUNDLE
- }
-
- private Resource resource;
-
- public Resource getResource() {
- return resource;
- }
-
- public abstract ReturnTypeEnum getReturnType();
-
- public abstract boolean matches(String theResourceName, IdDt theId, IdDt theVersion, Set theParameterNames);
-
- public void setResource(Resource theResource) {
- this.resource = theResource;
- }
-
- public abstract List invoke(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map theParameterValues) throws InvalidRequestException,
- InternalErrorException;
-
- protected static List toResourceList(Object response) throws InternalErrorException {
- if (response == null) {
- return Collections.emptyList();
- } else if (response instanceof IResource) {
- return Collections.singletonList((IResource) response);
- } else if (response instanceof Collection) {
- List retVal = new ArrayList();
- for (Object next : ((Collection>) response)) {
- retVal.add((IResource) next);
- }
- return retVal;
- } else {
- throw new InternalErrorException("Unexpected return type: " + response.getClass().getCanonicalName());
- }
- }
-}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Resource.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Resource.java
index 27667590896..d1d741c094b 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Resource.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Resource.java
@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Set;
import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.common.BaseMethodBinding;
/**
* Created by dsotnikov on 2/25/2014.
@@ -14,25 +15,25 @@ public class Resource {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Resource.class);
private String resourceName;
- private List methods = new ArrayList();
+ private List methods = new ArrayList();
private IResourceProvider resourceProvider;
public Resource() {
}
- public Resource(String resourceName, List methods) {
+ public Resource(String resourceName, List methods) {
this.resourceName = resourceName;
this.methods = methods;
}
- public BaseMethod getMethod(String theResourceName, IdDt theId, IdDt theVersionId, Set theParameters) throws Exception {
+ public BaseMethodBinding getMethod(String theResourceName, IdDt theId, IdDt theVersionId, Set theParameters) throws Exception {
if (null == methods) {
ourLog.warn("No methods exist for resource provider: {}", resourceProvider.getClass());
return null;
}
ourLog.info("Looking for a handler for {} / {} / {} / {}", new Object[] {theResourceName,theId, theVersionId, theParameters});
- for (BaseMethod rm : methods) {
+ for (BaseMethodBinding rm : methods) {
if (rm.matches(theResourceName, theId, theVersionId, theParameters)) {
ourLog.info("Handler {} matches", rm);
return rm;
@@ -51,17 +52,16 @@ public class Resource {
this.resourceName = resourceName;
}
- public List getMethods() {
+ public List getMethods() {
return methods;
}
- public void setMethods(List methods) {
+ public void setMethods(List methods) {
this.methods = methods;
}
- public void addMethod(BaseMethod method) {
+ public void addMethod(BaseMethodBinding method) {
this.methods.add(method);
- method.setResource(this);
}
@Override
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
index ccd4456d9a8..682a327ad73 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java
@@ -1,14 +1,10 @@
package ca.uhn.fhir.rest.server;
-import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.net.URL;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -20,22 +16,20 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.commons.lang3.StringUtils;
+
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
-import ca.uhn.fhir.parser.XmlParser;
-import ca.uhn.fhir.rest.annotation.Read;
+import ca.uhn.fhir.rest.common.BaseMethodBinding;
+import ca.uhn.fhir.rest.common.SearchMethodBinding;
import ca.uhn.fhir.rest.server.exceptions.AbstractResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
-import ca.uhn.fhir.rest.server.operations.DELETE;
-import ca.uhn.fhir.rest.server.operations.Search;
-import ca.uhn.fhir.rest.server.operations.POST;
-import ca.uhn.fhir.rest.server.operations.PUT;
public abstract class RestfulServer extends HttpServlet {
@@ -50,38 +44,6 @@ public abstract class RestfulServer extends HttpServlet {
// map of request handler resources keyed by resource name
private Map resources = new HashMap();
- private boolean addResourceMethod(Resource resource, Method method) throws Exception {
-
- // each operation name must have a request type annotation and be unique
- if (null != method.getAnnotation(Read.class)) {
- Integer idIndex = Util.findReadIdParameterIndex(method);
- Integer versionIdIndex = Util.findReadVersionIdParameterIndex(method);
- ReadMethod rm = new ReadMethod(method, idIndex, versionIdIndex);
- resource.addMethod(rm);
- return true;
- }
-
- SearchMethod sm = new SearchMethod();
- if (null != method.getAnnotation(Search.class)) {
- sm.setRequestType(SearchMethod.RequestType.GET);
- } else if (null != method.getAnnotation(PUT.class)) {
- sm.setRequestType(SearchMethod.RequestType.PUT);
- } else if (null != method.getAnnotation(POST.class)) {
- sm.setRequestType(SearchMethod.RequestType.POST);
- } else if (null != method.getAnnotation(DELETE.class)) {
- sm.setRequestType(SearchMethod.RequestType.DELETE);
- } else {
- return false;
- }
-
- sm.setMethod(method);
- sm.setResourceType(method.getReturnType());
- sm.setParameters(Util.getResourceParameters(method));
-
- resource.addMethod(sm);
- return true;
- }
-
@SuppressWarnings("unused")
private EncodingUtil determineResponseEncoding(Map theParams) {
String[] format = theParams.remove(Constants.PARAM_FORMAT);
@@ -91,22 +53,22 @@ public abstract class RestfulServer extends HttpServlet {
@Override
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- handleRequest(SearchMethod.RequestType.DELETE, request, response);
+ handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- handleRequest(SearchMethod.RequestType.GET, request, response);
+ handleRequest(SearchMethodBinding.RequestType.GET, request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- handleRequest(SearchMethod.RequestType.POST, request, response);
+ handleRequest(SearchMethodBinding.RequestType.POST, request, response);
}
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- handleRequest(SearchMethod.RequestType.PUT, request, response);
+ handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
}
private void findResourceMethods(IResourceProvider theProvider) throws Exception {
@@ -120,16 +82,17 @@ public abstract class RestfulServer extends HttpServlet {
resources.put(definition.getName(), r);
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
-
+
Class> clazz = theProvider.getClass();
for (Method m : clazz.getDeclaredMethods()) {
if (Modifier.isPublic(m.getModifiers())) {
ourLog.info("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
- boolean foundMethod = addResourceMethod(r, m);
- if (foundMethod) {
+ BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m);
+ if (foundMethodBinding != null) {
+ r.addMethod(foundMethodBinding);
ourLog.info(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
- }else {
+ } else {
ourLog.info(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
}
}
@@ -138,19 +101,20 @@ public abstract class RestfulServer extends HttpServlet {
public abstract Collection getResourceProviders();
- protected void handleRequest(SearchMethod.RequestType requestType, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ protected void handleRequest(SearchMethodBinding.RequestType requestType, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
String resourceName = null;
Long identity = null;
- String requestPath = request.getRequestURI();
- requestPath = requestPath.substring(request.getContextPath().length());
- if (requestPath.charAt(0)=='/') {
+ String requestPath = StringUtils.defaultString(request.getRequestURI());
+ String contextPath = StringUtils.defaultString(request.getContextPath());
+ requestPath = requestPath.substring(contextPath.length());
+ if (requestPath.charAt(0) == '/') {
requestPath = requestPath.substring(1);
}
-
+
ourLog.info("Request URI: {}", requestPath);
-
+
Map params = new HashMap(request.getParameterMap());
EncodingUtil responseEncoding = determineResponseEncoding(params);
@@ -178,21 +142,23 @@ public abstract class RestfulServer extends HttpServlet {
//
// if (identity != null && !tok.hasMoreTokens()) {
// if (params == null || params.isEmpty()) {
- // IResource resource = resourceBinding.getResourceProvider().getResourceById(identity);
+ // IResource resource =
+ // resourceBinding.getResourceProvider().getResourceById(identity);
// if (resource == null) {
// throw new ResourceNotFoundException(identity);
// }
- // streamResponseAsResource(response, resource, resourceBinding, responseEncoding);
+ // streamResponseAsResource(response, resource, resourceBinding,
+ // responseEncoding);
// return;
// }
// }
- BaseMethod resourceMethod = resourceBinding.getMethod(resourceName, id, versionId, params.keySet());
+ BaseMethodBinding resourceMethod = resourceBinding.getMethod(resourceName, id, versionId, params.keySet());
if (null == resourceMethod) {
throw new MethodNotFoundException("No resource method available for the supplied parameters " + params);
}
- List result = resourceMethod.invoke(resourceBinding.getResourceProvider(), id, versionId, params);
+ List result = resourceMethod.invokeServer(resourceBinding.getResourceProvider(), id, versionId, params);
switch (resourceMethod.getReturnType()) {
case BUNDLE:
streamResponseAsBundle(response, result, responseEncoding);
@@ -209,13 +175,13 @@ public abstract class RestfulServer extends HttpServlet {
// resourceMethod.get
} catch (AbstractResponseException e) {
-
+
if (e instanceof InternalErrorException) {
ourLog.error("Failure during REST processing", e);
} else {
ourLog.warn("Failure during REST processing: {}", e.toString());
}
-
+
response.setStatus(e.getStatusCode());
response.setContentType("text/plain");
response.setCharacterEncoding("UTF-8");
@@ -293,6 +259,4 @@ public abstract class RestfulServer extends HttpServlet {
}
-
-
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/operations/Search.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/operations/Search.java
index d42f6ea04e3..347173f7387 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/operations/Search.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/operations/Search.java
@@ -3,8 +3,16 @@ package ca.uhn.fhir.rest.server.operations;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import ca.uhn.fhir.model.api.IResource;
+
@Retention(RetentionPolicy.RUNTIME)
public @interface Search {
+ /**
+ * Returns the resource type that is returned by the method annotated
+ * with this annotation
+ */
+ Class extends IResource> value();
+
}
\ No newline at end of file
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java
new file mode 100644
index 00000000000..8ce8648f715
--- /dev/null
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/client/ITestClient.java
@@ -0,0 +1,22 @@
+package ca.uhn.fhir.rest.client;
+
+import ca.uhn.fhir.model.api.Bundle;
+import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
+import ca.uhn.fhir.model.dstu.resource.Patient;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.annotation.Read;
+import ca.uhn.fhir.rest.client.api.IRestfulClient;
+import ca.uhn.fhir.rest.server.operations.Search;
+import ca.uhn.fhir.rest.server.parameters.Required;
+
+public interface ITestClient extends IRestfulClient {
+
+ @Read(value=Patient.class)
+ Patient getPatientById(@Read.IdParam IdDt theId);
+
+ @Search(value=Patient.class)
+ Patient findPatientByMrn(@Required(name = Patient.SP_IDENTIFIER) IdentifierDt theId);
+
+ @Search(value=Patient.class)
+ Bundle findPatientByLastName(@Required(name = Patient.SP_FAMILY) IdentifierDt theId);
+}
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/DummyPatientResourceProvider.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/DummyPatientResourceProvider.java
index 383069acb6d..a046e1dc43b 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/DummyPatientResourceProvider.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/DummyPatientResourceProvider.java
@@ -51,7 +51,7 @@ public class DummyPatientResourceProvider implements IResourceProvider {
}
}
- @Search
+ @Search(Patient.class)
public Patient getPatient(@Required(name = "identifier") IdentifierDt theIdentifier) {
for (Patient next : myIdToPatient.values()) {
for (IdentifierDt nextId : next.getIdentifier()) {
@@ -75,7 +75,7 @@ public class DummyPatientResourceProvider implements IResourceProvider {
* The resource identity
* @return The resource
*/
- @Read
+ @Read(Patient.class)
public Patient getResourceById(@Read.IdParam IdDt theId) {
return myIdToPatient.get(theId.getValue());
}
diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java
index 68de4a36687..5d95afeb78a 100644
--- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java
+++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResourceMethodTest.java
@@ -10,20 +10,17 @@ import java.util.Set;
import org.junit.Before;
import org.junit.Test;
+import ca.uhn.fhir.model.dstu.resource.Patient;
+import ca.uhn.fhir.rest.common.SearchMethodBinding;
import ca.uhn.fhir.rest.server.Parameter;
-import ca.uhn.fhir.rest.server.SearchMethod;
public class ResourceMethodTest {
- private SearchMethod rm;
+ private SearchMethodBinding rm;
@Before
- public void before() {
- Resource resource = new Resource();
- resource.setResourceName("ResName");
-
- rm = new SearchMethod();
- rm.setResource(resource);
+ public void before() throws NoSuchMethodException, SecurityException {
+ rm = new SearchMethodBinding(Patient.class, ResourceMethodTest.class.getMethod("before"));
}
@Test