More client work

This commit is contained in:
jamesagnew 2014-03-06 08:55:31 -05:00
parent b05ea01b45
commit 82193017d8
15 changed files with 296 additions and 184 deletions

View File

@ -3,7 +3,7 @@
<classpathentry including="**/*.java" kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry excluding="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry including="**/*.java" kind="src" path="src/main/java"/>
<classpathentry kind="var" path="M2_REPO/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0.jar" sourcepath="M2_REPO/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0-sources.jar">
<classpathentry kind="var" path="M2_REPO/javax/servlet/javax.servlet-api/3.0.1/javax.servlet-api-3.0.1.jar" sourcepath="M2_REPO/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/javax/servlet/javax.servlet-api/3.1.0/javax.servlet-api-3.1.0-javadoc.jar!/"/>
</attributes>
@ -80,8 +80,7 @@
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/xmlunit/xmlunit/1.5/xmlunit-1.5-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
<classpathentry kind="var" path="M2_REPO/org/mockito/mockito-all/1.9.5/mockito-all-1.9.5.jar"/>
<classpathentry combineaccessrules="false" kind="src" path="/hapi-fhir-structures-dstu"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

View File

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

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.client;
public class ClientInvocation {
}

View File

@ -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<Method, BaseMethodBinding> myBindings = new HashMap<Method, BaseMethodBinding>();
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);
}
}

View File

@ -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,20 +23,23 @@ 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 extends IRestfulClient> T newClient(Class<T> theClientType, String theServerBase){
if (!theClientType.isInterface()) {
@ -43,7 +49,12 @@ public class RestfulClientFactory {
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);
@ -51,6 +62,7 @@ public class RestfulClientFactory {
}
@SuppressWarnings("unchecked")
private <T extends IRestfulClient> T instantiateProxy(Class<T> theClientType, InvocationHandler theInvocationHandler) {
T proxy = (T) Proxy.newProxyInstance(RestfulClientFactory.class.getClassLoader(), new Class[] { theClientType }, theInvocationHandler);

View File

@ -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<IResource> invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException, InternalErrorException;
public abstract boolean matches(String theResourceName, IdDt theId, IdDt theVersion, Set<String> 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<IResource> 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<IResource> retVal = new ArrayList<IResource>();
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
}
}

View File

@ -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) {
public ReadMethodBinding(Class<? extends IResource> theAnnotatedResourceType, Method theMethod) {
super(theAnnotatedResourceType);
Validate.notNull(theMethod, "Method must not be null");
Validate.notNull(theIdIndex, "ID Index 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<String> 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<IResource> invoke(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException,
public List<IResource> invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException,
InternalErrorException {
Object[] params = new Object[myParameterCount];
params[myIdIndex] = theId;

View File

@ -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<Parameter> parameters;
private RequestType requestType;
private Class<?> resourceType;
private Class<?> myDeclaredResourceType;
public SearchMethod() {
}
public SearchMethodBinding(Class<? extends IResource> theAnnotatedResourceType, Method theMethod) {
super(theAnnotatedResourceType);
this.method = theMethod;
this.parameters = Util.getResourceParameters(theMethod);
public SearchMethod(Method method, List<Parameter> parameters) {
this.method = method;
this.parameters = parameters;
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<IResource> invoke(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> parameterValues) throws InvalidRequestException, InternalErrorException {
public List<IResource> invokeServer(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> 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<String> 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 {

View File

@ -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<String> theParameterNames);
public void setResource(Resource theResource) {
this.resource = theResource;
}
public abstract List<IResource> invoke(IResourceProvider theResourceProvider, IdDt theId, IdDt theVersionId, Map<String, String[]> theParameterValues) throws InvalidRequestException,
InternalErrorException;
protected static List<IResource> 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<IResource> retVal = new ArrayList<IResource>();
for (Object next : ((Collection<?>) response)) {
retVal.add((IResource) next);
}
return retVal;
} else {
throw new InternalErrorException("Unexpected return type: " + response.getClass().getCanonicalName());
}
}
}

View File

@ -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<BaseMethod> methods = new ArrayList<BaseMethod>();
private List<BaseMethodBinding> methods = new ArrayList<BaseMethodBinding>();
private IResourceProvider resourceProvider;
public Resource() {
}
public Resource(String resourceName, List<BaseMethod> methods) {
public Resource(String resourceName, List<BaseMethodBinding> methods) {
this.resourceName = resourceName;
this.methods = methods;
}
public BaseMethod getMethod(String theResourceName, IdDt theId, IdDt theVersionId, Set<String> theParameters) throws Exception {
public BaseMethodBinding getMethod(String theResourceName, IdDt theId, IdDt theVersionId, Set<String> 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<BaseMethod> getMethods() {
public List<BaseMethodBinding> getMethods() {
return methods;
}
public void setMethods(List<BaseMethod> methods) {
public void setMethods(List<BaseMethodBinding> methods) {
this.methods = methods;
}
public void addMethod(BaseMethod method) {
public void addMethod(BaseMethodBinding method) {
this.methods.add(method);
method.setResource(this);
}
@Override

View File

@ -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<String, Resource> resources = new HashMap<String, Resource>();
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<String, String[]> 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 {
@ -126,8 +88,9 @@ public abstract class RestfulServer extends HttpServlet {
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 {
ourLog.info(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
@ -138,13 +101,14 @@ public abstract class RestfulServer extends HttpServlet {
public abstract Collection<IResourceProvider> 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());
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);
}
@ -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<IResource> result = resourceMethod.invoke(resourceBinding.getResourceProvider(), id, versionId, params);
List<IResource> result = resourceMethod.invokeServer(resourceBinding.getResourceProvider(), id, versionId, params);
switch (resourceMethod.getReturnType()) {
case BUNDLE:
streamResponseAsBundle(response, result, responseEncoding);
@ -293,6 +259,4 @@ public abstract class RestfulServer extends HttpServlet {
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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());
}

View File

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