Some refactoring - Currently does not compile,will fix this afternoon
This commit is contained in:
parent
e9a71d843a
commit
5da4698fab
|
@ -1,15 +1,3 @@
|
|||
|
||||
* Implement JSON parser and encoder- I'm thinking probably just using GSON
|
||||
and writing the encode() part from scratch. For the parse() part, the
|
||||
ParserState class from the XML parser should be able to be shared with
|
||||
the Json parser, which will make it a lot easier to implement..?
|
||||
|
||||
* Implement strategy for narrative generation- I'm thinking maybe
|
||||
an interface that turns a given resource into an HTML representation
|
||||
and then a default implementation for our basic types (Patient, Observation)
|
||||
and maybe a way to configure this in the FhirContext so that the parser
|
||||
can take advantage?
|
||||
|
||||
* Add SimpleSetters for all primitive datatypes
|
||||
|
||||
* Implement and add Simple Getters in a similar way to simple setters
|
||||
|
|
|
@ -14,9 +14,11 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||
|
@ -36,18 +38,25 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
|
|||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
||||
public abstract class BaseMethodBinding {
|
||||
|
||||
private Method myMethod;
|
||||
private FhirContext myContext;
|
||||
private Object myProvider;
|
||||
|
||||
public BaseMethodBinding(Method theMethod, FhirContext theConetxt) {
|
||||
public BaseMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
||||
assert theMethod != null;
|
||||
assert theConetxt != null;
|
||||
|
||||
myMethod = theMethod;
|
||||
myContext = theConetxt;
|
||||
myProvider = theProvider;
|
||||
}
|
||||
|
||||
public Object getProvider() {
|
||||
return myProvider;
|
||||
}
|
||||
|
||||
public FhirContext getContext() {
|
||||
|
@ -78,7 +87,7 @@ public abstract class BaseMethodBinding {
|
|||
return parser;
|
||||
}
|
||||
|
||||
public static BaseMethodBinding bindMethod(Class<? extends IResource> theReturnType, Method theMethod, FhirContext theContext, IResourceProvider theProvider) {
|
||||
public static BaseMethodBinding bindMethod(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
Read read = theMethod.getAnnotation(Read.class);
|
||||
Search search = theMethod.getAnnotation(Search.class);
|
||||
Metadata conformance = theMethod.getAnnotation(Metadata.class);
|
||||
|
@ -87,34 +96,63 @@ public abstract class BaseMethodBinding {
|
|||
Delete delete = theMethod.getAnnotation(Delete.class);
|
||||
History history = theMethod.getAnnotation(History.class);
|
||||
// ** if you add another annotation above, also add it to the next line:
|
||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance,create,update,delete,history)) {
|
||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Class<? extends IResource> returnType = theReturnType;
|
||||
if (returnType == null) {
|
||||
Class<? extends IResource> returnTypeFromRp = null;
|
||||
if (theProvider instanceof IResourceProvider) {
|
||||
returnTypeFromRp = ((IResourceProvider) theProvider).getResourceType();
|
||||
if (!verifyIsValidResourceReturnType(returnTypeFromRp)) {
|
||||
throw new ConfigurationException("getResourceType() from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returned " + toLogString(returnTypeFromRp) + " - Must return a resource type");
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> returnTypeFromMethod = theMethod.getReturnType();
|
||||
if (Collection.class.isAssignableFrom(returnTypeFromMethod)) {
|
||||
returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
|
||||
}
|
||||
|
||||
|
||||
if (Bundle.class.isAssignableFrom(returnTypeFromMethod)) {
|
||||
returnTypeFromMethod = returnTypeFromRp;
|
||||
}
|
||||
if (!IResource.class.isAssignableFrom(returnTypeFromMethod)) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " - Must return a resource type");
|
||||
}
|
||||
|
||||
if (returnTypeFromRp != null) {
|
||||
if (returnTypeFromRp.equals(returnTypeFromMethod) == false) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " returns type " + returnTypeFromMethod.getCanonicalName() + " but must return " + returnTypeFromRp.getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
if (returnTypeFromMethod == null) {
|
||||
if (read != null) {
|
||||
returnType = read.type();
|
||||
returnTypeFromMethod = read.type();
|
||||
} else if (search != null) {
|
||||
returnType = search.type();
|
||||
returnTypeFromMethod = search.type();
|
||||
}
|
||||
|
||||
if (returnType == IResource.class) {
|
||||
if (returnTypeFromMethod == IResource.class) {
|
||||
throw new ConfigurationException("Could not determine return type for method '" + theMethod.getName() + "'. Try explicitly specifying one in the operation annotation.");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends IResource> retType = (Class<? extends IResource>) returnTypeFromMethod;
|
||||
|
||||
if (read != null) {
|
||||
return new ReadMethodBinding(returnType, theMethod, theContext);
|
||||
return new ReadMethodBinding(retType, theMethod, theContext, theProvider);
|
||||
} else if (search != null) {
|
||||
String queryName = search.queryName();
|
||||
return new SearchMethodBinding(returnType, theMethod, queryName, theContext);
|
||||
return new SearchMethodBinding(retType, theMethod, queryName, theContext, theProvider);
|
||||
} else if (conformance != null) {
|
||||
return new ConformanceMethodBinding(theMethod, theContext);
|
||||
return new ConformanceMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (create != null) {
|
||||
return new CreateMethodBinding(theMethod, theContext);
|
||||
return new CreateMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (update != null) {
|
||||
return new UpdateMethodBinding(theMethod, theContext);
|
||||
return new UpdateMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (delete != null) {
|
||||
return new DeleteMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (history != null) {
|
||||
|
@ -145,6 +183,22 @@ public abstract class BaseMethodBinding {
|
|||
// return sm;
|
||||
}
|
||||
|
||||
private static boolean verifyIsValidResourceReturnType(Class<?> theReturnType) {
|
||||
if (theReturnType==null) {
|
||||
return false;
|
||||
}
|
||||
if (!IResource.class.isAssignableFrom(theReturnType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String toLogString(Class<? extends IResource> theType) {
|
||||
if (theType==null) {
|
||||
return null;
|
||||
}
|
||||
return theType.getCanonicalName();
|
||||
}
|
||||
|
||||
public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
|
||||
Object obj1 = null;
|
||||
for (Object object : theAnnotations) {
|
||||
|
@ -152,8 +206,8 @@ public abstract class BaseMethodBinding {
|
|||
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.");
|
||||
throw new ConfigurationException("Method " + theNextMethod.getName() + " on type '" + theNextMethod.getDeclaringClass().getSimpleName() + " has annotations @" + obj1.getClass().getSimpleName() + " and @" + object.getClass().getSimpleName()
|
||||
+ ". Can not have both.");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -215,4 +269,5 @@ public abstract class BaseMethodBinding {
|
|||
return bindMethod(null, theMethod, theContext, null);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -43,8 +43,8 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
private List<IParameter> myParameters;
|
||||
private boolean myReturnVoid;
|
||||
|
||||
public BaseOutcomeReturningMethodBinding(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation) {
|
||||
super(theMethod, theContext);
|
||||
public BaseOutcomeReturningMethodBinding(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
|
||||
super(theMethod, theContext, theProvider);
|
||||
myParameters = Util.getResourceParameters(theMethod);
|
||||
|
||||
if (!theMethod.getReturnType().equals(MethodOutcome.class)) {
|
||||
|
@ -132,7 +132,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
|
||||
MethodOutcome response;
|
||||
try {
|
||||
response = (MethodOutcome) this.getMethod().invoke(theRequest.getResourceProvider(), params);
|
||||
response = (MethodOutcome) this.getMethod().invoke(getProvider(), params);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalErrorException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
|
|
@ -27,8 +27,8 @@ public abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends
|
|||
private int myResourceParameterIndex;
|
||||
private String myResourceName;
|
||||
|
||||
public BaseOutcomeReturningMethodBindingWithResourceParam(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation) {
|
||||
super(theMethod, theContext, theMethodAnnotation);
|
||||
public BaseOutcomeReturningMethodBindingWithResourceParam(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
|
||||
super(theMethod, theContext, theMethodAnnotation, theProvider);
|
||||
|
||||
ResourceParameter resourceParameter = null;
|
||||
|
||||
|
@ -76,7 +76,7 @@ public abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends
|
|||
|
||||
MethodOutcome response;
|
||||
try {
|
||||
response = (MethodOutcome) this.getMethod().invoke(theRequest.getResourceProvider(), params);
|
||||
response = (MethodOutcome) this.getMethod().invoke(getProvider(), params);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalErrorException(e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
|
|
@ -56,8 +56,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
|
||||
private String myResourceName;
|
||||
|
||||
public BaseResourceReturningMethodBinding(Class<? extends IResource> theReturnResourceType, Method theMethod, FhirContext theConetxt) {
|
||||
super(theMethod, theConetxt);
|
||||
public BaseResourceReturningMethodBinding(Class<? extends IResource> theReturnResourceType, Method theMethod, FhirContext theConetxt, Object theProvider) {
|
||||
super(theMethod, theConetxt, theProvider);
|
||||
|
||||
Class<?> methodReturnType = theMethod.getReturnType();
|
||||
if (Collection.class.isAssignableFrom(methodReturnType)) {
|
||||
|
@ -165,7 +165,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
requestIsBrowser = true;
|
||||
}
|
||||
|
||||
List<IResource> result = invokeServer(theRequest.getResourceProvider(), theRequest.getId(), theRequest.getVersion(), theRequest.getParameters());
|
||||
List<IResource> result = invokeServer(getProvider(), theRequest.getId(), theRequest.getVersion(), theRequest.getParameters());
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE:
|
||||
streamResponseAsBundle(theServer, theResponse, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode);
|
||||
|
|
|
@ -19,8 +19,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
|
||||
public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
|
||||
public ConformanceMethodBinding(Method theMethod, FhirContext theContext) {
|
||||
super(Conformance.class, theMethod, theContext);
|
||||
public ConformanceMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(Conformance.class, theMethod, theContext, theProvider);
|
||||
|
||||
if (getMethodReturnType() != MethodReturnTypeEnum.RESOURCE || theMethod.getReturnType() != Conformance.class) {
|
||||
throw new ConfigurationException("Conformance resource provider method '" + theMethod.getName() + "' should return type " + Conformance.class);
|
||||
|
|
|
@ -17,8 +17,8 @@ public class CreateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
|
||||
|
||||
|
||||
public CreateMethodBinding(Method theMethod, FhirContext theContext) {
|
||||
super(theMethod, theContext, Create.class);
|
||||
public CreateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod, theContext, Create.class, theProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,8 +27,9 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
private String myResourceName;
|
||||
private Integer myIdParameterIndex;
|
||||
|
||||
public DeleteMethodBinding(Method theMethod, FhirContext theContext, IResourceProvider theProvider) {
|
||||
super(theMethod, theContext, Delete.class);
|
||||
public DeleteMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod, theContext, Delete.class,
|
||||
theProvider);
|
||||
|
||||
Delete deleteAnnotation = theMethod.getAnnotation(Delete.class);
|
||||
Class<? extends IResource> resourceType = deleteAnnotation.resourceType();
|
||||
|
@ -36,8 +37,8 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
RuntimeResourceDefinition def = theContext.getResourceDefinition(resourceType);
|
||||
myResourceName = def.getName();
|
||||
} else {
|
||||
if (theProvider != null) {
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(theProvider.getResourceType());
|
||||
if (theProvider != null && theProvider instanceof IResourceProvider) {
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(((IResourceProvider) theProvider).getResourceType());
|
||||
myResourceName = def.getName();
|
||||
} else {
|
||||
throw new ConfigurationException("Can not determine resource type for method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getCanonicalName() + " - Did you forget to include the resourceType() value on the @"
|
||||
|
|
|
@ -35,8 +35,8 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private Integer mySinceParamIndex;
|
||||
private Integer myCountParamIndex;
|
||||
|
||||
public HistoryMethodBinding(Method theMethod, FhirContext theConetxt, IResourceProvider theProvider) {
|
||||
super(toReturnType(theMethod, theProvider), theMethod, theConetxt);
|
||||
public HistoryMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
||||
super(toReturnType(theMethod, theProvider), theMethod, theConetxt, theProvider);
|
||||
|
||||
myIdParamIndex = Util.findIdParameterIndex(theMethod);
|
||||
mySinceParamIndex = Util.findSinceParameterIndex(theMethod);
|
||||
|
@ -45,8 +45,8 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
History historyAnnotation = theMethod.getAnnotation(History.class);
|
||||
Class<? extends IResource> type = historyAnnotation.resourceType();
|
||||
if (type == History.AllResources.class) {
|
||||
if (theProvider != null) {
|
||||
type = theProvider.getResourceType();
|
||||
if (theProvider instanceof IResourceProvider) {
|
||||
type = ((IResourceProvider) theProvider).getResourceType();
|
||||
if (myIdParamIndex != null) {
|
||||
myResourceOperationType = RestfulOperationTypeEnum.HISTORY_INSTANCE;
|
||||
} else {
|
||||
|
@ -74,9 +74,9 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
}
|
||||
|
||||
private static Class<? extends IResource> toReturnType(Method theMethod, IResourceProvider theProvider) {
|
||||
if (theProvider != null) {
|
||||
return theProvider.getResourceType();
|
||||
private static Class<? extends IResource> toReturnType(Method theMethod, Object theProvider) {
|
||||
if (theProvider instanceof IResourceProvider) {
|
||||
return ((IResourceProvider) theProvider).getResourceType();
|
||||
}
|
||||
History historyAnnotation = theMethod.getAnnotation(History.class);
|
||||
Class<? extends IResource> type = historyAnnotation.resourceType();
|
||||
|
|
|
@ -25,8 +25,8 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private Integer myVersionIdIndex;
|
||||
private int myParameterCount;
|
||||
|
||||
public ReadMethodBinding(Class<? extends IResource> theAnnotatedResourceType, Method theMethod, FhirContext theContext) {
|
||||
super( theAnnotatedResourceType, theMethod, theContext);
|
||||
public ReadMethodBinding(Class<? extends IResource> theAnnotatedResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super( theAnnotatedResourceType, theMethod, theContext, theProvider);
|
||||
|
||||
Validate.notNull(theMethod, "Method must not be null");
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ public class Request {
|
|||
private Map<String, String[]> myParameters;
|
||||
private RequestType myRequestType;
|
||||
private String myResourceName;
|
||||
private Object myResourceProvider;
|
||||
private IdDt myVersion;
|
||||
private HttpServletRequest myServletRequest;
|
||||
|
||||
|
@ -56,21 +55,16 @@ public class Request {
|
|||
return myResourceName;
|
||||
}
|
||||
|
||||
public Object getResourceProvider() {
|
||||
return myResourceProvider;
|
||||
}
|
||||
|
||||
public IdDt getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
|
||||
public void setCompleteUrl(String theCompleteUrl) {
|
||||
myCompleteUrl=theCompleteUrl;
|
||||
myCompleteUrl = theCompleteUrl;
|
||||
}
|
||||
|
||||
public void setFhirServerBase(String theFhirServerBase) {
|
||||
myFhirServerBase=theFhirServerBase;
|
||||
myFhirServerBase = theFhirServerBase;
|
||||
}
|
||||
|
||||
public void setId(IdDt theId) {
|
||||
|
@ -78,7 +72,7 @@ public class Request {
|
|||
}
|
||||
|
||||
public void setInputReader(Reader theReader) {
|
||||
myInputReader=theReader;
|
||||
myInputReader = theReader;
|
||||
}
|
||||
|
||||
public void setOperation(String theOperation) {
|
||||
|
@ -89,7 +83,6 @@ public class Request {
|
|||
myParameters = theParams;
|
||||
}
|
||||
|
||||
|
||||
public void setRequestType(RequestType theRequestType) {
|
||||
myRequestType = theRequestType;
|
||||
}
|
||||
|
@ -98,10 +91,6 @@ public class Request {
|
|||
myResourceName = theResourceName;
|
||||
}
|
||||
|
||||
public void setResourceProvider(Object theProvider) {
|
||||
myResourceProvider=theProvider;
|
||||
}
|
||||
|
||||
public void setVersion(IdDt theVersion) {
|
||||
myVersion = theVersion;
|
||||
}
|
||||
|
@ -119,7 +108,7 @@ public class Request {
|
|||
}
|
||||
|
||||
public void setServletRequest(HttpServletRequest theRequest) {
|
||||
myServletRequest=theRequest;
|
||||
myServletRequest = theRequest;
|
||||
}
|
||||
|
||||
public HttpServletRequest getServletRequest() {
|
||||
|
|
|
@ -34,8 +34,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
private List<IParameter> myParameters;
|
||||
private String myQueryName;
|
||||
|
||||
public SearchMethodBinding(Class<? extends IResource> theReturnResourceType, Method theMethod, String theQueryName, FhirContext theContext) {
|
||||
super(theReturnResourceType, theMethod, theContext);
|
||||
public SearchMethodBinding(Class<? extends IResource> theReturnResourceType, Method theMethod, String theQueryName, FhirContext theContext, Object theProvider) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
this.myParameters = Util.getResourceParameters(theMethod);
|
||||
this.myQueryName = StringUtils.defaultIfBlank(theQueryName, null);
|
||||
this.myDeclaredResourceType = theMethod.getReturnType();
|
||||
|
|
|
@ -21,6 +21,7 @@ import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
|||
import ca.uhn.fhir.rest.client.PutClientInvocation;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
|
@ -29,8 +30,8 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
|
||||
private Integer myVersionIdParameterIndex;
|
||||
|
||||
public UpdateMethodBinding(Method theMethod, FhirContext theContext) {
|
||||
super(theMethod, theContext, Update.class);
|
||||
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod, theContext, Update.class, theProvider);
|
||||
|
||||
myIdParameterIndex = Util.findIdParameterIndex(theMethod);
|
||||
if (myIdParameterIndex == null) {
|
||||
|
|
|
@ -15,7 +15,6 @@ public class ResourceBinding {
|
|||
|
||||
private String resourceName;
|
||||
private List<BaseMethodBinding> methods = new ArrayList<BaseMethodBinding>();
|
||||
private IResourceProvider resourceProvider;
|
||||
|
||||
public ResourceBinding() {
|
||||
}
|
||||
|
@ -27,7 +26,7 @@ public class ResourceBinding {
|
|||
|
||||
public BaseMethodBinding getMethod(Request theRequest) throws Exception {
|
||||
if (null == methods) {
|
||||
ourLog.warn("No methods exist for resource provider: {}", resourceProvider.getClass());
|
||||
ourLog.warn("No methods exist for resource: {}", resourceName);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -75,12 +74,4 @@ public class ResourceBinding {
|
|||
return 0;
|
||||
}
|
||||
|
||||
public void setResourceProvider(IResourceProvider theProvider) {
|
||||
resourceProvider = theProvider;
|
||||
}
|
||||
|
||||
public IResourceProvider getResourceProvider() {
|
||||
return resourceProvider;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.rest.server;
|
|||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
@ -14,98 +15,295 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.codehaus.stax2.ri.evt.ProcInstrEventImpl;
|
||||
|
||||
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.IResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.Request;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ConfigurationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
|
||||
public abstract class RestfulServer extends HttpServlet {
|
||||
|
||||
public class RestfulServer extends HttpServlet {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private FhirContext myFhirContext;
|
||||
private Collection<Object> myProviders;
|
||||
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
|
||||
private Object myServerConformanceProvider;
|
||||
private Map<Class<? extends IResource>, IResourceProvider> myTypeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
||||
private boolean myUseBrowserFriendlyContentTypes;
|
||||
private ISecurityManager securityManager;
|
||||
private Collection<IResourceProvider> myResourceProviders;
|
||||
private ISecurityManager mySecurityManager;
|
||||
private BaseMethodBinding myServerConformanceMethod;
|
||||
private Object myServerConformanceProvider;
|
||||
private String myServerName = "HAPI FHIR Server";
|
||||
private String myServerVersion = VersionUtil.getVersion();
|
||||
private String myServerVersion = VersionUtil.getVersion(); // defaults to
|
||||
// HAPI version
|
||||
private boolean myUseBrowserFriendlyContentTypes;
|
||||
|
||||
public RestfulServer() {
|
||||
myFhirContext = new FhirContext();
|
||||
myServerConformanceProvider=new ServerConformanceProvider(this);
|
||||
myServerConformanceProvider = new ServerConformanceProvider(this);
|
||||
}
|
||||
|
||||
public void addHapiHeader(HttpServletResponse theHttpResponse) {
|
||||
theHttpResponse.addHeader("X-CatchingFhir", "Powered by HAPI FHIR " + VersionUtil.getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the non-resource specific providers which implement method calls
|
||||
* on this server
|
||||
*
|
||||
* @see #getResourceProviders()
|
||||
*/
|
||||
public Collection<Object> getProviders() {
|
||||
return myProviders;
|
||||
}
|
||||
|
||||
public Collection<ResourceBinding> getResourceBindings() {
|
||||
return myResourceNameToProvider.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the resource providers for this server
|
||||
*/
|
||||
public Collection<IResourceProvider> getResourceProviders() {
|
||||
return myResourceProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the security manager, or <code>null</code> if none
|
||||
*/
|
||||
public ISecurityManager getSecurityManager() {
|
||||
return mySecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is
|
||||
* used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can
|
||||
* be changed, or set to <code>null</code> if you do not wish to export a
|
||||
* conformance statement.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
return myServerConformanceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by
|
||||
* the server. This is informational only, but can be helpful to set with
|
||||
* something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(StringDt)
|
||||
*/
|
||||
public String getServerName() {
|
||||
return myServerName;
|
||||
}
|
||||
|
||||
public IResourceProvider getServerProfilesProvider() {
|
||||
return new ServerProfileProvider(getFhirContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported
|
||||
* by the server. This is informational only, but can be helpful to set with
|
||||
* something appropriate.
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return myServerVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
||||
public final void init() throws ServletException {
|
||||
initialize();
|
||||
try {
|
||||
ourLog.info("Initializing HAPI FHIR restful server");
|
||||
|
||||
mySecurityManager = getSecurityManager();
|
||||
if (null == mySecurityManager) {
|
||||
ourLog.warn("No security manager has been provided, requests will not be authenticated!");
|
||||
}
|
||||
|
||||
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
||||
if (resourceProvider != null) {
|
||||
Map<Class<? extends IResource>, IResourceProvider> typeToProvider = new HashMap<Class<? extends IResource>, IResourceProvider>();
|
||||
for (IResourceProvider nextProvider : resourceProvider) {
|
||||
Class<? extends IResource> resourceType = nextProvider.getResourceType();
|
||||
if (resourceType == null) {
|
||||
throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null");
|
||||
}
|
||||
if (typeToProvider.containsKey(resourceType)) {
|
||||
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
||||
}
|
||||
typeToProvider.put(resourceType, nextProvider);
|
||||
}
|
||||
ourLog.info("Got {} resource providers", typeToProvider.size());
|
||||
for (IResourceProvider provider : typeToProvider.values()) {
|
||||
findResourceMethods(provider);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<Object> providers = getProviders();
|
||||
if (providers != null) {
|
||||
for (Object next : providers) {
|
||||
findResourceMethods(next);
|
||||
}
|
||||
}
|
||||
|
||||
findResourceMethods(getServerProfilesProvider());
|
||||
findSystemMethods(getServerConformanceProvider());
|
||||
|
||||
} catch (Exception ex) {
|
||||
ourLog.error("An error occurred while loading request handlers!", ex);
|
||||
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
||||
}
|
||||
|
||||
ourLog.info("A FHIR has been lit on this server");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.OPTIONS, theReq, theResp);
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return myUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.POST, request, response);
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on
|
||||
* this server
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setProviders(Collection<Object> theProviders) {
|
||||
myProviders = theProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on
|
||||
* this server
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
public void setProviders(Object... theProviders) {
|
||||
myProviders = Arrays.asList(theProviders);
|
||||
}
|
||||
|
||||
private void findResourceMethods(IResourceProvider theProvider) throws Exception {
|
||||
/**
|
||||
* Sets the resource providers for this server
|
||||
*/
|
||||
public void setResourceProviders(Collection<IResourceProvider> theResourceProviders) {
|
||||
myResourceProviders = theResourceProviders;
|
||||
}
|
||||
|
||||
Class<? extends IResource> resourceType = theProvider.getResourceType();
|
||||
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceType);
|
||||
/**
|
||||
* Sets the resource providers for this server
|
||||
*/
|
||||
public void setResourceProviders(IResourceProvider... theResourceProviders) {
|
||||
myResourceProviders = Arrays.asList(theResourceProviders);
|
||||
}
|
||||
|
||||
ResourceBinding r = new ResourceBinding();
|
||||
r.setResourceProvider(theProvider);
|
||||
r.setResourceName(definition.getName());
|
||||
myResourceNameToProvider.put(definition.getName(), r);
|
||||
/**
|
||||
* Sets the security manager, or <code>null</code> if none
|
||||
*/
|
||||
public void setSecurityManager(ISecurityManager theSecurityManager) {
|
||||
mySecurityManager = theSecurityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is
|
||||
* used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but this can
|
||||
* be changed, or set to <code>null</code> if you do not wish to export a
|
||||
* conformance statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is
|
||||
* initialized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* Note that this method can only be called prior to
|
||||
* {@link #init() initialization} and will throw an
|
||||
* {@link IllegalStateException} if called after that.
|
||||
*/
|
||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||
if (myFhirContext != null) {
|
||||
throw new IllegalStateException("Server is already started");
|
||||
}
|
||||
myServerConformanceProvider = theServerConformanceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by
|
||||
* the server. This is informational only, but can be helpful to set with
|
||||
* something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(StringDt)
|
||||
*/
|
||||
public void setServerName(String theServerName) {
|
||||
myServerName = theServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported
|
||||
* by the server. This is informational only, but can be helpful to set with
|
||||
* something appropriate.
|
||||
*/
|
||||
public void setServerVersion(String theServerVersion) {
|
||||
myServerVersion = theServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the server will use
|
||||
* browser friendly content-types (instead of standard FHIR ones) when it
|
||||
* detects that the request is coming from a browser instead of a FHIR
|
||||
*/
|
||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
private void findResourceMethods(Object theProvider) throws Exception {
|
||||
|
||||
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
|
||||
|
||||
Class<?> clazz = theProvider.getClass();
|
||||
for (Method m : clazz.getDeclaredMethods()) {
|
||||
if (Modifier.isPublic(m.getModifiers())) {
|
||||
if (Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())) {
|
||||
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
|
||||
|
||||
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(theProvider.getResourceType(), m, myFhirContext, theProvider);
|
||||
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider);
|
||||
if (foundMethodBinding != null) {
|
||||
r.addMethod(foundMethodBinding);
|
||||
|
||||
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(retType);
|
||||
ResourceBinding resourceBinding;
|
||||
if (myResourceNameToProvider.containsKey(definition.getName())) {
|
||||
resourceBinding = myResourceNameToProvider.get(definition.getName());
|
||||
} else {
|
||||
resourceBinding = new ResourceBinding();
|
||||
String resourceName = definition.getName();
|
||||
resourceBinding.setResourceName(resourceName);
|
||||
myResourceNameToProvider.put(resourceName, resourceBinding);
|
||||
}
|
||||
|
||||
resourceBinding.addMethod(foundMethodBinding);
|
||||
ourLog.info(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
|
||||
} else {
|
||||
ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
|
||||
|
@ -134,55 +332,59 @@ public abstract class RestfulServer extends HttpServlet {
|
|||
|
||||
}
|
||||
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
@Override
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
|
||||
// serializing responses from this server, or <code>null</code> (which is
|
||||
// the default) to disable narrative generation.
|
||||
// * Note that this method can only be called before the server is
|
||||
// initialized.
|
||||
// *
|
||||
// * @throws IllegalStateException
|
||||
// * Note that this method can only be called prior to {@link #init()
|
||||
// initialization} and will throw an {@link IllegalStateException} if called
|
||||
// after that.
|
||||
// */
|
||||
// public void setNarrativeGenerator(INarrativeGenerator
|
||||
// theNarrativeGenerator) {
|
||||
// myNarrativeGenerator = theNarrativeGenerator;
|
||||
// }
|
||||
|
||||
public Collection<ResourceBinding> getResourceBindings() {
|
||||
return myResourceNameToProvider.values();
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be overridden to provide one or more resource providers
|
||||
*/
|
||||
public abstract Collection<IResourceProvider> getResourceProviders();
|
||||
|
||||
|
||||
/**
|
||||
* This method should be overridden to provide a security manager instance. By default, returns null.
|
||||
*/
|
||||
public ISecurityManager getSecurityManager() {
|
||||
return null;
|
||||
@Override
|
||||
protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.OPTIONS, theReq, theResp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that
|
||||
* is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but
|
||||
* this can be changed, or set to <code>null</code> if you do not wish
|
||||
* to export a conformance statement.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
return myServerConformanceProvider;
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.POST, request, response);
|
||||
}
|
||||
|
||||
public IResourceProvider getServerProfilesProvider() {
|
||||
return new ServerProfileProvider(getFhirContext());
|
||||
@Override
|
||||
protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
|
||||
}
|
||||
|
||||
protected void handleRequest(SearchMethodBinding.RequestType requestType, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
try {
|
||||
|
||||
if (null != securityManager) {
|
||||
securityManager.authenticate(request);
|
||||
if (null != mySecurityManager) {
|
||||
mySecurityManager.authenticate(request);
|
||||
}
|
||||
|
||||
String resourceName = null;
|
||||
String requestFullPath = StringUtils.defaultString(request.getRequestURI());
|
||||
// String contextPath = StringUtils.defaultString(request.getContextPath());
|
||||
// String contextPath =
|
||||
// StringUtils.defaultString(request.getContextPath());
|
||||
String servletPath = StringUtils.defaultString(request.getServletPath());
|
||||
StringBuffer requestUrl = request.getRequestURL();
|
||||
String servletContextPath = "";
|
||||
|
@ -230,24 +432,17 @@ public abstract class RestfulServer extends HttpServlet {
|
|||
}
|
||||
resourceName = tok.nextToken();
|
||||
|
||||
Object provider=null;
|
||||
ResourceBinding resourceBinding=null;
|
||||
BaseMethodBinding resourceMethod=null;
|
||||
ResourceBinding resourceBinding = null;
|
||||
BaseMethodBinding resourceMethod = null;
|
||||
if ("metadata".equals(resourceName)) {
|
||||
provider = myServerConformanceProvider;
|
||||
if (provider==null) {
|
||||
throw new ResourceNotFoundException("This server does not support 'metadata' query");
|
||||
}
|
||||
resourceMethod = myServerConformanceMethod;
|
||||
} else {
|
||||
resourceBinding = myResourceNameToProvider.get(resourceName);
|
||||
if (resourceBinding == null) {
|
||||
throw new MethodNotFoundException("Unknown resource type '" + resourceName+"' - Server knows how to handle: "+myResourceNameToProvider.keySet());
|
||||
throw new MethodNotFoundException("Unknown resource type '" + resourceName + "' - Server knows how to handle: " + myResourceNameToProvider.keySet());
|
||||
}
|
||||
provider = resourceBinding.getResourceProvider();
|
||||
}
|
||||
|
||||
|
||||
if (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (nextString.startsWith("_")) {
|
||||
|
@ -260,7 +455,7 @@ public abstract class RestfulServer extends HttpServlet {
|
|||
if (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
if (nextString.startsWith("_")) {
|
||||
if (operation !=null) {
|
||||
if (operation != null) {
|
||||
throw new InvalidRequestException("URL Path contains two operations (part beginning with _): " + requestPath);
|
||||
}
|
||||
operation = nextString;
|
||||
|
@ -281,7 +476,6 @@ public abstract class RestfulServer extends HttpServlet {
|
|||
r.setOperation(operation);
|
||||
r.setParameters(params);
|
||||
r.setRequestType(requestType);
|
||||
r.setResourceProvider(provider);
|
||||
r.setInputReader(request.getReader());
|
||||
r.setFhirServerBase(fhirServerBase);
|
||||
r.setCompleteUrl(completeUrl);
|
||||
|
@ -325,140 +519,20 @@ public abstract class RestfulServer extends HttpServlet {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void init() throws ServletException {
|
||||
initialize();
|
||||
try {
|
||||
ourLog.info("Initializing HAPI FHIR restful server");
|
||||
|
||||
securityManager = getSecurityManager();
|
||||
if (null == securityManager) {
|
||||
ourLog.warn("No security manager has been provided, requests will not be authenticated!");
|
||||
}
|
||||
|
||||
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
||||
for (IResourceProvider nextProvider : resourceProvider) {
|
||||
Class<? extends IResource> resourceType = nextProvider.getResourceType();
|
||||
if (resourceType==null) {
|
||||
throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null");
|
||||
}
|
||||
if (myTypeToProvider.containsKey(resourceType)) {
|
||||
throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName());
|
||||
}
|
||||
myTypeToProvider.put(resourceType, nextProvider);
|
||||
}
|
||||
|
||||
ourLog.info("Got {} resource providers", myTypeToProvider.size());
|
||||
|
||||
for (IResourceProvider provider : myTypeToProvider.values()) {
|
||||
findResourceMethods(provider);
|
||||
}
|
||||
|
||||
findResourceMethods(getServerProfilesProvider());
|
||||
findSystemMethods(getServerConformanceProvider());
|
||||
|
||||
} catch (Exception ex) {
|
||||
ourLog.error("An error occurred while loading request handlers!", ex);
|
||||
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
||||
}
|
||||
|
||||
ourLog.info("A FHIR has been lit on this server");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
||||
* This method may be overridden by subclasses to do perform initialization
|
||||
* that needs to be performed prior to the server being used.
|
||||
*/
|
||||
protected void initialize() {
|
||||
// nothing by default
|
||||
}
|
||||
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return myUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when serializing responses from this server, or <code>null</code> (which is the default) to disable narrative generation.
|
||||
// * Note that this method can only be called before the server is initialized.
|
||||
// *
|
||||
// * @throws IllegalStateException
|
||||
// * Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
|
||||
// */
|
||||
// public void setNarrativeGenerator(INarrativeGenerator theNarrativeGenerator) {
|
||||
// myNarrativeGenerator = theNarrativeGenerator;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that
|
||||
* is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the {@link ServerConformanceProvider} is used, but
|
||||
* this can be changed, or set to <code>null</code> if you do not wish
|
||||
* to export a conformance statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is initialized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
|
||||
*/
|
||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||
if (myFhirContext!=null) {
|
||||
throw new IllegalStateException("Server is already started");
|
||||
}
|
||||
myServerConformanceProvider = theServerConformanceProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
|
||||
* instead of a FHIR
|
||||
*/
|
||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
public enum NarrativeModeEnum {
|
||||
NORMAL,
|
||||
ONLY,
|
||||
SUPPRESS;
|
||||
|
||||
NORMAL, ONLY, SUPPRESS;
|
||||
|
||||
public static NarrativeModeEnum valueOfCaseInsensitive(String theCode) {
|
||||
return valueOf(NarrativeModeEnum.class, theCode.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This
|
||||
* is informational only, but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(StringDt)
|
||||
*/
|
||||
public String getServerName() {
|
||||
return myServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This
|
||||
* is informational only, but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(StringDt)
|
||||
*/
|
||||
public void setServerName(String theServerName) {
|
||||
myServerName = theServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This
|
||||
* is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerVersion(String theServerVersion) {
|
||||
myServerVersion = theServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This
|
||||
* is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return myServerVersion;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,10 @@ import javax.servlet.ServletException;
|
|||
|
||||
public class ConfigurationException extends ServletException {
|
||||
|
||||
public ConfigurationException(String theString) {
|
||||
super(theString);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import java.util.Set;
|
|||
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.Rest;
|
||||
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
|
||||
|
@ -57,8 +56,8 @@ public class ServerConformanceProvider {
|
|||
Set<RestfulOperationTypeEnum> resourceOps = new HashSet<RestfulOperationTypeEnum>();
|
||||
RestResource resource = rest.addResource();
|
||||
|
||||
Class<? extends IResource> resourceType = next.getResourceProvider().getResourceType();
|
||||
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceType);
|
||||
String resourceName = next.getResourceName();
|
||||
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
||||
resource.getType().setValue(def.getName());
|
||||
resource.getProfile().setId(new IdDt(def.getResourceProfile()));
|
||||
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||
|
||||
public class NonResourceProviderServerTest {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(NonResourceProviderServerTest.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = RandomServerPortProvider.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
ourCtx = new FhirContext(Patient.class);
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
ServletHolder servletHolder = new ServletHolder(new DummyRestfulServer());
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/fhir/context/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() throws Exception {
|
||||
ourServer.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByParamIdentifier() throws Exception {
|
||||
|
||||
String baseUri = "http://localhost:" + ourPort + "/fhir/context";
|
||||
String uri = baseUri + "/Patient?identifier=urn:hapitest:mrns%7C00001";
|
||||
HttpGet httpGet = new HttpGet(uri);
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
ourLog.info("Response was:\n{}", responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
|
||||
|
||||
assertEquals(1, bundle.getEntries().size());
|
||||
|
||||
Patient patient = (Patient) bundle.getEntries().get(0).getResource();
|
||||
assertEquals("PatientOne", patient.getName().get(0).getGiven().get(0).getValue());
|
||||
|
||||
assertEquals(uri, bundle.getLinkSelf().getValue());
|
||||
assertEquals(baseUri, bundle.getLinkBase().getValue());
|
||||
}
|
||||
|
||||
public static class DummyRestfulServer extends RestfulServer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public DummyRestfulServer() {
|
||||
setProviders(new DummyProvider());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public static class DummyProvider {
|
||||
|
||||
public Map<String, Patient> getIdToPatient() {
|
||||
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier();
|
||||
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
|
||||
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
patient.getIdentifier().get(0).setValue("00001");
|
||||
patient.addName();
|
||||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addGiven("PatientOne");
|
||||
patient.getGender().setText("M");
|
||||
idToPatient.put("1", patient);
|
||||
}
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.getIdentifier().add(new IdentifierDt());
|
||||
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
|
||||
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
|
||||
patient.getIdentifier().get(0).setValue("00002");
|
||||
patient.getName().add(new HumanNameDt());
|
||||
patient.getName().get(0).addFamily("Test");
|
||||
patient.getName().get(0).addGiven("PatientTwo");
|
||||
patient.getGender().setText("F");
|
||||
idToPatient.put("2", patient);
|
||||
}
|
||||
return idToPatient;
|
||||
}
|
||||
|
||||
@Search(type = Patient.class)
|
||||
public Patient findPatient(@RequiredParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier) {
|
||||
for (Patient next : getIdToPatient().values()) {
|
||||
for (IdentifierDt nextId : next.getIdentifier()) {
|
||||
if (nextId.matchesSystemAndValue(theIdentifier)) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the resource by its identifier
|
||||
*
|
||||
* @param theId
|
||||
* The resource identity
|
||||
* @return The resource
|
||||
*/
|
||||
@Read(type = Patient.class)
|
||||
public Patient getPatientById(@IdParam IdDt theId) {
|
||||
return getIdToPatient().get(theId.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -29,7 +29,7 @@ public class ResourceMethodTest {
|
|||
|
||||
@Before
|
||||
public void before() throws NoSuchMethodException, SecurityException {
|
||||
rm = new SearchMethodBinding(Patient.class, ResourceMethodTest.class.getMethod("foo"), null, new FhirContext());
|
||||
rm = new SearchMethodBinding(Patient.class, ResourceMethodTest.class.getMethod("foo"), null, new FhirContext(), null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.model.api.BaseResource;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ConfigurationException;
|
||||
|
||||
public class ServerInvalidDefinitionTest {
|
||||
|
||||
@Test(expected=ConfigurationException.class)
|
||||
public void testNonInstantiableTypeForResourceProvider() throws ServletException {
|
||||
RestfulServer srv = new RestfulServer();
|
||||
srv.setResourceProviders(new NonInstantiableTypeForResourceProvider());
|
||||
srv.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal, should initialize properly
|
||||
*/
|
||||
@Test()
|
||||
public void testBaseline() throws ServletException {
|
||||
RestfulServer srv = new RestfulServer();
|
||||
srv.setResourceProviders(new InstantiableTypeForResourceProvider());
|
||||
srv.init();
|
||||
}
|
||||
|
||||
|
||||
private static class NonInstantiableTypeForResourceProvider implements IResourceProvider
|
||||
{
|
||||
|
||||
@Override
|
||||
public Class<? extends IResource> getResourceType() {
|
||||
return BaseResource.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Read
|
||||
public BaseResource read(@IdParam IdDt theId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class InstantiableTypeForResourceProvider implements IResourceProvider
|
||||
{
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Read
|
||||
public Patient read(@IdParam IdDt theId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
22
pom.xml
22
pom.xml
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>org.sonatype.oss</groupId>
|
||||
<artifactId>oss-parent</artifactId>
|
||||
<version>6</version>
|
||||
<version>7</version>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
@ -85,6 +85,26 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
<plugins>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>1.5</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>sign-artifacts</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
|
||||
</build>
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue