mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-09 14:33:32 +00:00
Merge branch 'master' of git://github.com/petervanhoute/hapi-fhir into petervanhoute-master
This commit is contained in:
commit
e6b47855e6
@ -40,6 +40,24 @@
|
|||||||
<artifactId>javax.servlet-api</artifactId>
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- jax rs server -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
|
||||||
|
<version>1.4-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.ws.rs</groupId>
|
||||||
|
<artifactId>javax.ws.rs-api</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.ejb</groupId>
|
||||||
|
<artifactId>ejb-api</artifactId>
|
||||||
|
<version>3.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<reporting>
|
<reporting>
|
||||||
|
41
examples/src/main/java/example/JaxRsConformanceProvider.java
Normal file
41
examples/src/main/java/example/JaxRsConformanceProvider.java
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package example;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.ejb.EJB;
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conformance Rest Service
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte
|
||||||
|
*/
|
||||||
|
// START SNIPPET: jax-rs-conformance
|
||||||
|
@Path("")
|
||||||
|
@Stateless
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class JaxRsConformanceProvider extends AbstractJaxRsConformanceProvider {
|
||||||
|
|
||||||
|
@EJB
|
||||||
|
private JaxRsPatientRestProvider provider;
|
||||||
|
|
||||||
|
public JaxRsConformanceProvider() {
|
||||||
|
super("My Server Version", "My Server Description", "My Server Name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
|
||||||
|
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
|
||||||
|
map.put(JaxRsConformanceProvider.class, this);
|
||||||
|
map.put(JaxRsPatientRestProvider.class, provider);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// END SNIPPET: jax-rs-conformance
|
82
examples/src/main/java/example/JaxRsPatientRestProvider.java
Normal file
82
examples/src/main/java/example/JaxRsPatientRestProvider.java
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package example;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
|
||||||
|
import javax.ejb.Local;
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A demo JaxRs Patient Rest Provider
|
||||||
|
*/
|
||||||
|
@Local
|
||||||
|
@Stateless
|
||||||
|
// START SNIPPET: jax-rs-provider-construction
|
||||||
|
@Path("/Patient")
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Patient> {
|
||||||
|
|
||||||
|
public JaxRsPatientRestProvider() {
|
||||||
|
super(JaxRsPatientRestProvider.class);
|
||||||
|
}
|
||||||
|
// END SNIPPET: jax-rs-provider-construction
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional) {
|
||||||
|
// create the patient ...
|
||||||
|
return new MethodOutcome(new IdDt(1L)).setCreated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// START SNIPPET: jax-rs-provider-operation
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/$someCustomOperation")
|
||||||
|
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
|
||||||
|
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
|
||||||
|
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
|
||||||
|
@OperationParam(name = "return", type = StringDt.class) })
|
||||||
|
public Parameters someCustomOperation(@IdParam IdDt myId, @OperationParam(name = "dummy") StringDt dummyInput) {
|
||||||
|
Parameters parameters = new Parameters();
|
||||||
|
parameters.addParameter().setName("return").setValue(new StringDt("My Dummy Result"));
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
// END SNIPPET: jax-rs-provider-operation
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{id}/$someCustomOperation")
|
||||||
|
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
|
||||||
|
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
|
||||||
|
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INTERNAL API (do not use): REST method annotation for the method called when a client requests a page.
|
||||||
|
* <p>
|
||||||
|
* This annotation is currently intended as an internal part of HAPI's API. At some point we
|
||||||
|
* will hopefully provide a way to create alternate implementations of the GetPage mewthod. If
|
||||||
|
* you would like to help out or have ideas, please get in touch!
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
@Target(value= ElementType.METHOD)
|
||||||
|
@Retention(value=RetentionPolicy.RUNTIME)
|
||||||
|
public @interface GetPage {
|
||||||
|
// nothing
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter annotation which specifies the parameter to receive the ID of the page
|
||||||
|
* being requested. Parameter must be of type {@link String}
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.PARAMETER)
|
||||||
|
public @interface PageIdParam {
|
||||||
|
// nothing
|
||||||
|
}
|
@ -45,6 +45,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|||||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
@ -154,8 +155,8 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||||
Object[] params = createParametersForServerRequest(theRequest, null);
|
Object[] params = createParametersForServerRequest(theRequest);
|
||||||
|
|
||||||
params[myIdParamIndex] = theRequest.getId();
|
params[myIdParamIndex] = theRequest.getId();
|
||||||
|
|
||||||
@ -164,7 +165,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
|||||||
}
|
}
|
||||||
|
|
||||||
IParser parser = createAppropriateParserForParsingServerRequest(theRequest);
|
IParser parser = createAppropriateParserForParsingServerRequest(theRequest);
|
||||||
Reader reader = theRequest.getServletRequest().getReader();
|
Reader reader = theRequest.getReader();
|
||||||
try {
|
try {
|
||||||
TagList tagList = parser.parseTagList(reader);
|
TagList tagList = parser.parseTagList(reader);
|
||||||
params[myTagListParamIndex] = tagList;
|
params[myTagListParamIndex] = tagList;
|
||||||
@ -175,21 +176,13 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
|||||||
|
|
||||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||||
boolean continueProcessing = next.outgoingResponse(theRequest, theRequest.getServletRequest(), theRequest.getServletResponse());
|
boolean continueProcessing = next.outgoingResponse(theRequest);
|
||||||
if (!continueProcessing) {
|
if (!continueProcessing) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpServletResponse response = theRequest.getServletResponse();
|
return theRequest.getResponse().returnResponse(null, Constants.STATUS_HTTP_200_OK, false, null, null);
|
||||||
response.setContentType(Constants.CT_TEXT);
|
|
||||||
response.setStatus(Constants.STATUS_HTTP_200_OK);
|
|
||||||
response.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
|
|
||||||
|
|
||||||
theServer.addHeadersToResponse(response);
|
|
||||||
|
|
||||||
PrintWriter writer = response.getWriter();
|
|
||||||
writer.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.annotation.AddTags;
|
|||||||
import ca.uhn.fhir.rest.annotation.Create;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.Delete;
|
import ca.uhn.fhir.rest.annotation.Delete;
|
||||||
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetPage;
|
||||||
import ca.uhn.fhir.rest.annotation.GetTags;
|
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||||
import ca.uhn.fhir.rest.annotation.History;
|
import ca.uhn.fhir.rest.annotation.History;
|
||||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||||
@ -69,7 +70,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
|
|||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
@ -81,15 +82,12 @@ import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
|||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
|
||||||
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
|
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
|
||||||
/**
|
|
||||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
|
||||||
*/
|
|
||||||
private static volatile IRequestReader ourRequestReader;
|
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
private Method myMethod;
|
private Method myMethod;
|
||||||
private List<IParameter> myParameters;
|
private List<IParameter> myParameters;
|
||||||
@ -131,7 +129,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected IParser createAppropriateParserForParsingServerRequest(RequestDetails theRequest) {
|
protected IParser createAppropriateParserForParsingServerRequest(RequestDetails theRequest) {
|
||||||
String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type");
|
String contentTypeHeader = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||||
EncodingEnum encoding;
|
EncodingEnum encoding;
|
||||||
if (isBlank(contentTypeHeader)) {
|
if (isBlank(contentTypeHeader)) {
|
||||||
encoding = EncodingEnum.XML;
|
encoding = EncodingEnum.XML;
|
||||||
@ -151,14 +149,14 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object[] createParametersForServerRequest(RequestDetails theRequest, byte[] theRequestContents) {
|
protected Object[] createParametersForServerRequest(RequestDetails theRequest) {
|
||||||
Object[] params = new Object[getParameters().size()];
|
Object[] params = new Object[getParameters().size()];
|
||||||
for (int i = 0; i < getParameters().size(); i++) {
|
for (int i = 0; i < getParameters().size(); i++) {
|
||||||
IParameter param = getParameters().get(i);
|
IParameter param = getParameters().get(i);
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theRequestContents, this);
|
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, this);
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
@ -251,9 +249,9 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||||||
|
|
||||||
public abstract BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
|
public abstract BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
|
||||||
|
|
||||||
public abstract void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException;
|
public abstract Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException;
|
||||||
|
|
||||||
protected final Object invokeServerMethod(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) {
|
protected final Object invokeServerMethod(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) {
|
||||||
// Handle server action interceptors
|
// Handle server action interceptors
|
||||||
RestOperationTypeEnum operationType = getRestOperationType(theRequest);
|
RestOperationTypeEnum operationType = getRestOperationType(theRequest);
|
||||||
if (operationType != null) {
|
if (operationType != null) {
|
||||||
@ -293,41 +291,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||||||
return mySupportsConditionalMultiple;
|
return mySupportsConditionalMultiple;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected byte[] loadRequestContents(RequestDetails theRequest) throws IOException {
|
|
||||||
/*
|
|
||||||
* This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on servlet-api in clients since there is no point. So we dynamically load a class
|
|
||||||
* that does the servlet processing in servers. Down the road it may make sense to just split the method binding classes into server and client versions, but this isn't actually a huge deal I
|
|
||||||
* don't think.
|
|
||||||
*/
|
|
||||||
IRequestReader reader = ourRequestReader;
|
|
||||||
if (reader == null) {
|
|
||||||
try {
|
|
||||||
Class.forName("javax.servlet.ServletInputStream");
|
|
||||||
String className = BaseMethodBinding.class.getName() + "$" + "ActiveRequestReader";
|
|
||||||
try {
|
|
||||||
reader = (IRequestReader) Class.forName(className).newInstance();
|
|
||||||
} catch (Exception e1) {
|
|
||||||
throw new ConfigurationException("Failed to instantiate class " + className, e1);
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
String className = BaseMethodBinding.class.getName() + "$" + "InactiveRequestReader";
|
|
||||||
try {
|
|
||||||
reader = (IRequestReader) Class.forName(className).newInstance();
|
|
||||||
} catch (Exception e1) {
|
|
||||||
throw new ConfigurationException("Failed to instantiate class " + className, e1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ourRequestReader = reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
InputStream inputStream = reader.getInputStream(theRequest);
|
|
||||||
byte[] requestContents = IOUtils.toByteArray(inputStream);
|
|
||||||
|
|
||||||
theRequest.setRawRequest(requestContents);
|
|
||||||
|
|
||||||
return requestContents;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subclasses may override this method (but should also call super.{@link #populateActionRequestDetailsForInterceptor(RequestDetails, ActionRequestDetails, Object[])} to provide method specifics to the
|
* Subclasses may override this method (but should also call super.{@link #populateActionRequestDetailsForInterceptor(RequestDetails, ActionRequestDetails, Object[])} to provide method specifics to the
|
||||||
* interceptors.
|
* interceptors.
|
||||||
@ -421,12 +384,17 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||||||
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
||||||
Transaction transaction = theMethod.getAnnotation(Transaction.class);
|
Transaction transaction = theMethod.getAnnotation(Transaction.class);
|
||||||
Operation operation = theMethod.getAnnotation(Operation.class);
|
Operation operation = theMethod.getAnnotation(Operation.class);
|
||||||
|
GetPage getPage = theMethod.getAnnotation(GetPage.class);
|
||||||
|
|
||||||
// ** if you add another annotation above, also add it to the next line:
|
// ** if you add another annotation above, also add it to the next line:
|
||||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction, operation)) {
|
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction, operation, getPage)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (getPage != null) {
|
||||||
|
return new PageMethodBinding(theContext, theMethod);
|
||||||
|
}
|
||||||
|
|
||||||
Class<? extends IBaseResource> returnType;
|
Class<? extends IBaseResource> returnType;
|
||||||
|
|
||||||
Class<? extends IBaseResource> returnTypeFromRp = null;
|
Class<? extends IBaseResource> returnTypeFromRp = null;
|
||||||
@ -641,19 +609,19 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
* @see ServletRequestDetails#getByteStreamRequestContents()
|
||||||
*/
|
*/
|
||||||
static class ActiveRequestReader implements IRequestReader {
|
public static class ActiveRequestReader implements IRequestReader {
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream(RequestDetails theRequestDetails) throws IOException {
|
public InputStream getInputStream(RequestDetails theRequestDetails) throws IOException {
|
||||||
return theRequestDetails.getServletRequest().getInputStream();
|
return theRequestDetails.getInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
* @see ServletRequestDetails#getByteStreamRequestContents()
|
||||||
*/
|
*/
|
||||||
static class InactiveRequestReader implements IRequestReader {
|
public static class InactiveRequestReader implements IRequestReader {
|
||||||
@Override
|
@Override
|
||||||
public InputStream getInputStream(RequestDetails theRequestDetails) {
|
public InputStream getInputStream(RequestDetails theRequestDetails) {
|
||||||
throw new IllegalStateException("The servlet-api JAR is not found on the classpath. Please check that this library is available.");
|
throw new IllegalStateException("The servlet-api JAR is not found on the classpath. Please check that this library is available.");
|
||||||
@ -661,9 +629,9 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
* @see ServletRequestDetails#getByteStreamRequestContents()
|
||||||
*/
|
*/
|
||||||
private static interface IRequestReader {
|
public static interface IRequestReader {
|
||||||
InputStream getInputStream(RequestDetails theRequestDetails) throws IOException;
|
InputStream getInputStream(RequestDetails theRequestDetails) throws IOException;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import java.io.IOException;
|
|||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -41,9 +42,11 @@ import ca.uhn.fhir.parser.IParser;
|
|||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
@ -68,24 +71,6 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation) {
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
b.append(theRequest.getFhirServerBase());
|
|
||||||
b.append('/');
|
|
||||||
b.append(getResourceName());
|
|
||||||
b.append('/');
|
|
||||||
b.append(response.getId().getIdPart());
|
|
||||||
if (response.getId().hasVersionIdPart()) {
|
|
||||||
b.append("/" + Constants.PARAM_HISTORY + "/");
|
|
||||||
b.append(response.getId().getVersionIdPart());
|
|
||||||
} else if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) {
|
|
||||||
b.append("/" + Constants.PARAM_HISTORY + "/");
|
|
||||||
b.append(response.getVersionId().getValue());
|
|
||||||
}
|
|
||||||
theResponse.addHeader(headerLocation, b.toString());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams);
|
protected abstract void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -137,21 +122,18 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||||
byte[] requestContents = loadRequestContents(theRequest);
|
|
||||||
|
|
||||||
// if (requestContainsResource()) {
|
// if (requestContainsResource()) {
|
||||||
// requestContents = parseIncomingServerResource(theRequest);
|
// requestContents = parseIncomingServerResource(theRequest);
|
||||||
// } else {
|
// } else {
|
||||||
// requestContents = null;
|
// requestContents = null;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Object[] params = createParametersForServerRequest(theRequest, requestContents);
|
Object[] params = createParametersForServerRequest(theRequest);
|
||||||
addParametersForServerRequest(theRequest, params);
|
addParametersForServerRequest(theRequest, params);
|
||||||
|
|
||||||
HttpServletResponse servletResponse = theRequest.getServletResponse();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* No need to catch nd handle exceptions here, we already handle them one level up
|
* No need to catch nd handle exceptions here, we already handle them one level up
|
||||||
@ -182,13 +164,16 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||||||
IBaseResource resource = response != null ? response.getResource() : null;
|
IBaseResource resource = response != null ? response.getResource() : null;
|
||||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||||
boolean continueProcessing = next.outgoingResponse(theRequest, outcome, theRequest.getServletRequest(), theRequest.getServletResponse());
|
boolean continueProcessing = next.outgoingResponse(theRequest, outcome);
|
||||||
if (!continueProcessing) {
|
if (!continueProcessing) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean allowPrefer = false;
|
return returnResponse(theRequest, response, outcome, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getOperationStatus(MethodOutcome response) {
|
||||||
switch (getRestOperationType()) {
|
switch (getRestOperationType()) {
|
||||||
case CREATE:
|
case CREATE:
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
@ -196,23 +181,17 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||||||
+ " returned null, which is not allowed for create operation");
|
+ " returned null, which is not allowed for create operation");
|
||||||
}
|
}
|
||||||
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
|
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
return Constants.STATUS_HTTP_201_CREATED;
|
||||||
} else {
|
} else {
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
return Constants.STATUS_HTTP_200_OK;
|
||||||
}
|
}
|
||||||
addContentLocationHeaders(theRequest, servletResponse, response);
|
|
||||||
allowPrefer = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case UPDATE:
|
case UPDATE:
|
||||||
if (response == null || response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
|
if (response == null || response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
return Constants.STATUS_HTTP_200_OK;
|
||||||
} else {
|
} else {
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
return Constants.STATUS_HTTP_201_CREATED;
|
||||||
}
|
}
|
||||||
addContentLocationHeaders(theRequest, servletResponse, response);
|
|
||||||
allowPrefer = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case VALIDATE:
|
case VALIDATE:
|
||||||
case DELETE:
|
case DELETE:
|
||||||
@ -221,21 +200,29 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||||||
if (isReturnVoid() == false) {
|
if (isReturnVoid() == false) {
|
||||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
||||||
}
|
}
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
|
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||||
} else {
|
} else {
|
||||||
if (response.getOperationOutcome() == null) {
|
if (response.getOperationOutcome() == null) {
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
|
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||||
} else {
|
} else {
|
||||||
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
return Constants.STATUS_HTTP_200_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
theServer.addHeadersToResponse(servletResponse);
|
private Object returnResponse(RequestDetails theRequest, MethodOutcome response, IBaseResource originalOutcome, IBaseResource resource) throws IOException {
|
||||||
|
boolean allowPrefer = false;
|
||||||
|
int operationStatus = getOperationStatus(response);
|
||||||
|
IBaseResource outcome = originalOutcome;
|
||||||
|
|
||||||
|
if(EnumSet.of(RestOperationTypeEnum.CREATE, RestOperationTypeEnum.UPDATE).contains(getRestOperationType())) {
|
||||||
|
allowPrefer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (resource != null && allowPrefer) {
|
if (resource != null && allowPrefer) {
|
||||||
String prefer = theRequest.getServletRequest().getHeader(Constants.HEADER_PREFER);
|
String prefer = theRequest.getHeader(Constants.HEADER_PREFER);
|
||||||
PreferReturnEnum preferReturn = RestfulServerUtils.parsePreferHeader(prefer);
|
PreferReturnEnum preferReturn = RestfulServerUtils.parsePreferHeader(prefer);
|
||||||
if (preferReturn != null) {
|
if (preferReturn != null) {
|
||||||
if (preferReturn == PreferReturnEnum.REPRESENTATION) {
|
if (preferReturn == PreferReturnEnum.REPRESENTATION) {
|
||||||
@ -244,31 +231,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (outcome != null) {
|
return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
|
||||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
|
||||||
servletResponse.setContentType(encoding.getResourceContentType());
|
|
||||||
Writer writer = servletResponse.getWriter();
|
|
||||||
IParser parser = encoding.newParser(getContext());
|
|
||||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theServer, theRequest));
|
|
||||||
try {
|
|
||||||
parser.encodeResourceToWriter(outcome, writer);
|
|
||||||
} finally {
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
servletResponse.setContentType(Constants.CT_TEXT_WITH_UTF8);
|
|
||||||
Writer writer = servletResponse.getWriter();
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
// getMethod().in
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addContentLocationHeaders(RequestDetails theRequest, HttpServletResponse servletResponse, MethodOutcome response) {
|
|
||||||
if (response != null && response.getId() != null) {
|
|
||||||
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION);
|
|
||||||
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_CONTENT_LOCATION);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReturnVoid() {
|
public boolean isReturnVoid() {
|
||||||
|
@ -29,7 +29,6 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||||
|
@ -35,8 +35,6 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
@ -55,8 +53,8 @@ import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
|||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
|
||||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
@ -234,9 +232,10 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||||
|
|
||||||
byte[] requestContents = loadRequestContents(theRequest);
|
// byte[] requestContents = loadRequestContents(theRequest);
|
||||||
|
byte[] requestContents = null;
|
||||||
|
|
||||||
final ResourceOrDstu1Bundle responseObject = invokeServer(theServer, theRequest, requestContents);
|
final ResourceOrDstu1Bundle responseObject = invokeServer(theServer, theRequest, requestContents);
|
||||||
|
|
||||||
@ -245,19 +244,20 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||||||
|
|
||||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||||
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getResource(), theRequest.getServletRequest(), theRequest.getServletResponse());
|
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getResource());
|
||||||
if (!continueProcessing) {
|
if (!continueProcessing) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||||
HttpServletResponse response = theRequest.getServletResponse();
|
|
||||||
RestfulServerUtils.streamResponseAsResource(theServer, response, responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(),
|
return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(),
|
||||||
isAddContentLocationHeader(), theRequest);
|
isAddContentLocationHeader());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Is this request coming from a browser
|
// Is this request coming from a browser
|
||||||
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
String uaHeader = theRequest.getHeader("user-agent");
|
||||||
boolean requestIsBrowser = false;
|
boolean requestIsBrowser = false;
|
||||||
if (uaHeader != null && uaHeader.contains("Mozilla")) {
|
if (uaHeader != null && uaHeader.contains("Mozilla")) {
|
||||||
requestIsBrowser = true;
|
requestIsBrowser = true;
|
||||||
@ -265,26 +265,24 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||||||
|
|
||||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||||
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getDstu1Bundle(), theRequest.getServletRequest(), theRequest.getServletResponse());
|
boolean continueProcessing = next.outgoingResponse(theRequest, responseObject.getDstu1Bundle());
|
||||||
if (!continueProcessing) {
|
if (!continueProcessing) {
|
||||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpServletResponse response = theRequest.getServletResponse();
|
return theRequest.getResponse().streamResponseAsBundle(responseObject.getDstu1Bundle(), summaryMode, theRequest.isRespondGzip(), requestIsBrowser);
|
||||||
RestfulServerUtils.streamResponseAsBundle(theServer, response, responseObject.getDstu1Bundle(), theRequest.getFhirServerBase(), summaryMode, theRequest.isRespondGzip(), requestIsBrowser,
|
|
||||||
theRequest);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResourceOrDstu1Bundle invokeServer(RestfulServer theServer, RequestDetails theRequest, byte[] requestContents) {
|
public ResourceOrDstu1Bundle invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, byte[] requestContents) {
|
||||||
// Method params
|
// Method params
|
||||||
Object[] params = new Object[getParameters().size()];
|
Object[] params = new Object[getParameters().size()];
|
||||||
for (int i = 0; i < getParameters().size(); i++) {
|
for (int i = 0; i < getParameters().size(); i++) {
|
||||||
IParameter param = getParameters().get(i);
|
IParameter param = getParameters().get(i);
|
||||||
if (param != null) {
|
if (param != null) {
|
||||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, requestContents, this);
|
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +298,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||||||
/*
|
/*
|
||||||
* Figure out the self-link for this request
|
* Figure out the self-link for this request
|
||||||
*/
|
*/
|
||||||
String serverBase = theServer.getServerBaseForRequest(theRequest.getServletRequest());
|
String serverBase = theRequest.getServerBaseForRequest();
|
||||||
String linkSelf;
|
String linkSelf;
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(serverBase);
|
b.append(serverBase);
|
||||||
@ -349,6 +347,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||||||
|
|
||||||
responseObject = new ResourceOrDstu1Bundle(resource);
|
responseObject = new ResourceOrDstu1Bundle(resource);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Set<Include> includes = getRequestIncludesFromParams(params);
|
Set<Include> includes = getRequestIncludesFromParams(params);
|
||||||
|
|
||||||
@ -356,14 +355,20 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||||||
if (count == null) {
|
if (count == null) {
|
||||||
count = result.preferredPageSize();
|
count = result.preferredPageSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
|
||||||
|
if (offsetI == null || offsetI < 0) {
|
||||||
|
offsetI = 0;
|
||||||
|
}
|
||||||
|
int start = Math.max(0, Math.min(offsetI, result.size() - 1));
|
||||||
|
|
||||||
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
||||||
|
|
||||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest(), theServer.getDefaultResponseEncoding());
|
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
|
||||||
EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) ? responseEncoding : null;
|
EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) ? responseEncoding : null;
|
||||||
|
|
||||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||||
bundleFactory.initializeBundleFromBundleProvider(theServer, result, linkEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, 0, count, null, getResponseBundleType(), includes);
|
bundleFactory.initializeBundleFromBundleProvider(theServer, result, linkEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, null, getResponseBundleType(), includes);
|
||||||
Bundle bundle = bundleFactory.getDstu1Bundle();
|
Bundle bundle = bundleFactory.getDstu1Bundle();
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
responseObject = new ResourceOrDstu1Bundle(bundle);
|
responseObject = new ResourceOrDstu1Bundle(bundle);
|
||||||
@ -393,7 +398,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||||||
return responseObject;
|
return responseObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
public abstract Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should the response include a Content-Location header. Search method bunding (and any others?) may override this to disable the content-location, since it doesn't make sense
|
* Should the response include a Content-Location header. Search method bunding (and any others?) may override this to disable the content-location, since it doesn't make sense
|
||||||
|
@ -61,10 +61,10 @@ class ConditionalParamBinder implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
|
|
||||||
if (myOperationType == RestOperationTypeEnum.CREATE) {
|
if (myOperationType == RestOperationTypeEnum.CREATE) {
|
||||||
String retVal = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_NONE_EXIST);
|
String retVal = theRequest.getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||||
if (isBlank(retVal)) {
|
if (isBlank(retVal)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
|||||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
@ -72,7 +73,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||||
IBaseResource conf = (IBaseResource) invokeServerMethod(theServer, theRequest, theMethodParams);
|
IBaseResource conf = (IBaseResource) invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||||
return new SimpleBundleProvider(conf);
|
return new SimpleBundleProvider(conf);
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ public class CountParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_COUNT);
|
String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_COUNT);
|
||||||
if (sinceParams != null) {
|
if (sinceParams != null) {
|
||||||
if (sinceParams.length > 0) {
|
if (sinceParams.length > 0) {
|
||||||
|
@ -37,7 +37,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
if (myIdParamIndex != null) {
|
if (myIdParamIndex != null) {
|
||||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ public class DynamicSearchParameter implements IParameter {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
SearchParameterMap retVal = new SearchParameterMap();
|
SearchParameterMap retVal = new SearchParameterMap();
|
||||||
|
|
||||||
for (String next : theRequest.getParameters().keySet()) {
|
for (String next : theRequest.getParameters().keySet()) {
|
||||||
|
@ -71,7 +71,7 @@ public class ElementsParameter implements IParameter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
Set<String> value = getElementsValueOrNull(theRequest);
|
Set<String> value = getElementsValueOrNull(theRequest);
|
||||||
if (value == null || value.isEmpty()) {
|
if (value == null || value.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -21,15 +21,12 @@ package ca.uhn.fhir.rest.method;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
@ -44,10 +41,8 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
|||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
@ -168,8 +163,8 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||||
Object[] params = createParametersForServerRequest(theRequest, null);
|
Object[] params = createParametersForServerRequest(theRequest);
|
||||||
|
|
||||||
if (myIdParamIndex != null) {
|
if (myIdParamIndex != null) {
|
||||||
params[myIdParamIndex] = theRequest.getId();
|
params[myIdParamIndex] = theRequest.getId();
|
||||||
@ -182,29 +177,13 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||||||
|
|
||||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||||
boolean continueProcessing = next.outgoingResponse(theRequest, resp, theRequest.getServletRequest(), theRequest.getServletResponse());
|
boolean continueProcessing = next.outgoingResponse(theRequest, resp);
|
||||||
if (!continueProcessing) {
|
if (!continueProcessing) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
return theRequest.getResponse().returnResponse(ParseAction.create(resp), Constants.STATUS_HTTP_200_OK, false, null, null);
|
||||||
|
|
||||||
HttpServletResponse response = theRequest.getServletResponse();
|
|
||||||
response.setContentType(responseEncoding.getResourceContentType());
|
|
||||||
response.setStatus(Constants.STATUS_HTTP_200_OK);
|
|
||||||
response.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
|
|
||||||
|
|
||||||
theServer.addHeadersToResponse(response);
|
|
||||||
|
|
||||||
IParser parser = responseEncoding.newParser(getContext());
|
|
||||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theServer, theRequest));
|
|
||||||
PrintWriter writer = response.getWriter();
|
|
||||||
try {
|
|
||||||
parser.encodeTagListToWriter(resp, writer);
|
|
||||||
} finally {
|
|
||||||
writer.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
@ -155,7 +154,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
if (myIdParamIndex != null) {
|
if (myIdParamIndex != null) {
|
||||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public interface IParameter {
|
|||||||
* @param theMethodBinding TODO
|
* @param theMethodBinding TODO
|
||||||
* @return Returns the argument object as it will be passed to the {@link IResourceProvider} method.
|
* @return Returns the argument object as it will be passed to the {@link IResourceProvider} method.
|
||||||
*/
|
*/
|
||||||
Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException;
|
Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException;
|
||||||
|
|
||||||
void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType);
|
void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType);
|
||||||
|
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
public interface IRestfulHeader {
|
||||||
|
|
||||||
|
}
|
@ -39,7 +39,7 @@ class NullParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
// nothing
|
// nothing
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
|||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||||
@ -246,7 +246,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||||
if (theRequest.getRequestType() == RequestTypeEnum.POST) {
|
if (theRequest.getRequestType() == RequestTypeEnum.POST) {
|
||||||
// always ok
|
// always ok
|
||||||
} else if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
} else if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||||
|
@ -24,7 +24,6 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -156,7 +155,7 @@ public class OperationParameter implements IParameter {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
List<Object> matchingParamValues = new ArrayList<Object>();
|
List<Object> matchingParamValues = new ArrayList<Object>();
|
||||||
|
|
||||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||||
|
@ -0,0 +1,178 @@
|
|||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.model.api.Include;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.method.BaseResourceReturningMethodBinding.ResourceOrDstu1Bundle;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
|
import net.sourceforge.cobertura.CoverageIgnore;
|
||||||
|
|
||||||
|
public class PageMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
|
public PageMethodBinding(FhirContext theContext, Method theMethod) {
|
||||||
|
super(null, theMethod, theContext, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PageMethodBinding.class);
|
||||||
|
|
||||||
|
public IBaseResource provider() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BundleTypeEnum getResponseBundleType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReturnTypeEnum getReturnType() {
|
||||||
|
return ReturnTypeEnum.BUNDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
|
return handlePagingRequest(theServer, theRequest, null, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResourceOrDstu1Bundle invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, byte[] requestContents) {
|
||||||
|
IBase bundle = handlePagingRequest(theServer, theRequest, null, theRequest.getParameters().get(Constants.PARAM_PAGINGACTION)[0]);
|
||||||
|
if (bundle instanceof Bundle) {
|
||||||
|
return new ResourceOrDstu1Bundle((Bundle) bundle);
|
||||||
|
} else {
|
||||||
|
return new ResourceOrDstu1Bundle((IBaseResource) bundle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IBase handlePagingRequest(IRestfulServer<?> theServer, RequestDetails theRequest, HttpServletResponse theResponse, String thePagingAction) {
|
||||||
|
IPagingProvider pagingProvider = theServer.getPagingProvider();
|
||||||
|
if (pagingProvider == null) {
|
||||||
|
throw new InvalidRequestException("This server does not support paging");
|
||||||
|
}
|
||||||
|
IBundleProvider resultList = pagingProvider.retrieveResultList(thePagingAction);
|
||||||
|
if (resultList == null) {
|
||||||
|
ourLog.info("Client requested unknown paging ID[{}]", thePagingAction);
|
||||||
|
throw new ResourceGoneException("Search ID[" + thePagingAction + "] does not exist and may have expired.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
||||||
|
if (count == null) {
|
||||||
|
count = pagingProvider.getDefaultPageSize();
|
||||||
|
} else if (count > pagingProvider.getMaximumPageSize()) {
|
||||||
|
count = pagingProvider.getMaximumPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
|
||||||
|
if (offsetI == null || offsetI < 0) {
|
||||||
|
offsetI = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start = Math.min(offsetI, resultList.size() - 1);
|
||||||
|
|
||||||
|
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
|
||||||
|
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||||
|
|
||||||
|
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
||||||
|
|
||||||
|
Set<Include> includes = new HashSet<Include>();
|
||||||
|
String[] reqIncludes = theRequest.getParameters().get(Constants.PARAM_INCLUDE);
|
||||||
|
if (reqIncludes != null) {
|
||||||
|
for (String nextInclude : reqIncludes) {
|
||||||
|
includes.add(new Include(nextInclude));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String linkSelfBase = theRequest.getFhirServerBase(); // myServerAddressStrategy.determineServerBase(getServletContext(),
|
||||||
|
// theRequest.getServletRequest());
|
||||||
|
String completeUrl = theRequest.getCompleteUrl();
|
||||||
|
String linkSelf = linkSelfBase + completeUrl.substring(theRequest.getCompleteUrl().indexOf('?'));
|
||||||
|
|
||||||
|
BundleTypeEnum bundleType = null;
|
||||||
|
String[] bundleTypeValues = theRequest.getParameters().get(Constants.PARAM_BUNDLETYPE);
|
||||||
|
if (bundleTypeValues != null) {
|
||||||
|
bundleType = BundleTypeEnum.VALUESET_BINDER.fromCodeString(bundleTypeValues[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bundleFactory.initializeBundleFromBundleProvider(theServer, resultList, responseEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, thePagingAction, bundleType, includes);
|
||||||
|
|
||||||
|
Bundle bundle = bundleFactory.getDstu1Bundle();
|
||||||
|
if (bundle != null) {
|
||||||
|
return bundle;
|
||||||
|
} else {
|
||||||
|
return bundleFactory.getResourceBundle();
|
||||||
|
}
|
||||||
|
// if (bundle != null) {
|
||||||
|
// for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
|
// IServerInterceptor next = getInterceptors().get(i);
|
||||||
|
// boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(),
|
||||||
|
// theRequest.getServletResponse());
|
||||||
|
// if (!continueProcessing) {
|
||||||
|
// ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// theRequest.getResponse().streamResponseAsBundle(bundle, summaryMode, respondGzip, requestIsBrowser);
|
||||||
|
// } else {
|
||||||
|
// IBaseResource resBundle = bundleFactory.getResourceBundle();
|
||||||
|
// for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||||
|
// IServerInterceptor next = getInterceptors().get(i);
|
||||||
|
// boolean continueProcessing = next.outgoingResponse(theRequest, resBundle, theRequest.getServletRequest(),
|
||||||
|
// theRequest.getServletResponse());
|
||||||
|
// if (!continueProcessing) {
|
||||||
|
// ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// theRequest.getResponse().streamResponseAsResource(resBundle, prettyPrint, summaryMode,
|
||||||
|
// Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), false);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestOperationTypeEnum getRestOperationType() {
|
||||||
|
return RestOperationTypeEnum.GET_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||||
|
String[] pageId = theRequest.getParameters().get(Constants.PARAM_PAGINGACTION);
|
||||||
|
if (pageId == null || pageId.length == 0 || isBlank(pageId[0])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (theRequest.getRequestType() != RequestTypeEnum.GET) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@CoverageIgnore
|
||||||
|
@Override
|
||||||
|
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.Writer;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Peter Van Houte
|
||||||
|
*
|
||||||
|
* @param <T> A functional class that parses an outcome
|
||||||
|
*/
|
||||||
|
public abstract class ParseAction<T> {
|
||||||
|
|
||||||
|
protected T theOutcome;
|
||||||
|
|
||||||
|
protected ParseAction(T outcome) {
|
||||||
|
this.theOutcome = outcome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void execute(IParser parser, Writer writer) throws IOException;
|
||||||
|
|
||||||
|
public static ParseAction<TagList> create(TagList outcome) {
|
||||||
|
return outcome == null ? null : new ParseAction<TagList>(outcome) {
|
||||||
|
@Override
|
||||||
|
public void execute(IParser theParser, Writer theWriter) throws IOException {
|
||||||
|
theParser.encodeTagListToWriter(this.theOutcome, theWriter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParseAction<IBaseResource> create(IBaseResource outcome) {
|
||||||
|
return outcome == null ? null : new ParseAction<IBaseResource>(outcome) {
|
||||||
|
@Override
|
||||||
|
public void execute(IParser theParser, Writer theWriter) throws IOException {
|
||||||
|
theParser.encodeResourceToWriter(this.theOutcome, theWriter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -51,7 +51,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
@ -212,7 +212,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public IBundleProvider invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
theMethodParams[myIdIndex] = MethodUtil.convertIdToType(theRequest.getId(), myIdParameterType);
|
theMethodParams[myIdIndex] = MethodUtil.convertIdToType(theRequest.getId(), myIdParameterType);
|
||||||
if (myVersionIdIndex != null) {
|
if (myVersionIdIndex != null) {
|
||||||
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getId().getVersionIdPart());
|
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getId().getVersionIdPart());
|
||||||
@ -222,7 +222,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
|||||||
IBundleProvider retVal = toResourceList(response);
|
IBundleProvider retVal = toResourceList(response);
|
||||||
|
|
||||||
if (theRequest.getServer().getETagSupport() == ETagSupportEnum.ENABLED) {
|
if (theRequest.getServer().getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||||
String ifNoneMatch = theRequest.getFirstHeader(Constants.HEADER_IF_NONE_MATCH_LC);
|
String ifNoneMatch = theRequest.getHeader(Constants.HEADER_IF_NONE_MATCH_LC);
|
||||||
if (retVal.size() == 1 && StringUtils.isNotBlank(ifNoneMatch)) {
|
if (retVal.size() == 1 && StringUtils.isNotBlank(ifNoneMatch)) {
|
||||||
List<IBaseResource> responseResources = retVal.getResources(0, 1);
|
List<IBaseResource> responseResources = retVal.getResources(0, 1);
|
||||||
IBaseResource responseResource = responseResources.get(0);
|
IBaseResource responseResource = responseResources.get(0);
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
package ca.uhn.fhir.rest.method;
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
@ -25,37 +30,31 @@ import java.util.Collections;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulResponse;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||||
|
|
||||||
public class RequestDetails {
|
public abstract class RequestDetails {
|
||||||
|
|
||||||
private Map<String, ArrayList<String>> myHeaders = new HashMap<String, ArrayList<String>>();
|
private byte[] myRequestContents;
|
||||||
private String myCompartmentName;
|
private String myCompartmentName;
|
||||||
private String myCompleteUrl;
|
private String myCompleteUrl;
|
||||||
private String myFhirServerBase;
|
private String myFhirServerBase;
|
||||||
private IdDt myId;
|
private IdDt myId;
|
||||||
private String myOperation;
|
private String myOperation;
|
||||||
private Map<String, String[]> myParameters;
|
private Map<String, String[]> myParameters;
|
||||||
private byte[] myRawRequest;
|
|
||||||
private String myRequestPath;
|
private String myRequestPath;
|
||||||
private RequestTypeEnum myRequestType;
|
private RequestTypeEnum myRequestType;
|
||||||
private String myResourceName;
|
private String myResourceName;
|
||||||
private boolean myRespondGzip;
|
private boolean myRespondGzip;
|
||||||
private RestOperationTypeEnum myRestOperationType;
|
private RestOperationTypeEnum myRestOperationType;
|
||||||
private String mySecondaryOperation;
|
private String mySecondaryOperation;
|
||||||
private RestfulServer myServer;
|
|
||||||
private HttpServletRequest myServletRequest;
|
|
||||||
private HttpServletResponse myServletResponse;
|
|
||||||
private Map<String, List<String>> myUnqualifiedToQualifiedNames;
|
private Map<String, List<String>> myUnqualifiedToQualifiedNames;
|
||||||
|
private IRestfulResponse myResponse;
|
||||||
|
|
||||||
public String getCompartmentName() {
|
public String getCompartmentName() {
|
||||||
return myCompartmentName;
|
return myCompartmentName;
|
||||||
}
|
}
|
||||||
@ -64,6 +63,10 @@ public class RequestDetails {
|
|||||||
return myCompleteUrl;
|
return myCompleteUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The fhir server base url, independant of the query being executed
|
||||||
|
* @return the fhir server base url
|
||||||
|
*/
|
||||||
public String getFhirServerBase() {
|
public String getFhirServerBase() {
|
||||||
return myFhirServerBase;
|
return myFhirServerBase;
|
||||||
}
|
}
|
||||||
@ -80,10 +83,6 @@ public class RequestDetails {
|
|||||||
return myParameters;
|
return myParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getRawRequest() {
|
|
||||||
return myRawRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The part of the request URL that comes after the server base.
|
* The part of the request URL that comes after the server base.
|
||||||
* <p>
|
* <p>
|
||||||
@ -110,17 +109,7 @@ public class RequestDetails {
|
|||||||
return mySecondaryOperation;
|
return mySecondaryOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RestfulServer getServer() {
|
public abstract IRestfulServerDefaults getServer();
|
||||||
return myServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpServletRequest getServletRequest() {
|
|
||||||
return myServletRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpServletResponse getServletResponse() {
|
|
||||||
return myServletResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, List<String>> getUnqualifiedToQualifiedNames() {
|
public Map<String, List<String>> getUnqualifiedToQualifiedNames() {
|
||||||
return myUnqualifiedToQualifiedNames;
|
return myUnqualifiedToQualifiedNames;
|
||||||
@ -178,10 +167,6 @@ public class RequestDetails {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRawRequest(byte[] theRawRequest) {
|
|
||||||
myRawRequest = theRawRequest;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRequestPath(String theRequestPath) {
|
public void setRequestPath(String theRequestPath) {
|
||||||
assert theRequestPath.length() == 0 || theRequestPath.charAt(0) != '/';
|
assert theRequestPath.length() == 0 || theRequestPath.charAt(0) != '/';
|
||||||
myRequestPath = theRequestPath;
|
myRequestPath = theRequestPath;
|
||||||
@ -207,46 +192,66 @@ public class RequestDetails {
|
|||||||
mySecondaryOperation = theSecondaryOperation;
|
mySecondaryOperation = theSecondaryOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setServer(RestfulServer theServer) {
|
public IRestfulResponse getResponse() {
|
||||||
myServer = theServer;
|
return myResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setServletRequest(HttpServletRequest theRequest) {
|
public void setResponse(IRestfulResponse theResponse) {
|
||||||
myServletRequest = theRequest;
|
this.myResponse = theResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setServletResponse(HttpServletResponse theServletResponse) {
|
public abstract String getHeader(String name);
|
||||||
myServletResponse = theServletResponse;
|
|
||||||
}
|
public final byte[] loadRequestContents(RequestDetails theRequest) {
|
||||||
|
if (myRequestContents == null) {
|
||||||
public static RequestDetails withResourceAndParams(String theResourceName, RequestTypeEnum theRequestType, Set<String> theParamNames) {
|
myRequestContents = getByteStreamRequestContents();
|
||||||
RequestDetails retVal = new RequestDetails();
|
|
||||||
retVal.setResourceName(theResourceName);
|
|
||||||
retVal.setRequestType(theRequestType);
|
|
||||||
Map<String, String[]> paramNames = new HashMap<String, String[]>();
|
|
||||||
for (String next : theParamNames) {
|
|
||||||
paramNames.put(next, new String[0]);
|
|
||||||
}
|
}
|
||||||
retVal.setParameters(paramNames);
|
return myRequestContents;
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHeader(String theName, String theValue) {
|
protected abstract byte[] getByteStreamRequestContents();
|
||||||
String lowerCase = theName.toLowerCase();
|
|
||||||
ArrayList<String> list = myHeaders.get(lowerCase);
|
|
||||||
if (list == null) {
|
|
||||||
list = new ArrayList<String>();
|
|
||||||
myHeaders.put(lowerCase, list);
|
|
||||||
}
|
|
||||||
list.add(theValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFirstHeader(String theName) {
|
public abstract List<String> getHeaders(String name);
|
||||||
ArrayList<String> list = myHeaders.get(theName.toLowerCase());
|
|
||||||
if (list == null || list.isEmpty()) {
|
/**
|
||||||
return null;
|
* Retrieves the body of the request as character data using
|
||||||
}
|
* a <code>BufferedReader</code>. The reader translates the character
|
||||||
return list.get(0);
|
* data according to the character encoding used on the body.
|
||||||
}
|
* Either this method or {@link #getInputStream} may be called to read the
|
||||||
|
* body, not both.
|
||||||
|
*
|
||||||
|
* @return a <code>Reader</code> containing the body of the request
|
||||||
|
*
|
||||||
|
* @exception UnsupportedEncodingException if the character set encoding
|
||||||
|
* used is not supported and the text cannot be decoded
|
||||||
|
*
|
||||||
|
* @exception IllegalStateException if {@link #getInputStream} method
|
||||||
|
* has been called on this request
|
||||||
|
*
|
||||||
|
* @exception IOException if an input or output exception occurred
|
||||||
|
*
|
||||||
|
* @see javax.servlet.http.HttpServletRequest#getInputStream
|
||||||
|
*/
|
||||||
|
public abstract Reader getReader() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the body of the request as binary data.
|
||||||
|
* Either this method or {@link #getReader} may be called to
|
||||||
|
* read the body, not both.
|
||||||
|
*
|
||||||
|
* @return a {@link InputStream} object containing
|
||||||
|
* the body of the request
|
||||||
|
*
|
||||||
|
* @exception IllegalStateException if the {@link #getReader} method
|
||||||
|
* has already been called for this request
|
||||||
|
*
|
||||||
|
* @exception IOException if an input or output exception occurred
|
||||||
|
*/
|
||||||
|
public abstract InputStream getInputStream() throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server base URL (with no trailing '/') for a given request
|
||||||
|
*/
|
||||||
|
public abstract String getServerBaseForRequest();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class RequestDetailsParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
return theRequest;
|
return theRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||||||
import ca.uhn.fhir.rest.param.BaseQueryParameter;
|
import ca.uhn.fhir.rest.param.BaseQueryParameter;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
@ -266,7 +266,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
if (myIdParamIndex != null) {
|
if (myIdParamIndex != null) {
|
||||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class ServerBaseParamBinder implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
return theRequest.getFhirServerBase();
|
return theRequest.getFhirServerBase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
class ServletRequestParameter implements IParameter {
|
class ServletRequestParameter implements IParameter {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class);
|
||||||
@ -43,8 +44,8 @@ class ServletRequestParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
return theRequest.getServletRequest();
|
return ((ServletRequestDetails) theRequest).getServletRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
class ServletResponseParameter implements IParameter {
|
class ServletResponseParameter implements IParameter {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
|
||||||
@ -43,8 +44,8 @@ class ServletResponseParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
return theRequest.getServletResponse();
|
return ((ServletRequestDetails) theRequest).getServletResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ class SinceParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_SINCE);
|
String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_SINCE);
|
||||||
if (sinceParams != null) {
|
if (sinceParams != null) {
|
||||||
if (sinceParams.length > 0) {
|
if (sinceParams.length > 0) {
|
||||||
|
@ -65,7 +65,7 @@ public class SortParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) {
|
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) {
|
||||||
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) {
|
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) {
|
||||||
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) {
|
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) {
|
||||||
|
@ -67,7 +67,7 @@ public class SummaryEnumParameter implements IParameter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
Set<SummaryEnum> value = getSummaryValueOrNull(theRequest);
|
Set<SummaryEnum> value = getSummaryValueOrNull(theRequest);
|
||||||
if (value == null || value.isEmpty()) {
|
if (value == null || value.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||||||
import ca.uhn.fhir.rest.param.TransactionParameter;
|
import ca.uhn.fhir.rest.param.TransactionParameter;
|
||||||
import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle;
|
import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle;
|
||||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
@ -119,7 +120,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The design of HAPI's transaction method for DSTU1 support assumed that a transaction was just an update on a
|
* The design of HAPI's transaction method for DSTU1 support assumed that a transaction was just an update on a
|
||||||
|
@ -37,7 +37,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||||
|
|
||||||
private Integer myIdParameterIndex;
|
private Integer myIdParameterIndex;
|
||||||
|
|
||||||
@ -58,7 +58,7 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||||||
* We are being a bit lenient here, since technically the client is supposed to include the version in the
|
* We are being a bit lenient here, since technically the client is supposed to include the version in the
|
||||||
* Content-Location header, but we allow it in the PUT URL as well..
|
* Content-Location header, but we allow it in the PUT URL as well..
|
||||||
*/
|
*/
|
||||||
String locationHeader = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_LOCATION);
|
String locationHeader = theRequest.getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||||
IdDt id = theRequest.getId();
|
IdDt id = theRequest.getId();
|
||||||
if (isNotBlank(locationHeader)) {
|
if (isNotBlank(locationHeader)) {
|
||||||
id = new IdDt(locationHeader);
|
id = new IdDt(locationHeader);
|
||||||
@ -69,7 +69,7 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String ifMatchValue = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_MATCH);
|
String ifMatchValue = theRequest.getHeader(Constants.HEADER_IF_MATCH);
|
||||||
if (isNotBlank(ifMatchValue)) {
|
if (isNotBlank(ifMatchValue)) {
|
||||||
ifMatchValue = MethodUtil.parseETagValue(ifMatchValue);
|
ifMatchValue = MethodUtil.parseETagValue(ifMatchValue);
|
||||||
if (id != null && id.hasVersionIdPart() == false) {
|
if (id != null && id.hasVersionIdPart() == false) {
|
||||||
|
@ -133,7 +133,7 @@ public abstract class BaseQueryParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
|
|
||||||
List<QualifiedParamList> paramList = new ArrayList<QualifiedParamList>();
|
List<QualifiedParamList> paramList = new ArrayList<QualifiedParamList>();
|
||||||
String name = getName();
|
String name = getName();
|
||||||
|
@ -30,7 +30,6 @@ import java.lang.reflect.Method;
|
|||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -102,38 +101,37 @@ public class ResourceParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding)
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding)
|
||||||
throws InternalErrorException, InvalidRequestException {
|
throws InternalErrorException, InvalidRequestException {
|
||||||
switch (myMode) {
|
switch (myMode) {
|
||||||
case BODY:
|
case BODY:
|
||||||
try {
|
try {
|
||||||
return IOUtils.toString(createRequestReader(theRequest, theRequestContents));
|
return IOUtils.toString(createRequestReader(theRequest));
|
||||||
} catch (IOException e) {
|
}
|
||||||
// Shouldn't happen since we're reading from a byte array
|
catch (IOException e) {
|
||||||
throw new InternalErrorException("Failed to load request");
|
// Shouldn't happen since we're reading from a byte array
|
||||||
|
throw new InternalErrorException("Failed to load request");
|
||||||
|
}
|
||||||
|
case ENCODING:
|
||||||
|
return RestfulServerUtils.determineRequestEncoding(theRequest);
|
||||||
|
case RESOURCE:
|
||||||
|
default:
|
||||||
|
return parseResourceFromRequest(theRequest, theMethodBinding, myResourceType);
|
||||||
}
|
}
|
||||||
case ENCODING:
|
// }
|
||||||
return RestfulServerUtils.determineRequestEncoding(theRequest);
|
|
||||||
case RESOURCE:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
IBaseResource retVal = parseResourceFromRequest(theRequest, theMethodBinding, myResourceType);
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Reader createRequestReader(byte[] theRequestContents, Charset charset) {
|
public static Reader createRequestReader(RequestDetails theRequest, Charset charset) {
|
||||||
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset);
|
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.loadRequestContents(theRequest)), charset);
|
||||||
return requestReader;
|
return requestReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Reader createRequestReader(RequestDetails theRequest, byte[] theRequestContents) {
|
public static Reader createRequestReader(RequestDetails theRequest) throws IOException {
|
||||||
return createRequestReader(theRequestContents, determineRequestCharset(theRequest));
|
return createRequestReader(theRequest, determineRequestCharset(theRequest));
|
||||||
}
|
}
|
||||||
|
|
||||||
static Charset determineRequestCharset(RequestDetails theRequest) {
|
public static Charset determineRequestCharset(RequestDetails theRequest) {
|
||||||
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
String ct = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||||
|
|
||||||
Charset charset = null;
|
Charset charset = null;
|
||||||
if (isNotBlank(ct)) {
|
if (isNotBlank(ct)) {
|
||||||
@ -144,20 +142,21 @@ public class ResourceParameter implements IParameter {
|
|||||||
charset = Charset.forName("UTF-8");
|
charset = Charset.forName("UTF-8");
|
||||||
}
|
}
|
||||||
return charset;
|
return charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<T> theResourceType) {
|
public static <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding,
|
||||||
|
Class<T> theResourceType) {
|
||||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||||
|
|
||||||
final Charset charset = determineRequestCharset(theRequest);
|
final Charset charset = determineRequestCharset(theRequest);
|
||||||
Reader requestReader = createRequestReader(theRequest.getRawRequest(), charset);
|
Reader requestReader = createRequestReader(theRequest, charset);
|
||||||
|
|
||||||
RestOperationTypeEnum restOperationType = theMethodBinding != null ? theMethodBinding.getRestOperationType() : null;
|
RestOperationTypeEnum restOperationType = theMethodBinding != null ? theMethodBinding.getRestOperationType() : null;
|
||||||
|
|
||||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest);
|
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest);
|
||||||
if (encoding == null) {
|
if (encoding == null) {
|
||||||
String ctValue = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
String ctValue = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||||
if (ctValue != null) {
|
if (ctValue != null) {
|
||||||
if (ctValue.startsWith("application/x-www-form-urlencoded")) {
|
if (ctValue.startsWith("application/x-www-form-urlencoded")) {
|
||||||
String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
|
String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
|
||||||
@ -171,7 +170,7 @@ public class ResourceParameter implements IParameter {
|
|||||||
String body;
|
String body;
|
||||||
try {
|
try {
|
||||||
body = IOUtils.toString(requestReader);
|
body = IOUtils.toString(requestReader);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// This shouldn't happen since we're reading from a byte array..
|
// This shouldn't happen since we're reading from a byte array..
|
||||||
throw new InternalErrorException(e);
|
throw new InternalErrorException(e);
|
||||||
}
|
}
|
||||||
@ -180,7 +179,7 @@ public class ResourceParameter implements IParameter {
|
|||||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", restOperationType);
|
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", restOperationType);
|
||||||
throw new InvalidRequestException(msg);
|
throw new InvalidRequestException(msg);
|
||||||
} else {
|
} else {
|
||||||
requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.getRawRequest()), charset);
|
requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.loadRequestContents(theRequest)), charset);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, restOperationType);
|
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, restOperationType);
|
||||||
@ -203,8 +202,7 @@ public class ResourceParameter implements IParameter {
|
|||||||
|
|
||||||
if (theRequest.getServer().getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
|
if (theRequest.getServer().getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) {
|
||||||
TagList tagList = new TagList();
|
TagList tagList = new TagList();
|
||||||
for (Enumeration<String> enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) {
|
for (String nextTagComplete : theRequest.getHeaders(Constants.HEADER_CATEGORY)) {
|
||||||
String nextTagComplete = enumeration.nextElement();
|
|
||||||
MethodUtil.parseTagValue(tagList, nextTagComplete);
|
MethodUtil.parseTagValue(tagList, nextTagComplete);
|
||||||
}
|
}
|
||||||
if (tagList.isEmpty() == false) {
|
if (tagList.isEmpty() == false) {
|
||||||
@ -212,23 +210,23 @@ public class ResourceParameter implements IParameter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType) {
|
public static IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType) {
|
||||||
IBaseResource retVal;
|
IBaseResource retVal;
|
||||||
if (IBaseBinary.class.isAssignableFrom(theResourceType)) {
|
if (IBaseBinary.class.isAssignableFrom(theResourceType)) {
|
||||||
FhirContext ctx = theRequest.getServer().getFhirContext();
|
FhirContext ctx = theRequest.getServer().getFhirContext();
|
||||||
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
String ct = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||||
IBaseBinary binary = (IBaseBinary) ctx.getResourceDefinition("Binary").newInstance();
|
IBaseBinary binary = (IBaseBinary) ctx.getResourceDefinition("Binary").newInstance();
|
||||||
binary.setContentType(ct);
|
binary.setContentType(ct);
|
||||||
binary.setContent(theRequest.getRawRequest());
|
binary.setContent(theRequest.loadRequestContents(theRequest));
|
||||||
|
|
||||||
retVal = binary;
|
retVal = binary;
|
||||||
} else {
|
} else {
|
||||||
retVal = loadResourceFromRequest(theRequest, theMethodBinding, theResourceType);
|
retVal = loadResourceFromRequest(theRequest, theMethodBinding, theResourceType);
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Mode {
|
public enum Mode {
|
||||||
BODY, ENCODING, RESOURCE
|
BODY, ENCODING, RESOURCE
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package ca.uhn.fhir.rest.param;
|
package ca.uhn.fhir.rest.param;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
@ -48,6 +50,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||||||
|
|
||||||
public class TransactionParameter implements IParameter {
|
public class TransactionParameter implements IParameter {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionParameter.class);
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
private ParamStyle myParamStyle;
|
private ParamStyle myParamStyle;
|
||||||
private Class<? extends IBaseResource> myResourceBundleType;
|
private Class<? extends IBaseResource> myResourceBundleType;
|
||||||
@ -96,35 +99,41 @@ public class TransactionParameter implements IParameter {
|
|||||||
// nothing
|
// nothing
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||||
|
|
||||||
// TODO: don't use a default encoding, just fail!
|
// TODO: don't use a default encoding, just fail!
|
||||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
|
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
|
||||||
|
|
||||||
IParser parser = encoding.newParser(myContext);
|
IParser parser = encoding.newParser(theRequest.getServer().getFhirContext());
|
||||||
|
|
||||||
|
Reader reader;
|
||||||
|
try {
|
||||||
|
reader = ResourceParameter.createRequestReader(theRequest);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
ourLog.error("Could not load request resource", e);
|
||||||
|
throw new InvalidRequestException(String.format("Could not load request resource: %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
Reader reader = ResourceParameter.createRequestReader(theRequest, theRequestContents);
|
|
||||||
|
|
||||||
switch (myParamStyle) {
|
switch (myParamStyle) {
|
||||||
case DSTU1_BUNDLE: {
|
case DSTU1_BUNDLE: {
|
||||||
Bundle bundle;
|
Bundle bundle;
|
||||||
bundle = parser.parseBundle(reader);
|
bundle = parser.parseBundle(reader);
|
||||||
return bundle;
|
return bundle;
|
||||||
}
|
|
||||||
case RESOURCE_LIST: {
|
|
||||||
Bundle bundle = parser.parseBundle(reader);
|
|
||||||
ArrayList<IResource> resourceList = new ArrayList<IResource>();
|
|
||||||
for (BundleEntry next : bundle.getEntries()) {
|
|
||||||
if (next.getResource() != null) {
|
|
||||||
resourceList.add(next.getResource());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return resourceList;
|
case RESOURCE_LIST: {
|
||||||
}
|
Bundle bundle = parser.parseBundle(reader);
|
||||||
case RESOURCE_BUNDLE:
|
ArrayList<IResource> resourceList = new ArrayList<IResource>();
|
||||||
return parser.parseResource(myResourceBundleType, reader);
|
for (BundleEntry next : bundle.getEntries()) {
|
||||||
|
if (next.getResource() != null) {
|
||||||
|
resourceList.add(next.getResource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resourceList;
|
||||||
|
}
|
||||||
|
case RESOURCE_BUNDLE:
|
||||||
|
return parser.parseResource(myResourceBundleType, reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException("Unknown type: " + myParamStyle); // should not happen
|
throw new IllegalStateException("Unknown type: " + myParamStyle); // should not happen
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.ParseAction;
|
||||||
|
|
||||||
|
public interface IRestfulResponse {
|
||||||
|
|
||||||
|
Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode, int operationStatus, boolean respondGzip, boolean addContentLocationHeader)
|
||||||
|
throws IOException;
|
||||||
|
Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser)
|
||||||
|
throws IOException;
|
||||||
|
|
||||||
|
Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response, String resourceName) throws IOException;
|
||||||
|
|
||||||
|
Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip) throws UnsupportedEncodingException, IOException;
|
||||||
|
Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException;
|
||||||
|
|
||||||
|
void addHeader(String headerKey, String headerValue);
|
||||||
|
|
||||||
|
Object sendAttachmentResponse(IBaseBinary bin, int stausCode, String contentType) throws IOException;
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2015 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
public interface IRestfulServer<T extends RequestDetails> extends IRestfulServerDefaults {
|
||||||
|
|
||||||
|
List<IServerInterceptor> getInterceptors();
|
||||||
|
|
||||||
|
IPagingProvider getPagingProvider();
|
||||||
|
|
||||||
|
BundleInclusionRule getBundleInclusionRule();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
||||||
|
public interface IRestfulServerDefaults {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
||||||
|
* creating their own.
|
||||||
|
*/
|
||||||
|
FhirContext getFhirContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should the server "pretty print" responses by default (requesting clients can always override this default by supplying an <code>Accept</code> header in the request, or a <code>_pretty</code>
|
||||||
|
* parameter in the request URL.
|
||||||
|
* <p>
|
||||||
|
* The default is <code>false</code>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @return Returns the default pretty print setting
|
||||||
|
*/
|
||||||
|
boolean isDefaultPrettyPrint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the server support for ETags (will not be <code>null</code>). Default is {@link RestfulServer#DEFAULT_ETAG_SUPPORT}
|
||||||
|
*/
|
||||||
|
ETagSupportEnum getETagSupport();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the setting for automatically adding profile tags
|
||||||
|
*/
|
||||||
|
AddProfileTagEnum getAddProfileTag();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with the <code>_format</code> URL parameter, or with an <code>Accept</code> header
|
||||||
|
* in the request. The default is {@link EncodingEnum#XML}. Will not return null.
|
||||||
|
*/
|
||||||
|
EncodingEnum getDefaultResponseEncoding();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return If <code>true</code> 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
|
||||||
|
*/
|
||||||
|
boolean isUseBrowserFriendlyContentTypes();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.param.ResourceParameter.Mode;
|
||||||
|
import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle;
|
||||||
|
|
||||||
|
public interface IRestfulServerUtil {
|
||||||
|
|
||||||
|
Object getResourceParameter(
|
||||||
|
RequestDetails requestDetails,
|
||||||
|
Mode myMode,
|
||||||
|
BaseMethodBinding<?> theMethodBinding,
|
||||||
|
Class<? extends IBaseResource> myResourceType);
|
||||||
|
|
||||||
|
Object getRequestResource(RequestDetails theRequest, ParamStyle myParamStyle, Class<? extends IBaseResource> myResourceBundleType);
|
||||||
|
|
||||||
|
<T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<T> theResourceType);
|
||||||
|
|
||||||
|
IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType);
|
||||||
|
|
||||||
|
}
|
@ -40,7 +40,7 @@ public interface IVersionSpecificBundleFactory {
|
|||||||
|
|
||||||
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
|
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
|
||||||
|
|
||||||
void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
|
void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
|
||||||
int theOffset, Integer theCount, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes);
|
int theOffset, Integer theCount, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes);
|
||||||
|
|
||||||
Bundle getDstu1Bundle();
|
Bundle getDstu1Bundle();
|
||||||
|
@ -0,0 +1,18 @@
|
|||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetPage;
|
||||||
|
|
||||||
|
public class PageProvider {
|
||||||
|
|
||||||
|
// @GetPage(dstu1=true)
|
||||||
|
// public Bundle getPageDstu1() {
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
|
||||||
|
@GetPage()
|
||||||
|
public IResource getPage() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
|
||||||
|
public abstract class RestfulResponse<T extends RequestDetails> implements IRestfulResponse {
|
||||||
|
|
||||||
|
private T theRequestDetails;
|
||||||
|
private ConcurrentHashMap<String, String> theHeaders = new ConcurrentHashMap<String, String>();
|
||||||
|
|
||||||
|
public RestfulResponse(T requestDetails) {
|
||||||
|
this.theRequestDetails = requestDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Object streamResponseAsResource(IBaseResource resource, boolean prettyPrint, Set<SummaryEnum> summaryMode,
|
||||||
|
int statusCode, boolean respondGzip, boolean addContentLocationHeader)
|
||||||
|
throws IOException {
|
||||||
|
return RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), resource, prettyPrint, summaryMode, statusCode, respondGzip, addContentLocationHeader,
|
||||||
|
respondGzip, getRequestDetails());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object streamResponseAsBundle(Bundle bundle, Set<SummaryEnum> summaryMode, boolean respondGzip, boolean requestIsBrowser)
|
||||||
|
throws IOException {
|
||||||
|
return RestfulServerUtils.streamResponseAsBundle(theRequestDetails.getServer(), bundle, summaryMode, requestIsBrowser, respondGzip, getRequestDetails());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addHeader(String headerKey, String headerValue) {
|
||||||
|
this.getHeaders().put(headerKey, headerValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the http headers
|
||||||
|
* @return the headers
|
||||||
|
*/
|
||||||
|
public ConcurrentHashMap<String, String> getHeaders() {
|
||||||
|
return theHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the requestDetails
|
||||||
|
* @return the requestDetails
|
||||||
|
*/
|
||||||
|
public T getRequestDetails() {
|
||||||
|
return theRequestDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the requestDetails
|
||||||
|
* @param requestDetails the requestDetails to set
|
||||||
|
*/
|
||||||
|
public void setRequestDetails(T requestDetails) {
|
||||||
|
this.theRequestDetails = requestDetails;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -20,9 +20,10 @@ package ca.uhn.fhir.rest.server;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Writer;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -31,15 +32,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.UnavailableException;
|
import javax.servlet.UnavailableException;
|
||||||
@ -47,6 +46,7 @@ import javax.servlet.http.HttpServlet;
|
|||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
@ -55,18 +55,17 @@ import ca.uhn.fhir.context.ConfigurationException;
|
|||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.ProvidedResourceScanner;
|
import ca.uhn.fhir.context.ProvidedResourceScanner;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
|
||||||
import ca.uhn.fhir.model.api.Include;
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.annotation.Destroy;
|
import ca.uhn.fhir.rest.annotation.Destroy;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Initialize;
|
import ca.uhn.fhir.rest.annotation.Initialize;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|
||||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
|
||||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
|
import ca.uhn.fhir.rest.method.ConformanceMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.PageMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.ParseAction;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
@ -74,11 +73,12 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||||||
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import ca.uhn.fhir.util.VersionUtil;
|
import ca.uhn.fhir.util.VersionUtil;
|
||||||
|
|
||||||
public class RestfulServer extends HttpServlet {
|
public class RestfulServer extends HttpServlet implements IRestfulServer<ServletRequestDetails> {
|
||||||
|
|
||||||
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
|
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@ -319,15 +319,12 @@ public class RestfulServer extends HttpServlet {
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the setting for automatically adding profile tags
|
|
||||||
*
|
|
||||||
* @see #setAddProfileTag(AddProfileTagEnum)
|
|
||||||
*/
|
|
||||||
public AddProfileTagEnum getAddProfileTag() {
|
public AddProfileTagEnum getAddProfileTag() {
|
||||||
return myAddProfileTag;
|
return myAddProfileTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public BundleInclusionRule getBundleInclusionRule() {
|
public BundleInclusionRule getBundleInclusionRule() {
|
||||||
return myBundleInclusionRule;
|
return myBundleInclusionRule;
|
||||||
}
|
}
|
||||||
@ -336,13 +333,12 @@ public class RestfulServer extends HttpServlet {
|
|||||||
* Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with the <code>_format</code> URL parameter, or with an <code>Accept</code> header
|
* Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with the <code>_format</code> URL parameter, or with an <code>Accept</code> header
|
||||||
* in the request. The default is {@link EncodingEnum#XML}. Will not return null.
|
* in the request. The default is {@link EncodingEnum#XML}. Will not return null.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public EncodingEnum getDefaultResponseEncoding() {
|
public EncodingEnum getDefaultResponseEncoding() {
|
||||||
return myDefaultResponseEncoding;
|
return myDefaultResponseEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Returns the server support for ETags (will not be <code>null</code>). Default is {@link #DEFAULT_ETAG_SUPPORT}
|
|
||||||
*/
|
|
||||||
public ETagSupportEnum getETagSupport() {
|
public ETagSupportEnum getETagSupport() {
|
||||||
return myETagSupport;
|
return myETagSupport;
|
||||||
}
|
}
|
||||||
@ -351,6 +347,7 @@ public class RestfulServer extends HttpServlet {
|
|||||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
||||||
* creating their own.
|
* creating their own.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public FhirContext getFhirContext() {
|
public FhirContext getFhirContext() {
|
||||||
if (myFhirContext == null) {
|
if (myFhirContext == null) {
|
||||||
myFhirContext = new FhirContext();
|
myFhirContext = new FhirContext();
|
||||||
@ -365,10 +362,12 @@ public class RestfulServer extends HttpServlet {
|
|||||||
/**
|
/**
|
||||||
* Returns a ist of all registered server interceptors
|
* Returns a ist of all registered server interceptors
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public List<IServerInterceptor> getInterceptors() {
|
public List<IServerInterceptor> getInterceptors() {
|
||||||
return Collections.unmodifiableList(myInterceptors);
|
return Collections.unmodifiableList(myInterceptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public IPagingProvider getPagingProvider() {
|
public IPagingProvider getPagingProvider() {
|
||||||
return myPagingProvider;
|
return myPagingProvider;
|
||||||
}
|
}
|
||||||
@ -414,7 +413,7 @@ public class RestfulServer extends HttpServlet {
|
|||||||
public IServerAddressStrategy getServerAddressStrategy() {
|
public IServerAddressStrategy getServerAddressStrategy() {
|
||||||
return myServerAddressStrategy;
|
return myServerAddressStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the server base URL (with no trailing '/') for a given request
|
* Returns the server base URL (with no trailing '/') for a given request
|
||||||
*/
|
*/
|
||||||
@ -466,99 +465,15 @@ public class RestfulServer extends HttpServlet {
|
|||||||
return myServerVersion;
|
return myServerVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePagingRequest(RequestDetails theRequest, HttpServletResponse theResponse, String thePagingAction) throws IOException {
|
|
||||||
IBundleProvider resultList = getPagingProvider().retrieveResultList(thePagingAction);
|
|
||||||
if (resultList == null) {
|
|
||||||
ourLog.info("Client requested unknown paging ID[{}]", thePagingAction);
|
|
||||||
theResponse.setStatus(Constants.STATUS_HTTP_410_GONE);
|
|
||||||
addHeadersToResponse(theResponse);
|
|
||||||
theResponse.setContentType("text/plain");
|
|
||||||
theResponse.setCharacterEncoding("UTF-8");
|
|
||||||
theResponse.getWriter().append("Search ID[" + thePagingAction + "] does not exist and may have expired.");
|
|
||||||
theResponse.getWriter().close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
|
||||||
if (count == null) {
|
|
||||||
count = getPagingProvider().getDefaultPageSize();
|
|
||||||
} else if (count > getPagingProvider().getMaximumPageSize()) {
|
|
||||||
count = getPagingProvider().getMaximumPageSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
|
|
||||||
if (offsetI == null || offsetI < 0) {
|
|
||||||
offsetI = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int start = Math.min(offsetI, resultList.size() - 1);
|
|
||||||
|
|
||||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest(), getDefaultResponseEncoding());
|
|
||||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(this, theRequest);
|
|
||||||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
|
||||||
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
|
|
||||||
boolean respondGzip = theRequest.isRespondGzip();
|
|
||||||
|
|
||||||
IVersionSpecificBundleFactory bundleFactory = getFhirContext().newBundleFactory();
|
|
||||||
|
|
||||||
Set<Include> includes = new HashSet<Include>();
|
|
||||||
String[] reqIncludes = theRequest.getServletRequest().getParameterValues(Constants.PARAM_INCLUDE);
|
|
||||||
if (reqIncludes != null) {
|
|
||||||
for (String nextInclude : reqIncludes) {
|
|
||||||
includes.add(new Include(nextInclude));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String linkSelfBase = getServerAddressStrategy().determineServerBase(getServletContext(), theRequest.getServletRequest());
|
|
||||||
String linkSelf = linkSelfBase + theRequest.getCompleteUrl().substring(theRequest.getCompleteUrl().indexOf('?'));
|
|
||||||
|
|
||||||
BundleTypeEnum bundleType = null;
|
|
||||||
String[] bundleTypeValues = theRequest.getParameters().get(Constants.PARAM_BUNDLETYPE);
|
|
||||||
if (bundleTypeValues != null) {
|
|
||||||
bundleType = BundleTypeEnum.VALUESET_BINDER.fromCodeString(bundleTypeValues[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bundleFactory.initializeBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, thePagingAction, bundleType, includes);
|
|
||||||
|
|
||||||
Bundle bundle = bundleFactory.getDstu1Bundle();
|
|
||||||
if (bundle != null) {
|
|
||||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
|
||||||
IServerInterceptor next = getInterceptors().get(i);
|
|
||||||
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
|
||||||
if (!continueProcessing) {
|
|
||||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RestfulServerUtils.streamResponseAsBundle(this, theResponse, bundle, theRequest.getFhirServerBase(), summaryMode, respondGzip, requestIsBrowser, theRequest);
|
|
||||||
} else {
|
|
||||||
IBaseResource resBundle = bundleFactory.getResourceBundle();
|
|
||||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
|
||||||
IServerInterceptor next = getInterceptors().get(i);
|
|
||||||
boolean continueProcessing = next.outgoingResponse(theRequest, resBundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
|
||||||
if (!continueProcessing) {
|
|
||||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RestfulServerUtils.streamResponseAsResource(this, theResponse, resBundle, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), false, theRequest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void handleRequest(RequestTypeEnum theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
protected void handleRequest(RequestTypeEnum theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
||||||
String fhirServerBase = null;
|
String fhirServerBase = null;
|
||||||
boolean requestIsBrowser = requestIsBrowser(theRequest);
|
boolean requestIsBrowser = requestIsBrowser(theRequest);
|
||||||
RequestDetails requestDetails = new RequestDetails();
|
ServletRequestDetails requestDetails = new ServletRequestDetails();
|
||||||
requestDetails.setServer(this);
|
requestDetails.setServer(this);
|
||||||
requestDetails.setRequestType(theRequestType);
|
requestDetails.setRequestType(theRequestType);
|
||||||
requestDetails.setServletRequest(theRequest);
|
requestDetails.setServletRequest(theRequest);
|
||||||
requestDetails.setServletResponse(theResponse);
|
requestDetails.setServletResponse(theResponse);
|
||||||
for (Enumeration<String> iter = theRequest.getHeaderNames(); iter.hasMoreElements(); ) {
|
|
||||||
String nextName = iter.nextElement();
|
|
||||||
for (Enumeration<String> valueIter = theRequest.getHeaders(nextName); valueIter.hasMoreElements();) {
|
|
||||||
requestDetails.addHeader(nextName, valueIter.nextElement());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
@ -625,19 +540,19 @@ public class RestfulServer extends HttpServlet {
|
|||||||
requestDetails.setFhirServerBase(fhirServerBase);
|
requestDetails.setFhirServerBase(fhirServerBase);
|
||||||
requestDetails.setCompleteUrl(completeUrl);
|
requestDetails.setCompleteUrl(completeUrl);
|
||||||
|
|
||||||
String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
|
// String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
|
||||||
if (getPagingProvider() != null && isNotBlank(pagingAction)) {
|
// if (getPagingProvider() != null && isNotBlank(pagingAction)) {
|
||||||
requestDetails.setRestOperationType(RestOperationTypeEnum.GET_PAGE);
|
// requestDetails.setRestOperationType(RestOperationTypeEnum.GET_PAGE);
|
||||||
if (theRequestType != RequestTypeEnum.GET) {
|
// if (theRequestType != RequestTypeEnum.GET) {
|
||||||
/*
|
// /*
|
||||||
* We reconstruct the link-self URL using the request parameters, and this would break if the parameters came in using a POST. We could probably work around that but why bother unless
|
// * We reconstruct the link-self URL using the request parameters, and this would break if the parameters came in using a POST. We could probably work around that but why bother unless
|
||||||
* someone comes up with a reason for needing it.
|
// * someone comes up with a reason for needing it.
|
||||||
*/
|
// */
|
||||||
throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(RestfulServer.class, "getPagesNonHttpGet"));
|
// throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(RestfulServer.class, "getPagesNonHttpGet"));
|
||||||
}
|
// }
|
||||||
handlePagingRequest(requestDetails, theResponse, pagingAction);
|
// handlePagingRequest(requestDetails, theResponse, pagingAction);
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath);
|
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath);
|
||||||
|
|
||||||
@ -892,6 +807,13 @@ public class RestfulServer extends HttpServlet {
|
|||||||
invokeInitialize(next);
|
invokeInitialize(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
findResourceMethods(new PageProvider());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
ourLog.error("An error occurred while loading request handlers!", ex);
|
||||||
|
throw new ServletException("Failed to initialize FHIR Restful server", ex);
|
||||||
|
}
|
||||||
|
|
||||||
myStarted = true;
|
myStarted = true;
|
||||||
ourLog.info("A FHIR has been lit on this server");
|
ourLog.info("A FHIR has been lit on this server");
|
||||||
@ -1117,10 +1039,12 @@ public class RestfulServer extends HttpServlet {
|
|||||||
*
|
*
|
||||||
* @return Returns the default pretty print setting
|
* @return Returns the default pretty print setting
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public boolean isDefaultPrettyPrint() {
|
public boolean isDefaultPrettyPrint() {
|
||||||
return myDefaultPrettyPrint;
|
return myDefaultPrettyPrint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isUseBrowserFriendlyContentTypes() {
|
public boolean isUseBrowserFriendlyContentTypes() {
|
||||||
return myUseBrowserFriendlyContentTypes;
|
return myUseBrowserFriendlyContentTypes;
|
||||||
}
|
}
|
||||||
@ -1369,4 +1293,86 @@ public class RestfulServer extends HttpServlet {
|
|||||||
return userAgent != null && userAgent.contains("Mozilla");
|
return userAgent != null && userAgent.contains("Mozilla");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object returnResponse(ServletRequestDetails theRequest, ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response,
|
||||||
|
String resourceName) throws IOException {
|
||||||
|
HttpServletResponse servletResponse = theRequest.getServletResponse();
|
||||||
|
servletResponse.setStatus(operationStatus);
|
||||||
|
servletResponse.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
|
||||||
|
addHeadersToResponse(servletResponse);
|
||||||
|
if(allowPrefer) {
|
||||||
|
addContentLocationHeaders(theRequest, servletResponse, response, resourceName);
|
||||||
|
}
|
||||||
|
if (outcome != null) {
|
||||||
|
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequest);
|
||||||
|
servletResponse.setContentType(encoding.getResourceContentType());
|
||||||
|
Writer writer = servletResponse.getWriter();
|
||||||
|
IParser parser = encoding.newParser(getFhirContext());
|
||||||
|
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(this, theRequest));
|
||||||
|
try {
|
||||||
|
outcome.execute(parser, writer);
|
||||||
|
} finally {
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
servletResponse.setContentType(Constants.CT_TEXT_WITH_UTF8);
|
||||||
|
Writer writer = servletResponse.getWriter();
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
// getMethod().in
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addContentLocationHeaders(RequestDetails theRequest, HttpServletResponse servletResponse, MethodOutcome response, String resourceName) {
|
||||||
|
if (response != null && response.getId() != null) {
|
||||||
|
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION, resourceName);
|
||||||
|
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_CONTENT_LOCATION, resourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation, String resourceName) {
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
b.append(theRequest.getFhirServerBase());
|
||||||
|
b.append('/');
|
||||||
|
b.append(resourceName);
|
||||||
|
b.append('/');
|
||||||
|
b.append(response.getId().getIdPart());
|
||||||
|
if (response.getId().hasVersionIdPart()) {
|
||||||
|
b.append("/" + Constants.PARAM_HISTORY + "/");
|
||||||
|
b.append(response.getId().getVersionIdPart());
|
||||||
|
} else if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) {
|
||||||
|
b.append("/" + Constants.PARAM_HISTORY + "/");
|
||||||
|
b.append(response.getVersionId().getValue());
|
||||||
|
}
|
||||||
|
theResponse.addHeader(headerLocation, b.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public RestulfulServerConfiguration createConfiguration() {
|
||||||
|
RestulfulServerConfiguration result = new RestulfulServerConfiguration();
|
||||||
|
result.setResourceBindings(getResourceBindings());
|
||||||
|
result.setServerBindings(getServerBindings());
|
||||||
|
result.setImplementationDescription(getImplementationDescription());
|
||||||
|
result.setServerVersion(getServerVersion());
|
||||||
|
result.setServerName(getServerName());
|
||||||
|
result.setFhirContext(getFhirContext());
|
||||||
|
result.setServerAddressStrategy(myServerAddressStrategy);
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
inputStream = getClass().getResourceAsStream("/META-INF/MANIFEST.MF");
|
||||||
|
if (inputStream != null) {
|
||||||
|
Manifest manifest = new Manifest(inputStream);
|
||||||
|
result.setConformanceDate(manifest.getMainAttributes().getValue("Build-Time"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// fall through
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
IOUtils.closeQuietly(inputStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
@ -33,17 +32,15 @@ import java.util.Collections;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.GZIPOutputStream;
|
|
||||||
|
|
||||||
import javax.servlet.ServletOutputStream;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.client.utils.DateUtils;
|
import org.apache.http.client.utils.DateUtils;
|
||||||
@ -215,10 +212,10 @@ public class RestfulServerUtils {
|
|||||||
|
|
||||||
public static EncodingEnum determineRequestEncodingNoDefault(RequestDetails theReq) {
|
public static EncodingEnum determineRequestEncodingNoDefault(RequestDetails theReq) {
|
||||||
EncodingEnum retVal = null;
|
EncodingEnum retVal = null;
|
||||||
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE);
|
Iterator<String> acceptValues = theReq.getHeaders(Constants.HEADER_CONTENT_TYPE).iterator();
|
||||||
if (acceptValues != null) {
|
if (acceptValues != null) {
|
||||||
while (acceptValues.hasMoreElements() && retVal == null) {
|
while (acceptValues.hasNext() && retVal == null) {
|
||||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
String nextAcceptHeaderValue = acceptValues.next();
|
||||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
||||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
||||||
int scIdx = nextPart.indexOf(';');
|
int scIdx = nextPart.indexOf(';');
|
||||||
@ -243,8 +240,8 @@ public class RestfulServerUtils {
|
|||||||
/**
|
/**
|
||||||
* Returns null if the request doesn't express that it wants FHIR. If it expresses that it wants XML and JSON equally, returns thePrefer.
|
* Returns null if the request doesn't express that it wants FHIR. If it expresses that it wants XML and JSON equally, returns thePrefer.
|
||||||
*/
|
*/
|
||||||
public static EncodingEnum determineResponseEncodingNoDefault(HttpServletRequest theReq, EncodingEnum thePrefer) {
|
public static EncodingEnum determineResponseEncodingNoDefault(RequestDetails theReq, EncodingEnum thePrefer) {
|
||||||
String[] format = theReq.getParameterValues(Constants.PARAM_FORMAT);
|
String[] format = theReq.getParameters().get(Constants.PARAM_FORMAT);
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
for (String nextFormat : format) {
|
for (String nextFormat : format) {
|
||||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
||||||
@ -259,12 +256,11 @@ public class RestfulServerUtils {
|
|||||||
*/
|
*/
|
||||||
// text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, image/png, */*;q=0.5
|
// text/xml, application/xml, application/xhtml+xml, text/html;q=0.9, text/plain;q=0.8, image/png, */*;q=0.5
|
||||||
|
|
||||||
Enumeration<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
List<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||||
if (acceptValues != null) {
|
float bestQ = -1f;
|
||||||
float bestQ = -1f;
|
EncodingEnum retVal = null;
|
||||||
EncodingEnum retVal = null;
|
if (acceptValues != null) {
|
||||||
while (acceptValues.hasMoreElements()) {
|
for (String nextAcceptHeaderValue : acceptValues) {
|
||||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
|
||||||
StringTokenizer tok = new StringTokenizer(nextAcceptHeaderValue, ",");
|
StringTokenizer tok = new StringTokenizer(nextAcceptHeaderValue, ",");
|
||||||
while (tok.hasMoreTokens()) {
|
while (tok.hasMoreTokens()) {
|
||||||
String nextToken = tok.nextToken();
|
String nextToken = tok.nextToken();
|
||||||
@ -375,10 +371,10 @@ public class RestfulServerUtils {
|
|||||||
/**
|
/**
|
||||||
* Determine whether a response should be given in JSON or XML format based on the incoming HttpServletRequest's <code>"_format"</code> parameter and <code>"Accept:"</code> HTTP header.
|
* Determine whether a response should be given in JSON or XML format based on the incoming HttpServletRequest's <code>"_format"</code> parameter and <code>"Accept:"</code> HTTP header.
|
||||||
*/
|
*/
|
||||||
public static EncodingEnum determineResponseEncodingWithDefault(RestfulServer theServer, HttpServletRequest theReq) {
|
public static EncodingEnum determineResponseEncodingWithDefault(RequestDetails theReq) {
|
||||||
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq, theServer.getDefaultResponseEncoding());
|
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq, theReq.getServer().getDefaultResponseEncoding());
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
retVal = theServer.getDefaultResponseEncoding();
|
retVal = theReq.getServer().getDefaultResponseEncoding();
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
@ -420,14 +416,13 @@ public class RestfulServerUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Integer extractCountParameter(RequestDetails theRequest) {
|
public static Integer extractCountParameter(RequestDetails theRequest) {
|
||||||
String paramName = Constants.PARAM_COUNT;
|
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
|
||||||
return tryToExtractNamedParameter(theRequest, paramName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IParser getNewParser(FhirContext theContext, RequestDetails theRequestDetails) {
|
public static IParser getNewParser(FhirContext theContext, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
// Determine response encoding
|
// Determine response encoding
|
||||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails.getServer(), theRequestDetails.getServletRequest());
|
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails);
|
||||||
IParser parser;
|
IParser parser;
|
||||||
switch (responseEncoding) {
|
switch (responseEncoding) {
|
||||||
case JSON:
|
case JSON:
|
||||||
@ -444,17 +439,6 @@ public class RestfulServerUtils {
|
|||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Writer getWriter(HttpServletResponse theHttpResponse, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
|
|
||||||
Writer writer;
|
|
||||||
if (theRespondGzip) {
|
|
||||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
|
||||||
writer = new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), "UTF-8");
|
|
||||||
} else {
|
|
||||||
writer = theHttpResponse.getWriter();
|
|
||||||
}
|
|
||||||
return writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Set<String> parseAcceptHeaderAndReturnHighestRankedOptions(HttpServletRequest theRequest) {
|
public static Set<String> parseAcceptHeaderAndReturnHighestRankedOptions(HttpServletRequest theRequest) {
|
||||||
Set<String> retVal = new HashSet<String>();
|
Set<String> retVal = new HashSet<String>();
|
||||||
|
|
||||||
@ -476,7 +460,8 @@ public class RestfulServerUtils {
|
|||||||
try {
|
try {
|
||||||
q = Float.parseFloat(value);
|
q = Float.parseFloat(value);
|
||||||
q = Math.max(q, 0.0f);
|
q = Math.max(q, 0.0f);
|
||||||
} catch (NumberFormatException e) {
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
ourLog.debug("Invalid Accept header q value: {}", value);
|
ourLog.debug("Invalid Accept header q value: {}", value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -536,7 +521,7 @@ public class RestfulServerUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean prettyPrintResponse(RestfulServer theServer, RequestDetails theRequest) {
|
public static boolean prettyPrintResponse(IRestfulServerDefaults theServer, RequestDetails theRequest) {
|
||||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||||
String[] pretty = requestParams.get(Constants.PARAM_PRETTY);
|
String[] pretty = requestParams.get(Constants.PARAM_PRETTY);
|
||||||
boolean prettyPrint;
|
boolean prettyPrint;
|
||||||
@ -548,10 +533,9 @@ public class RestfulServerUtils {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
prettyPrint = theServer.isDefaultPrettyPrint();
|
prettyPrint = theServer.isDefaultPrettyPrint();
|
||||||
Enumeration<String> acceptValues = theRequest.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
|
List<String> acceptValues = theRequest.getHeaders(Constants.HEADER_ACCEPT);
|
||||||
if (acceptValues != null) {
|
if (acceptValues != null) {
|
||||||
while (acceptValues.hasMoreElements()) {
|
for (String nextAcceptHeaderValue : acceptValues) {
|
||||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
|
||||||
if (nextAcceptHeaderValue.contains("pretty=true")) {
|
if (nextAcceptHeaderValue.contains("pretty=true")) {
|
||||||
prettyPrint = true;
|
prettyPrint = true;
|
||||||
}
|
}
|
||||||
@ -561,54 +545,59 @@ public class RestfulServerUtils {
|
|||||||
return prettyPrint;
|
return prettyPrint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, Bundle bundle, String theServerBase, Set<SummaryEnum> theSummaryMode, boolean theRespondGzip,
|
public static Object streamResponseAsBundle(IRestfulServerDefaults theServer, Bundle bundle, Set<SummaryEnum> theSummaryMode,
|
||||||
boolean theRequestIsBrowser, RequestDetails theRequestDetails) throws IOException {
|
boolean theRequestIsBrowser, boolean respondGzip, RequestDetails theRequestDetails)
|
||||||
assert !theServerBase.endsWith("/");
|
throws IOException {
|
||||||
|
|
||||||
theHttpResponse.setStatus(200);
|
int status = 200;
|
||||||
|
|
||||||
// Determine response encoding
|
// Determine response encoding
|
||||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequestDetails.getServletRequest());
|
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails);
|
||||||
|
|
||||||
|
String contentType;
|
||||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
contentType = responseEncoding.getBrowserFriendlyBundleContentType();
|
||||||
} else {
|
} else {
|
||||||
theHttpResponse.setContentType(responseEncoding.getBundleContentType());
|
contentType = responseEncoding.getBundleContentType();
|
||||||
}
|
}
|
||||||
|
|
||||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
|
String charset = Constants.CHARSET_NAME_UTF8;
|
||||||
|
Writer writer = theRequestDetails.getResponse().getResponseWriter(status, contentType, charset, respondGzip);
|
||||||
|
|
||||||
theServer.addHeadersToResponse(theHttpResponse);
|
|
||||||
|
|
||||||
Writer writer = RestfulServerUtils.getWriter(theHttpResponse, theRespondGzip);
|
|
||||||
try {
|
try {
|
||||||
IParser parser = RestfulServerUtils.getNewParser(theServer.getFhirContext(), theRequestDetails);
|
IParser parser = RestfulServerUtils.getNewParser(theServer.getFhirContext(), theRequestDetails);
|
||||||
if (theSummaryMode.contains(SummaryEnum.TEXT)) {
|
if (theSummaryMode.contains(SummaryEnum.TEXT)) {
|
||||||
parser.setEncodeElements(TEXT_ENCODE_ELEMENTS);
|
parser.setEncodeElements(TEXT_ENCODE_ELEMENTS);
|
||||||
}
|
}
|
||||||
parser.encodeBundleToWriter(bundle, writer);
|
parser.encodeBundleToWriter(bundle, writer);
|
||||||
} finally {
|
|
||||||
writer.close();
|
|
||||||
}
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
//always send a response, even if the parsing went wrong
|
||||||
|
}
|
||||||
|
return theRequestDetails.getResponse().sendWriterResponse(status, contentType, charset, writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IBaseResource theResource, boolean theRequestIsBrowser, Set<SummaryEnum> theSummaryMode,
|
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, boolean theRequestIsBrowser, Set<SummaryEnum> theSummaryMode,
|
||||||
int stausCode, boolean theRespondGzip, boolean theAddContentLocationHeader, RequestDetails theRequestDetails) throws IOException {
|
int stausCode, boolean theRespondGzip, boolean theAddContentLocationHeader, boolean respondGzip,
|
||||||
theHttpResponse.setStatus(stausCode);
|
RequestDetails theRequestDetails)
|
||||||
|
throws IOException {
|
||||||
|
IRestfulResponse restUtil = theRequestDetails.getResponse();
|
||||||
|
|
||||||
// Determine response encoding
|
// Determine response encoding
|
||||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequestDetails.getServletRequest(), theServer.getDefaultResponseEncoding());
|
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequestDetails,
|
||||||
|
theServer.getDefaultResponseEncoding());
|
||||||
|
|
||||||
String serverBase = theRequestDetails.getFhirServerBase();
|
String serverBase = theRequestDetails.getFhirServerBase();
|
||||||
if (theAddContentLocationHeader && theResource.getIdElement() != null && theResource.getIdElement().hasIdPart() && isNotBlank(serverBase)) {
|
if (theAddContentLocationHeader && theResource.getIdElement() != null && theResource.getIdElement().hasIdPart()
|
||||||
|
&& isNotBlank(serverBase)) {
|
||||||
String resName = theServer.getFhirContext().getResourceDefinition(theResource).getName();
|
String resName = theServer.getFhirContext().getResourceDefinition(theResource).getName();
|
||||||
IIdType fullId = theResource.getIdElement().withServerBase(serverBase, resName);
|
IIdType fullId = theResource.getIdElement().withServerBase(serverBase, resName);
|
||||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId.getValue());
|
restUtil.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
|
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||||
if (theResource.getIdElement().hasVersionIdPart()) {
|
if (theResource.getIdElement().hasVersionIdPart()) {
|
||||||
theHttpResponse.addHeader(Constants.HEADER_ETAG, "W/\"" + theResource.getIdElement().getVersionIdPart() + '"');
|
restUtil.addHeader(Constants.HEADER_ETAG, "W/\"" + theResource.getIdElement().getVersionIdPart() + '"');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -619,26 +608,19 @@ public class RestfulServerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String contentType;
|
||||||
if (theResource instanceof IBaseBinary && responseEncoding == null) {
|
if (theResource instanceof IBaseBinary && responseEncoding == null) {
|
||||||
IBaseBinary bin = (IBaseBinary) theResource;
|
IBaseBinary bin = (IBaseBinary) theResource;
|
||||||
if (isNotBlank(bin.getContentType())) {
|
if (isNotBlank(bin.getContentType())) {
|
||||||
theHttpResponse.setContentType(bin.getContentType());
|
contentType = bin.getContentType();
|
||||||
} else {
|
} else {
|
||||||
theHttpResponse.setContentType(Constants.CT_OCTET_STREAM);
|
contentType = Constants.CT_OCTET_STREAM;
|
||||||
}
|
}
|
||||||
if (bin.getContent() == null || bin.getContent().length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force binary resources to download - This is a security measure to prevent
|
// Force binary resources to download - This is a security measure to prevent
|
||||||
// malicious images or HTML blocks being served up as content.
|
// malicious images or HTML blocks being served up as content.
|
||||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_DISPOSITION, "Attachment;");
|
restUtil.addHeader(Constants.HEADER_CONTENT_DISPOSITION, "Attachment;");
|
||||||
|
|
||||||
theHttpResponse.setContentLength(bin.getContent().length);
|
return restUtil.sendAttachmentResponse(bin, stausCode, contentType);
|
||||||
ServletOutputStream oos = theHttpResponse.getOutputStream();
|
|
||||||
oos.write(bin.getContent());
|
|
||||||
oos.close();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ok, we're not serving a binary resource, so apply default encoding
|
// Ok, we're not serving a binary resource, so apply default encoding
|
||||||
@ -656,38 +638,36 @@ public class RestfulServerUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
contentType = responseEncoding.getBrowserFriendlyBundleContentType();
|
||||||
} else if (encodingDomainResourceAsText) {
|
} else if (encodingDomainResourceAsText) {
|
||||||
theHttpResponse.setContentType(Constants.CT_HTML);
|
contentType = Constants.CT_HTML;
|
||||||
} else {
|
} else {
|
||||||
theHttpResponse.setContentType(responseEncoding.getResourceContentType());
|
contentType = responseEncoding.getResourceContentType();
|
||||||
}
|
}
|
||||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
|
String charset = Constants.CHARSET_NAME_UTF8;
|
||||||
|
|
||||||
theServer.addHeadersToResponse(theHttpResponse);
|
|
||||||
|
|
||||||
if (theResource instanceof IResource) {
|
if (theResource instanceof IResource) {
|
||||||
InstantDt lastUpdated = ResourceMetadataKeyEnum.UPDATED.get((IResource) theResource);
|
InstantDt lastUpdated = ResourceMetadataKeyEnum.UPDATED.get((IResource) theResource);
|
||||||
if (lastUpdated != null && lastUpdated.isEmpty() == false) {
|
if (lastUpdated != null && lastUpdated.isEmpty() == false) {
|
||||||
theHttpResponse.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated.getValue()));
|
restUtil.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated.getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TagList list = (TagList) ((IResource) theResource).getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
TagList list = (TagList) ((IResource) theResource).getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
for (Tag tag : list) {
|
for (Tag tag : list) {
|
||||||
if (StringUtils.isNotBlank(tag.getTerm())) {
|
if (StringUtils.isNotBlank(tag.getTerm())) {
|
||||||
theHttpResponse.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
|
restUtil.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Date lastUpdated = ((IAnyResource) theResource).getMeta().getLastUpdated();
|
Date lastUpdated = ((IAnyResource) theResource).getMeta().getLastUpdated();
|
||||||
if (lastUpdated != null) {
|
if (lastUpdated != null) {
|
||||||
theHttpResponse.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated));
|
restUtil.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Writer writer = getWriter(theHttpResponse, theRespondGzip);
|
Writer writer = restUtil.getResponseWriter(stausCode, contentType, charset, respondGzip);
|
||||||
try {
|
try {
|
||||||
if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
||||||
writer.append(((IResource) theResource).getText().getDiv().getValueAsString());
|
writer.append(((IResource) theResource).getText().getDiv().getValueAsString());
|
||||||
@ -695,9 +675,11 @@ public class RestfulServerUtils {
|
|||||||
IParser parser = getNewParser(theServer.getFhirContext(), theRequestDetails);
|
IParser parser = getNewParser(theServer.getFhirContext(), theRequestDetails);
|
||||||
parser.encodeResourceToWriter(theResource, writer);
|
parser.encodeResourceToWriter(theResource, writer);
|
||||||
}
|
}
|
||||||
} finally {
|
} catch (Exception e) {
|
||||||
writer.close();
|
//always send a response, even if the parsing went wrong
|
||||||
}
|
}
|
||||||
|
return restUtil.sendWriterResponse(stausCode, contentType, charset, writer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
// static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||||
|
@ -0,0 +1,155 @@
|
|||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
|
||||||
|
public class RestulfulServerConfiguration {
|
||||||
|
|
||||||
|
private Collection<ResourceBinding> resourceBindings;
|
||||||
|
private List<BaseMethodBinding<?>> serverBindings;
|
||||||
|
private String implementationDescription;
|
||||||
|
private String serverVersion;
|
||||||
|
private String serverName;
|
||||||
|
private FhirContext fhirContext;
|
||||||
|
private IServerAddressStrategy serverAddressStrategy;
|
||||||
|
private String conformanceDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the resourceBindings
|
||||||
|
* @return the resourceBindings
|
||||||
|
*/
|
||||||
|
public Collection<ResourceBinding> getResourceBindings() {
|
||||||
|
return resourceBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the resourceBindings
|
||||||
|
* @param resourceBindings the resourceBindings to set
|
||||||
|
*/
|
||||||
|
public RestulfulServerConfiguration setResourceBindings(Collection<ResourceBinding> resourceBindings) {
|
||||||
|
this.resourceBindings = resourceBindings;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the serverBindings
|
||||||
|
* @return the serverBindings
|
||||||
|
*/
|
||||||
|
public List<BaseMethodBinding<?>> getServerBindings() {
|
||||||
|
return serverBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the serverBindings
|
||||||
|
* @param serverBindings the serverBindings to set
|
||||||
|
*/
|
||||||
|
public RestulfulServerConfiguration setServerBindings(List<BaseMethodBinding<?>> serverBindings) {
|
||||||
|
this.serverBindings = serverBindings;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the implementationDescription
|
||||||
|
* @return the implementationDescription
|
||||||
|
*/
|
||||||
|
public String getImplementationDescription() {
|
||||||
|
return implementationDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the implementationDescription
|
||||||
|
* @param implementationDescription the implementationDescription to set
|
||||||
|
*/
|
||||||
|
public RestulfulServerConfiguration setImplementationDescription(String implementationDescription) {
|
||||||
|
this.implementationDescription = implementationDescription;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the serverVersion
|
||||||
|
* @return the serverVersion
|
||||||
|
*/
|
||||||
|
public String getServerVersion() {
|
||||||
|
return serverVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the serverVersion
|
||||||
|
* @param serverVersion the serverVersion to set
|
||||||
|
*/
|
||||||
|
public RestulfulServerConfiguration setServerVersion(String serverVersion) {
|
||||||
|
this.serverVersion = serverVersion;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the serverName
|
||||||
|
* @return the serverName
|
||||||
|
*/
|
||||||
|
public String getServerName() {
|
||||||
|
return serverName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the serverName
|
||||||
|
* @param serverName the serverName to set
|
||||||
|
*/
|
||||||
|
public RestulfulServerConfiguration setServerName(String serverName) {
|
||||||
|
this.serverName = serverName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
||||||
|
* creating their own.
|
||||||
|
*/
|
||||||
|
public FhirContext getFhirContext() {
|
||||||
|
return this.fhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the fhirContext
|
||||||
|
* @param fhirContext the fhirContext to set
|
||||||
|
*/
|
||||||
|
public RestulfulServerConfiguration setFhirContext(FhirContext fhirContext) {
|
||||||
|
this.fhirContext = fhirContext;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the serverAddressStrategy
|
||||||
|
* @return the serverAddressStrategy
|
||||||
|
*/
|
||||||
|
public IServerAddressStrategy getServerAddressStrategy() {
|
||||||
|
return serverAddressStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the serverAddressStrategy
|
||||||
|
* @param serverAddressStrategy the serverAddressStrategy to set
|
||||||
|
*/
|
||||||
|
public void setServerAddressStrategy(IServerAddressStrategy serverAddressStrategy) {
|
||||||
|
this.serverAddressStrategy = serverAddressStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the conformanceDate
|
||||||
|
* @return the conformanceDate
|
||||||
|
*/
|
||||||
|
public String getConformanceDate() {
|
||||||
|
return conformanceDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the conformanceDate
|
||||||
|
* @param conformanceDate the conformanceDate to set
|
||||||
|
*/
|
||||||
|
public void setConformanceDate(String conformanceDate) {
|
||||||
|
this.conformanceDate = conformanceDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -36,7 +36,7 @@ import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
|||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
import ca.uhn.fhir.rest.server.IRestfulResponse;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||||
@ -48,8 +48,14 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
|
|||||||
private Class<?>[] myReturnStackTracesForExceptionTypes;
|
private Class<?>[] myReturnStackTracesForExceptionTypes;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theRequest, HttpServletResponse theResponse)
|
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
|
||||||
|
handleException(theRequestDetails, theException);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object handleException(RequestDetails theRequestDetails, BaseServerResponseException theException)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
IRestfulResponse response = theRequestDetails.getResponse();
|
||||||
|
|
||||||
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
||||||
|
|
||||||
@ -67,22 +73,19 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
|
|||||||
if (isNotBlank(next.getKey()) && next.getValue() != null) {
|
if (isNotBlank(next.getKey()) && next.getValue() != null) {
|
||||||
String nextKey = next.getKey();
|
String nextKey = next.getKey();
|
||||||
for (String nextValue : next.getValue()) {
|
for (String nextValue : next.getValue()) {
|
||||||
theResponse.addHeader(nextKey, nextValue);
|
response.addHeader(nextKey, nextValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), theResponse, oo, true, Collections.singleton(SummaryEnum.FALSE), statusCode, false, false, theRequestDetails);
|
return response.streamResponseAsResource(oo, true, Collections.singleton(SummaryEnum.FALSE), statusCode, false, false);
|
||||||
|
|
||||||
// theResponse.setStatus(statusCode);
|
// theResponse.setStatus(statusCode);
|
||||||
// theRequestDetails.getServer().addHeadersToResponse(theResponse);
|
// theRequestDetails.getServer().addHeadersToResponse(theResponse);
|
||||||
// theResponse.setContentType("text/plain");
|
// theResponse.setContentType("text/plain");
|
||||||
// theResponse.setCharacterEncoding("UTF-8");
|
// theResponse.setCharacterEncoding("UTF-8");
|
||||||
// theResponse.getWriter().append(theException.getMessage());
|
// theResponse.getWriter().append(theException.getMessage());
|
||||||
// theResponse.getWriter().close();
|
// theResponse.getWriter().close();
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -148,6 +148,26 @@ public interface IServerInterceptor {
|
|||||||
* client.
|
* client.
|
||||||
*/
|
*/
|
||||||
boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
||||||
|
*
|
||||||
|
* @param theRequestDetails
|
||||||
|
* A bean containing details about the request that is about to be processed, including
|
||||||
|
* @param theResponseObject
|
||||||
|
* The actual object which is being streamed to the client as a response
|
||||||
|
* @param theServletRequest
|
||||||
|
* The incoming request
|
||||||
|
* @param theServletResponse
|
||||||
|
* The response. Note that interceptors may choose to provide a response (i.e. by calling {@link HttpServletResponse#getWriter()}) but in that case it is important to return
|
||||||
|
* <code>false</code> to indicate that the server itself should not also provide a response.
|
||||||
|
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
|
||||||
|
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
|
||||||
|
* @throws AuthenticationException
|
||||||
|
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
boolean outgoingResponse(RequestDetails theRequest, Bundle bundle);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
||||||
@ -167,6 +187,20 @@ public interface IServerInterceptor {
|
|||||||
* client.
|
* client.
|
||||||
*/
|
*/
|
||||||
boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
||||||
|
*
|
||||||
|
* @param theRequestDetails
|
||||||
|
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
|
||||||
|
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
|
||||||
|
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
|
||||||
|
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
|
||||||
|
* @throws AuthenticationException
|
||||||
|
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
boolean outgoingResponse(RequestDetails theRequestDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
||||||
@ -189,6 +223,22 @@ public interface IServerInterceptor {
|
|||||||
*/
|
*/
|
||||||
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||||
throws AuthenticationException;
|
throws AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
||||||
|
*
|
||||||
|
* @param theRequestDetails
|
||||||
|
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
|
||||||
|
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
|
||||||
|
* @param theResponseObject
|
||||||
|
* The actual object which is being streamed to the client as a response
|
||||||
|
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
|
||||||
|
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
|
||||||
|
* @throws AuthenticationException
|
||||||
|
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
||||||
@ -211,6 +261,22 @@ public interface IServerInterceptor {
|
|||||||
*/
|
*/
|
||||||
boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called after the server implementation method has been called, but before any attempt to stream the response back to the client
|
||||||
|
*
|
||||||
|
* @param theRequestDetails
|
||||||
|
* A bean containing details about the request that is about to be processed, including details such as the resource type and logical ID (if any) and other FHIR-specific aspects of the
|
||||||
|
* request which have been pulled out of the {@link HttpServletRequest servlet request}.
|
||||||
|
* @param theResponseObject
|
||||||
|
* The actual object which is being streamed to the client as a response
|
||||||
|
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do. If your interceptor is providing a response rather than letting HAPI handle the
|
||||||
|
* response normally, you must return <code>false</code>. In this case, no further processing will occur and no further interceptors will be called.
|
||||||
|
* @throws AuthenticationException
|
||||||
|
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
|
boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called upon any exception being thrown within the server's request processing code. This includes any exceptions thrown within resource provider methods (e.g. {@link Search} and
|
* This method is called upon any exception being thrown within the server's request processing code. This includes any exceptions thrown within resource provider methods (e.g. {@link Search} and
|
||||||
* {@link Read} methods) as well as any runtime exceptions thrown by the server itself. This method is invoked for each interceptor (until one of them returns a non-<code>null</code> response or
|
* {@link Read} methods) as well as any runtime exceptions thrown by the server itself. This method is invoked for each interceptor (until one of them returns a non-<code>null</code> response or
|
||||||
|
@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation
|
* Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation
|
||||||
@ -66,21 +67,45 @@ public class InterceptorAdapter implements IServerInterceptor {
|
|||||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle bundle) {
|
||||||
|
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||||
|
return outgoingResponse(details, bundle, details.getServletRequest(), details.getServletResponse());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean outgoingResponse(RequestDetails theRequestDetails) {
|
||||||
|
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||||
|
return outgoingResponse(theRequestDetails, details.getServletRequest(), details.getServletResponse());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
|
||||||
|
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||||
|
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject) {
|
||||||
|
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||||
|
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException {
|
public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException {
|
||||||
|
@ -269,7 +269,7 @@ public class LoggingInterceptor extends InterceptorAdapter {
|
|||||||
} else if (theKey.startsWith("remoteAddr")) {
|
} else if (theKey.startsWith("remoteAddr")) {
|
||||||
return StringUtils.defaultString(myRequest.getRemoteAddr());
|
return StringUtils.defaultString(myRequest.getRemoteAddr());
|
||||||
} else if (theKey.equals("responseEncodingNoDefault")) {
|
} else if (theKey.equals("responseEncodingNoDefault")) {
|
||||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingNoDefault(myRequest, myRequestDetails.getServer().getDefaultResponseEncoding());
|
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingNoDefault(myRequestDetails, myRequestDetails.getServer().getDefaultResponseEncoding());
|
||||||
if (encoding != null) {
|
if (encoding != null) {
|
||||||
return encoding.name();
|
return encoding.name();
|
||||||
} else {
|
} else {
|
||||||
|
@ -171,7 +171,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||||||
/*
|
/*
|
||||||
* It's not a browser...
|
* It's not a browser...
|
||||||
*/
|
*/
|
||||||
Set<String> highestRankedAcceptValues = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theRequestDetails.getServletRequest());
|
Set<String> highestRankedAcceptValues = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theServletRequest);
|
||||||
if (highestRankedAcceptValues.contains(Constants.CT_HTML) == false) {
|
if (highestRankedAcceptValues.contains(Constants.CT_HTML) == false) {
|
||||||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||||
}
|
}
|
||||||
@ -284,7 +284,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||||||
/*
|
/*
|
||||||
* It's not a browser...
|
* It's not a browser...
|
||||||
*/
|
*/
|
||||||
Set<String> accept = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theRequestDetails.getServletRequest());
|
Set<String> accept = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theServletRequest);
|
||||||
if (!accept.contains(Constants.CT_HTML)) {
|
if (!accept.contains(Constants.CT_HTML)) {
|
||||||
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
|
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,145 @@
|
|||||||
|
package ca.uhn.fhir.rest.server.servlet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.BaseMethodBinding.IRequestReader;
|
||||||
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
public class ServletRequestDetails extends RequestDetails {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestDetails.class);
|
||||||
|
/**
|
||||||
|
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
||||||
|
*/
|
||||||
|
private static volatile IRequestReader ourRequestReader;
|
||||||
|
private RestfulServer myServer;
|
||||||
|
private HttpServletRequest myServletRequest;
|
||||||
|
private HttpServletResponse myServletResponse;
|
||||||
|
private byte[] requestContents;
|
||||||
|
|
||||||
|
public ServletRequestDetails() {
|
||||||
|
super();
|
||||||
|
setResponse(new ServletRestfulResponse(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] getByteStreamRequestContents() {
|
||||||
|
/*
|
||||||
|
* This is weird, but this class is used both in clients and in servers, and we want to avoid needing to depend on
|
||||||
|
* servlet-api in clients since there is no point. So we dynamically load a class that does the servlet processing
|
||||||
|
* in servers. Down the road it may make sense to just split the method binding classes into server and client
|
||||||
|
* versions, but this isn't actually a huge deal I don't think.
|
||||||
|
*/
|
||||||
|
IRequestReader reader = ourRequestReader;
|
||||||
|
if (reader == null) {
|
||||||
|
try {
|
||||||
|
Class.forName("javax.servlet.ServletInputStream");
|
||||||
|
String className = BaseMethodBinding.class.getName() + "$" + "ActiveRequestReader";
|
||||||
|
try {
|
||||||
|
reader = (IRequestReader) Class.forName(className).newInstance();
|
||||||
|
} catch (Exception e1) {
|
||||||
|
throw new ConfigurationException("Failed to instantiate class " + className, e1);
|
||||||
|
}
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
String className = BaseMethodBinding.class.getName() + "$" + "InactiveRequestReader";
|
||||||
|
try {
|
||||||
|
reader = (IRequestReader) Class.forName(className).newInstance();
|
||||||
|
} catch (Exception e1) {
|
||||||
|
throw new ConfigurationException("Failed to instantiate class " + className, e1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ourRequestReader = reader;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
InputStream inputStream = reader.getInputStream(this);
|
||||||
|
requestContents = IOUtils.toByteArray(inputStream);
|
||||||
|
return requestContents;
|
||||||
|
} catch (IOException e) {
|
||||||
|
ourLog.error("Could not load request resource", e);
|
||||||
|
throw new InvalidRequestException(String.format("Could not load request resource: %s", e.getMessage()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String name) {
|
||||||
|
return getServletRequest().getHeader(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getHeaders(String name) {
|
||||||
|
Enumeration<String> headers = getServletRequest().getHeaders(name);
|
||||||
|
return headers == null ? Collections.<String> emptyList() : Collections.list(getServletRequest().getHeaders(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return getServletRequest().getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Reader getReader() throws IOException {
|
||||||
|
return getServletRequest().getReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulServer getServer() {
|
||||||
|
return myServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServerBaseForRequest() {
|
||||||
|
return getServer().getServerBaseForRequest(getServletRequest());
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpServletRequest getServletRequest() {
|
||||||
|
return myServletRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HttpServletResponse getServletResponse() {
|
||||||
|
return myServletResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServer(RestfulServer theServer) {
|
||||||
|
this.myServer = theServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServletRequest(HttpServletRequest myServletRequest) {
|
||||||
|
this.myServletRequest = myServletRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServletResponse(HttpServletResponse myServletResponse) {
|
||||||
|
this.myServletResponse = myServletResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RequestDetails withResourceAndParams(String theResourceName, RequestTypeEnum theRequestType, Set<String> theParamNames) {
|
||||||
|
RequestDetails retVal = new ServletRequestDetails();
|
||||||
|
retVal.setResourceName(theResourceName);
|
||||||
|
retVal.setRequestType(theRequestType);
|
||||||
|
Map<String, String[]> paramNames = new HashMap<String, String[]>();
|
||||||
|
for (String next : theParamNames) {
|
||||||
|
paramNames.put(next, new String[0]);
|
||||||
|
}
|
||||||
|
retVal.setParameters(paramNames);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package ca.uhn.fhir.rest.server.servlet;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.method.ParseAction;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulResponse;
|
||||||
|
|
||||||
|
public class ServletRestfulResponse extends RestfulResponse<ServletRequestDetails> {
|
||||||
|
|
||||||
|
public ServletRestfulResponse(ServletRequestDetails servletRequestDetails) {
|
||||||
|
super(servletRequestDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object sendAttachmentResponse(IBaseBinary bin, int stausCode, String contentType) throws IOException {
|
||||||
|
addHeaders();
|
||||||
|
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
|
||||||
|
theHttpResponse.setStatus(stausCode);
|
||||||
|
theHttpResponse.setContentType(contentType);
|
||||||
|
if (bin.getContent() == null || bin.getContent().length == 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
theHttpResponse.setContentLength(bin.getContent().length);
|
||||||
|
ServletOutputStream oos = theHttpResponse.getOutputStream();
|
||||||
|
oos.write(bin.getContent());
|
||||||
|
oos.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean theRespondGzip) throws UnsupportedEncodingException, IOException {
|
||||||
|
addHeaders();
|
||||||
|
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
|
||||||
|
theHttpResponse.setCharacterEncoding(charset);
|
||||||
|
theHttpResponse.setStatus(statusCode);
|
||||||
|
theHttpResponse.setContentType(contentType);
|
||||||
|
if (theRespondGzip) {
|
||||||
|
theHttpResponse.addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
||||||
|
return new OutputStreamWriter(new GZIPOutputStream(theHttpResponse.getOutputStream()), Constants.CHARSET_NAME_UTF8);
|
||||||
|
} else {
|
||||||
|
return theHttpResponse.getWriter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void addHeaders() {
|
||||||
|
HttpServletResponse theHttpResponse = getRequestDetails().getServletResponse();
|
||||||
|
getRequestDetails().getServer().addHeadersToResponse(theHttpResponse);
|
||||||
|
for (Entry<String, String> header : getHeaders().entrySet()) {
|
||||||
|
theHttpResponse.setHeader(header.getKey(), header.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Object sendWriterResponse(int status, String contentType, String charset, Writer writer) throws IOException {
|
||||||
|
writer.close();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response,
|
||||||
|
String resourceName) throws IOException {
|
||||||
|
return getRequestDetails().getServer().returnResponse(getRequestDetails(), outcome, operationStatus, allowPrefer, response, resourceName);
|
||||||
|
}
|
||||||
|
}
|
110
hapi-fhir-jaxrsserver-base/pom.xml
Normal file
110
hapi-fhir-jaxrsserver-base/pom.xml
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
|
<version>1.4-SNAPSHOT</version>
|
||||||
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>maven.java.net</id>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<url>https://maven.java.net/service/local/repositories/snapshots/content/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>HAPI FHIR JAX-RS Server</name>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- HAPI DEPENDENCIES -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-base</artifactId>
|
||||||
|
<version>1.4-SNAPSHOT</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>commons-logging</artifactId>
|
||||||
|
<groupId>commons-logging</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<!-- conformance profile -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-structures-dstu2</artifactId>
|
||||||
|
<version>1.4-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.ws.rs</groupId>
|
||||||
|
<artifactId>javax.ws.rs-api</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.ejb</groupId>
|
||||||
|
<artifactId>ejb-api</artifactId>
|
||||||
|
<version>3.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>${jetty_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-servlet</artifactId>
|
||||||
|
<version>${jetty_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.core</groupId>
|
||||||
|
<artifactId>jersey-server</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.containers</groupId>
|
||||||
|
<artifactId>jersey-container-servlet-core</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.containers</groupId>
|
||||||
|
<artifactId>jersey-container-jetty-http</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.media</groupId>
|
||||||
|
<artifactId>jersey-media-moxy</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jxr-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,205 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.OPTIONS;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Conformance;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.ParseAction;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulResponse;
|
||||||
|
import ca.uhn.fhir.rest.server.ResourceBinding;
|
||||||
|
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
|
||||||
|
import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
|
||||||
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the conformance provider for the jax rs servers. It requires all providers to be registered
|
||||||
|
* during startup because the conformance profile is generated during the postconstruct phase.
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||||
|
public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
/** the logger */
|
||||||
|
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(AbstractJaxRsConformanceProvider.class);
|
||||||
|
/** the server bindings */
|
||||||
|
private ResourceBinding myServerBinding = new ResourceBinding();
|
||||||
|
/** the resource bindings */
|
||||||
|
private ConcurrentHashMap<String, ResourceBinding> myResourceNameToBinding = new ConcurrentHashMap<String, ResourceBinding>();
|
||||||
|
/** the server configuration */
|
||||||
|
private RestulfulServerConfiguration serverConfiguration = new RestulfulServerConfiguration();
|
||||||
|
|
||||||
|
/** the conformance. It is created once during startup */
|
||||||
|
private Conformance myConformance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor allowing the description, servername and server to be set
|
||||||
|
* @param implementationDescription the implementation description. If null, "" is used
|
||||||
|
* @param serverName the server name. If null, "" is used
|
||||||
|
* @param serverVersion the server version. If null, "" is used
|
||||||
|
*/
|
||||||
|
protected AbstractJaxRsConformanceProvider(String implementationDescription, String serverName, String serverVersion) {
|
||||||
|
serverConfiguration.setFhirContext(getFhirContext());
|
||||||
|
serverConfiguration.setImplementationDescription(StringUtils.defaultIfEmpty(implementationDescription, ""));
|
||||||
|
serverConfiguration.setServerName(StringUtils.defaultIfEmpty(serverName, ""));
|
||||||
|
serverConfiguration.setServerVersion(StringUtils.defaultIfEmpty(serverVersion, ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will set the conformance during the postconstruct phase. The
|
||||||
|
* method {@link AbstractJaxRsConformanceProvider#getProviders()} is used to
|
||||||
|
* get all the resource providers include in the conformance
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
protected void setUpPostConstruct() {
|
||||||
|
for (Entry<Class<? extends IResourceProvider>, IResourceProvider> provider : getProviders().entrySet()) {
|
||||||
|
addProvider(provider.getValue(), provider.getKey());
|
||||||
|
}
|
||||||
|
List<BaseMethodBinding<?>> serverBindings = new ArrayList<BaseMethodBinding<?>>();
|
||||||
|
for (ResourceBinding baseMethodBinding : myResourceNameToBinding.values()) {
|
||||||
|
serverBindings.addAll(baseMethodBinding.getMethodBindings());
|
||||||
|
}
|
||||||
|
serverConfiguration.setServerBindings(serverBindings);
|
||||||
|
serverConfiguration.setResourceBindings(new LinkedList<ResourceBinding>(myResourceNameToBinding.values()));
|
||||||
|
HardcodedServerAddressStrategy hardcodedServerAddressStrategy = new HardcodedServerAddressStrategy();
|
||||||
|
hardcodedServerAddressStrategy.setValue(getBaseForServer());
|
||||||
|
serverConfiguration.setServerAddressStrategy(hardcodedServerAddressStrategy);
|
||||||
|
ServerConformanceProvider serverConformanceProvider = new ServerConformanceProvider(serverConfiguration);
|
||||||
|
serverConformanceProvider.initializeOperations();
|
||||||
|
myConformance = serverConformanceProvider.getServerConformance(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method must return all the resource providers which need to be included in the conformance
|
||||||
|
* @return a map of the resource providers and their corresponding classes. This class needs to be given
|
||||||
|
* explicitly because retrieving the interface using {@link Object#getClass()} may not give the correct
|
||||||
|
* interface in a jee environment.
|
||||||
|
*/
|
||||||
|
protected abstract ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will retrieve the conformance using the http OPTIONS method
|
||||||
|
* @return the response containing the conformance
|
||||||
|
*/
|
||||||
|
@OPTIONS
|
||||||
|
@Path("/metadata")
|
||||||
|
public Response conformanceUsingOptions() throws IOException {
|
||||||
|
return conformance();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will retrieve the conformance using the http GET method
|
||||||
|
* @return the response containing the conformance
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/metadata")
|
||||||
|
public Response conformance() throws IOException {
|
||||||
|
Builder request = getRequest(RequestTypeEnum.OPTIONS, RestOperationTypeEnum.METADATA);
|
||||||
|
IRestfulResponse response = request.build().getResponse();
|
||||||
|
response.addHeader(Constants.HEADER_CORS_ALLOW_ORIGIN, "*");
|
||||||
|
return (Response) response.returnResponse(ParseAction.create(myConformance), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will add a provider to the conformance. This method is almost an exact copy of {@link ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods }
|
||||||
|
* @param theProvider an instance of the provider interface
|
||||||
|
* @param theProviderInterface the class describing the providers interface
|
||||||
|
* @return the numbers of basemethodbindings added
|
||||||
|
* @see ca.uhn.fhir.rest.server.RestfulServer#findResourceMethods
|
||||||
|
*/
|
||||||
|
public int addProvider(IResourceProvider theProvider, Class<? extends IResourceProvider> theProviderInterface) throws ConfigurationException {
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (Method m : ReflectionUtil.getDeclaredMethods(theProviderInterface)) {
|
||||||
|
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
|
||||||
|
if (foundMethodBinding == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
|
||||||
|
// if (foundMethodBinding instanceof ConformanceMethodBinding) {
|
||||||
|
// myServerConformanceMethod = foundMethodBinding;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (!Modifier.isPublic(m.getModifiers())) {
|
||||||
|
throw new ConfigurationException("Method '" + m.getName() + "' is not public, FHIR RESTful methods must be public");
|
||||||
|
} else {
|
||||||
|
if (Modifier.isStatic(m.getModifiers())) {
|
||||||
|
throw new ConfigurationException("Method '" + m.getName() + "' is static, FHIR RESTful methods must not be static");
|
||||||
|
} else {
|
||||||
|
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
|
||||||
|
|
||||||
|
String resourceName = foundMethodBinding.getResourceName();
|
||||||
|
ResourceBinding resourceBinding;
|
||||||
|
if (resourceName == null) {
|
||||||
|
resourceBinding = myServerBinding;
|
||||||
|
} else {
|
||||||
|
RuntimeResourceDefinition definition = getFhirContext().getResourceDefinition(resourceName);
|
||||||
|
if (myResourceNameToBinding.containsKey(definition.getName())) {
|
||||||
|
resourceBinding = myResourceNameToBinding.get(definition.getName());
|
||||||
|
} else {
|
||||||
|
resourceBinding = new ResourceBinding();
|
||||||
|
resourceBinding.setResourceName(resourceName);
|
||||||
|
myResourceNameToBinding.put(resourceName, resourceBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Class<?>> allowableParams = foundMethodBinding.getAllowableParamAnnotations();
|
||||||
|
if (allowableParams != null) {
|
||||||
|
for (Annotation[] nextParamAnnotations : m.getParameterAnnotations()) {
|
||||||
|
for (Annotation annotation : nextParamAnnotations) {
|
||||||
|
Package pack = annotation.annotationType().getPackage();
|
||||||
|
if (pack.equals(IdParam.class.getPackage())) {
|
||||||
|
if (!allowableParams.contains(annotation.annotationType())) {
|
||||||
|
throw new ConfigurationException("Method[" + m.toString() + "] is not allowed to have a parameter annotated with " + annotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resourceBinding.addMethod(foundMethodBinding);
|
||||||
|
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Conformance> getResourceType() {
|
||||||
|
return Conformance.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.interceptor.Interceptors;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.QueryParam;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.PageMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.server.BundleInclusionRule;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.PageProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for a provider to provide the <code>[baseUrl]?_getpages=foo</code> request, which is a request to the
|
||||||
|
* server to retrieve the next page of a set of paged results.
|
||||||
|
*/
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
|
||||||
|
@Interceptors(JaxRsExceptionInterceptor.class)
|
||||||
|
public abstract class AbstractJaxRsPageProvider extends AbstractJaxRsProvider implements IRestfulServer<JaxRsRequest> {
|
||||||
|
|
||||||
|
private PageMethodBinding myBinding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default constructor.
|
||||||
|
*/
|
||||||
|
protected AbstractJaxRsPageProvider() {
|
||||||
|
try {
|
||||||
|
myBinding = new PageMethodBinding(getFhirContext(), PageProvider.class.getMethod("getPage"));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new ca.uhn.fhir.context.ConfigurationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseForRequest() {
|
||||||
|
try {
|
||||||
|
return getUriInfo().getBaseUri().toURL().toExternalForm();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// cannot happen
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method implements the "getpages" action
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
public Response getPages(@QueryParam(Constants.PARAM_PAGINGACTION) String thePageId) throws IOException {
|
||||||
|
JaxRsRequest theRequest = getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.GET_PAGE).build();
|
||||||
|
try {
|
||||||
|
return (Response) myBinding.invokeServer(this, theRequest);
|
||||||
|
} catch (JaxRsResponseException theException) {
|
||||||
|
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, theException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default: an empty list of interceptors
|
||||||
|
*
|
||||||
|
* @see ca.uhn.fhir.rest.server.IRestfulServer#getInterceptors()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IServerInterceptor> getInterceptors() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default: no paging provider
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default: BundleInclusionRule.BASED_ON_INCLUDES
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BundleInclusionRule getBundleInclusionRule() {
|
||||||
|
return BundleInclusionRule.BASED_ON_INCLUDES;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,172 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||||
|
import ca.uhn.fhir.rest.server.IServerAddressStrategy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the abstract superclass for all jaxrs providers. It contains some defaults implementing
|
||||||
|
* the IRestfulServerDefaults interface and exposes the uri and headers.
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
|
||||||
|
|
||||||
|
/** a static initialization for the fhircontext. Only DSTU2 is supported */
|
||||||
|
private static final FhirContext CTX = FhirContext.forDstu2();
|
||||||
|
|
||||||
|
/** the uri info */
|
||||||
|
@Context
|
||||||
|
private UriInfo theUriInfo;
|
||||||
|
/** the http headers */
|
||||||
|
@Context
|
||||||
|
private HttpHeaders theHeaders;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FhirContext getFhirContext() {
|
||||||
|
return CTX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the query parameters
|
||||||
|
* @return the query parameters
|
||||||
|
*/
|
||||||
|
public Map<String, String[]> getParameters() {
|
||||||
|
MultivaluedMap<String, String> queryParameters = getUriInfo().getQueryParameters();
|
||||||
|
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||||
|
for (Entry<String, List<String>> paramEntry : queryParameters.entrySet()) {
|
||||||
|
params.put(paramEntry.getKey(), paramEntry.getValue().toArray(new String[paramEntry.getValue().size()]));
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the default server address strategy. The default strategy return the
|
||||||
|
* base uri for the request {@link AbstractJaxRsProvider#getBaseForRequest() getBaseForRequest()}
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public IServerAddressStrategy getServerAddressStrategy() {
|
||||||
|
HardcodedServerAddressStrategy addressStrategy = new HardcodedServerAddressStrategy();
|
||||||
|
addressStrategy.setValue(getBaseForRequest());
|
||||||
|
return addressStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the server base, independent of the request or resource.
|
||||||
|
* @see javax.ws.rs.core.UriInfo#getBaseUri()
|
||||||
|
* @return the ascii string for the server base
|
||||||
|
*/
|
||||||
|
public String getBaseForServer() {
|
||||||
|
return getUriInfo().getBaseUri().toASCIIString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the server base, including the resource path.
|
||||||
|
* {@link javax.ws.rs.core.UriInfo#getBaseUri() UriInfo#getBaseUri()}
|
||||||
|
* @return the ascii string for the base resource provider path
|
||||||
|
*/
|
||||||
|
public String getBaseForRequest() {
|
||||||
|
return getBaseForServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the uriInfo
|
||||||
|
* @return the uri info
|
||||||
|
*/
|
||||||
|
public UriInfo getUriInfo() {
|
||||||
|
return this.theUriInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Uri Info
|
||||||
|
* @param uriInfo the uri info
|
||||||
|
*/
|
||||||
|
public void setUriInfo(UriInfo uriInfo) {
|
||||||
|
this.theUriInfo = uriInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the headers
|
||||||
|
* @return the headers
|
||||||
|
*/
|
||||||
|
public HttpHeaders getHeaders() {
|
||||||
|
return this.theHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the headers
|
||||||
|
* @param headers the headers to set
|
||||||
|
*/
|
||||||
|
public void setHeaders(HttpHeaders headers) {
|
||||||
|
this.theHeaders = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the requestbuilder for the server
|
||||||
|
* @param requestType the type of the request
|
||||||
|
* @param restOperation the rest operation type
|
||||||
|
* @return the requestbuilder
|
||||||
|
*/
|
||||||
|
public Builder getRequest(RequestTypeEnum requestType, RestOperationTypeEnum restOperation) {
|
||||||
|
return new JaxRsRequest.Builder(this, requestType, restOperation, theUriInfo.getRequestUri().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEFAULT = EncodingEnum.JSON
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public EncodingEnum getDefaultResponseEncoding() {
|
||||||
|
return EncodingEnum.JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEFAULT = true
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isDefaultPrettyPrint() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEFAULT = ETagSupportEnum.DISABLED
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public ETagSupportEnum getETagSupport() {
|
||||||
|
return ETagSupportEnum.DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEFAULT = AddProfileTagEnum.NEVER
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AddProfileTagEnum getAddProfileTag() {
|
||||||
|
return AddProfileTagEnum.NEVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DEFAULT = false
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isUseBrowserFriendlyContentTypes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,295 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.interceptor.Interceptors;
|
||||||
|
import javax.ws.rs.Consumes;
|
||||||
|
import javax.ws.rs.DELETE;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.PUT;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsMethodBindings;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.server.BundleInclusionRule;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This server is the abstract superclass for all resource providers. It exposes
|
||||||
|
* a large amount of the fhir api functionality using JAXRS
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
|
||||||
|
@Consumes({ MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON,
|
||||||
|
Constants.CT_FHIR_XML })
|
||||||
|
@Interceptors(JaxRsExceptionInterceptor.class)
|
||||||
|
public abstract class AbstractJaxRsResourceProvider<R extends IResource> extends AbstractJaxRsProvider
|
||||||
|
implements IRestfulServer<JaxRsRequest>, IResourceProvider {
|
||||||
|
|
||||||
|
/** the method bindings for this class */
|
||||||
|
private final JaxRsMethodBindings theBindings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default constructor. The method bindings are retrieved from the class
|
||||||
|
* being constructed.
|
||||||
|
*/
|
||||||
|
protected AbstractJaxRsResourceProvider() {
|
||||||
|
theBindings = JaxRsMethodBindings.getMethodBindings(this, getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This constructor takes in an explicit interface class. This subclass
|
||||||
|
* should be identical to the class being constructed but is given
|
||||||
|
* explicitly in order to avoid issues with proxy classes in a jee
|
||||||
|
* environment.
|
||||||
|
*
|
||||||
|
* @param theProviderClass the interface of the class
|
||||||
|
*/
|
||||||
|
protected AbstractJaxRsResourceProvider(Class<? extends AbstractJaxRsProvider> theProviderClass) {
|
||||||
|
theBindings = JaxRsMethodBindings.getMethodBindings(this, theProviderClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base for request for a resource provider has the following form:</br>
|
||||||
|
* {@link AbstractJaxRsResourceProvider#getBaseForServer()
|
||||||
|
* getBaseForServer()} + "/" +
|
||||||
|
* {@link AbstractJaxRsResourceProvider#getResourceType() getResourceType()}
|
||||||
|
* .{@link java.lang.Class#getSimpleName() getSimpleName()}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getBaseForRequest() {
|
||||||
|
try {
|
||||||
|
return new URL(getUriInfo().getBaseUri().toURL(), getResourceType().getSimpleName()).toExternalForm();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// cannot happen
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new resource with a server assigned id
|
||||||
|
*
|
||||||
|
* @param resource the body of the post method containing resource being created in a xml/json form
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#create">https://www.hl7. org/fhir/http.html#create</a>
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
public Response create(final String resource) throws IOException {
|
||||||
|
return execute(getRequest(RequestTypeEnum.POST, RestOperationTypeEnum.CREATE).resource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the resource type based on some filter criteria
|
||||||
|
*
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
@Path("/_search")
|
||||||
|
public Response searchWithPost() throws IOException {
|
||||||
|
return execute(getRequest(RequestTypeEnum.POST, RestOperationTypeEnum.SEARCH_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search the resource type based on some filter criteria
|
||||||
|
*
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
public Response search() throws IOException {
|
||||||
|
return execute(getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing resource by its id (or create it if it is new)
|
||||||
|
*
|
||||||
|
* @param id the id of the resource
|
||||||
|
* @param resource the body contents for the put method
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#update">https://www.hl7.org/fhir/http.html#update</a>
|
||||||
|
*/
|
||||||
|
@PUT
|
||||||
|
@Path("/{id}")
|
||||||
|
public Response update(@PathParam("id") final String id, final String resource) throws IOException {
|
||||||
|
return execute(getRequest(RequestTypeEnum.PUT, RestOperationTypeEnum.UPDATE).id(id).resource(resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a resource
|
||||||
|
*
|
||||||
|
* @param id the id of the resource to delete
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#delete">https://www.hl7.org/fhir/http.html#delete</a>
|
||||||
|
*/
|
||||||
|
@DELETE
|
||||||
|
@Path("/{id}")
|
||||||
|
public Response delete(@PathParam("id") final String id) throws IOException {
|
||||||
|
return execute(getRequest(RequestTypeEnum.DELETE, RestOperationTypeEnum.DELETE).id(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the current state of the resource
|
||||||
|
*
|
||||||
|
* @param id the id of the resource to read
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#read">https://www.hl7.org/fhir/http.html#read</a>
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{id}")
|
||||||
|
public Response find(@PathParam("id") final String id) throws IOException {
|
||||||
|
return execute(getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.READ).id(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a custom operation
|
||||||
|
*
|
||||||
|
* @param resource the resource to create
|
||||||
|
* @param requestType the type of request
|
||||||
|
* @param id the id of the resource on which to perform the operation
|
||||||
|
* @param operationName the name of the operation to execute
|
||||||
|
* @param operationType the rest operation type
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/operations.html">https://www.hl7.org/fhir/operations.html</a>
|
||||||
|
*/
|
||||||
|
protected Response customOperation(final String resource, RequestTypeEnum requestType, String id,
|
||||||
|
String operationName, RestOperationTypeEnum operationType) throws IOException {
|
||||||
|
Builder request = getRequest(requestType, operationType).resource(resource).id(id);
|
||||||
|
return execute(request, operationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the update history for a particular resource
|
||||||
|
*
|
||||||
|
* @param id the id of the resource
|
||||||
|
* @param version the version of the resource
|
||||||
|
* @return the response
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#history">https://www.hl7.org/fhir/http.html#history</a>
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/_history/{version}")
|
||||||
|
public Response findHistory(@PathParam("id") final String id, @PathParam("version") final String version)
|
||||||
|
throws IOException {
|
||||||
|
Builder theRequest = getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.VREAD).id(id)
|
||||||
|
.version(version);
|
||||||
|
return execute(theRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compartment Based Access
|
||||||
|
*
|
||||||
|
* @param id the resource to which the compartment belongs
|
||||||
|
* @param compartment the compartment
|
||||||
|
* @return the repsonse
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/http.html#search">https://www.hl7.org/fhir/http.html#search</a>
|
||||||
|
* @see <a href="https://www.hl7.org/fhir/compartments.html#compartment">https://www.hl7.org/fhir/compartments.html#compartment</a>
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/{compartment}")
|
||||||
|
public Response findCompartment(@PathParam("id") final String id,
|
||||||
|
@PathParam("compartment") final String compartment) throws IOException {
|
||||||
|
Builder theRequest = getRequest(RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE).id(id)
|
||||||
|
.compartment(compartment);
|
||||||
|
return execute(theRequest, compartment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the method described by the requestBuilder and methodKey
|
||||||
|
*
|
||||||
|
* @param theRequestBuilder the requestBuilder that contains the information about the request
|
||||||
|
* @param methodKey the key determining the method to be executed
|
||||||
|
* @return the response
|
||||||
|
*/
|
||||||
|
private Response execute(Builder theRequestBuilder, String methodKey) throws IOException {
|
||||||
|
JaxRsRequest theRequest = theRequestBuilder.build();
|
||||||
|
BaseMethodBinding<?> method = getBinding(theRequest.getRestOperationType(), methodKey);
|
||||||
|
try {
|
||||||
|
return (Response) method.invokeServer(this, theRequest);
|
||||||
|
} catch (JaxRsResponseException theException) {
|
||||||
|
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, theException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the method described by the requestBuilder
|
||||||
|
*
|
||||||
|
* @param theRequestBuilder the requestBuilder that contains the information about the request
|
||||||
|
* @return the response
|
||||||
|
*/
|
||||||
|
private Response execute(Builder theRequestBuilder) throws IOException {
|
||||||
|
return execute(theRequestBuilder, JaxRsMethodBindings.DEFAULT_METHOD_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the method binding for the given rest operation
|
||||||
|
*
|
||||||
|
* @param restOperation the rest operation to retrieve
|
||||||
|
* @param theBindingKey the key determining the method to be executed (needed for e.g. custom operation)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
protected BaseMethodBinding<?> getBinding(RestOperationTypeEnum restOperation, String theBindingKey) {
|
||||||
|
return getBindings().getBinding(restOperation, theBindingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default: an empty list of interceptors
|
||||||
|
*
|
||||||
|
* @see ca.uhn.fhir.rest.server.IRestfulServer#getInterceptors()
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<IServerInterceptor> getInterceptors() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default: no paging provider
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default: BundleInclusionRule.BASED_ON_INCLUDES
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BundleInclusionRule getBundleInclusionRule() {
|
||||||
|
return BundleInclusionRule.BASED_ON_INCLUDES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resource type should return conform to the generic resource included
|
||||||
|
* in the topic
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public abstract Class<R> getResourceType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the bindings defined in this resource provider
|
||||||
|
*
|
||||||
|
* @return the jax-rs method bindings
|
||||||
|
*/
|
||||||
|
public JaxRsMethodBindings getBindings() {
|
||||||
|
return theBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.interceptor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.interceptor.AroundInvoke;
|
||||||
|
import javax.interceptor.InvocationContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interceptor that catches the jax-rs exceptions
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
public class JaxRsExceptionInterceptor {
|
||||||
|
|
||||||
|
/** the existing exception handler which is able to convert exception into responses*/
|
||||||
|
private ExceptionHandlingInterceptor exceptionHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default constructor
|
||||||
|
*/
|
||||||
|
public JaxRsExceptionInterceptor() {
|
||||||
|
this.exceptionHandler = new ExceptionHandlingInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility constructor for unit testing
|
||||||
|
* @param exceptionHandler the handler for the exception conversion
|
||||||
|
*/
|
||||||
|
JaxRsExceptionInterceptor(ExceptionHandlingInterceptor exceptionHandler) {
|
||||||
|
this.exceptionHandler = exceptionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interceptor will catch all exception and convert them using the exceptionhandler
|
||||||
|
* @param ctx the invocation context
|
||||||
|
* @return the result
|
||||||
|
* @throws JaxRsResponseException an exception that can be handled by a jee container
|
||||||
|
*/
|
||||||
|
@AroundInvoke
|
||||||
|
public Object intercept(final InvocationContext ctx) throws JaxRsResponseException {
|
||||||
|
try {
|
||||||
|
return ctx.proceed();
|
||||||
|
} catch(final Exception theException) {
|
||||||
|
AbstractJaxRsProvider theServer = (AbstractJaxRsProvider) ctx.getTarget();
|
||||||
|
throw convertException(theServer, theException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JaxRsResponseException convertException(final AbstractJaxRsProvider theServer, final Exception theException) {
|
||||||
|
JaxRsRequest requestDetails = theServer.getRequest(null, null).build();
|
||||||
|
BaseServerResponseException convertedException = preprocessException(theException, requestDetails);
|
||||||
|
return new JaxRsResponseException(convertedException);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts an exception into a response
|
||||||
|
* @param theRequest the request
|
||||||
|
* @param theException the thrown exception
|
||||||
|
* @return the response describing the error
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public Response convertExceptionIntoResponse(JaxRsRequest theRequest, JaxRsResponseException theException)
|
||||||
|
throws IOException {
|
||||||
|
return handleExceptionWithoutServletError(theRequest, theException);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BaseServerResponseException preprocessException(final Exception theException, JaxRsRequest requestDetails) {
|
||||||
|
try {
|
||||||
|
return exceptionHandler.preProcessOutgoingException(requestDetails, theException, null);
|
||||||
|
} catch(ServletException e) {
|
||||||
|
return new InternalErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response handleExceptionWithoutServletError(JaxRsRequest theRequest, BaseServerResponseException theException) throws IOException {
|
||||||
|
try {
|
||||||
|
return (Response) exceptionHandler.handleException(theRequest, theException);
|
||||||
|
} catch (ServletException e) {
|
||||||
|
BaseServerResponseException newException = preprocessException(new InternalErrorException(e), theRequest);
|
||||||
|
return handleExceptionWithoutServletError(theRequest, newException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.interceptor;
|
||||||
|
|
||||||
|
import javax.ejb.ApplicationException;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JEE wrapper exception that will not force a rollback.
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
@ApplicationException(rollback=false)
|
||||||
|
public class JaxRsResponseException extends BaseServerResponseException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility constructor
|
||||||
|
*
|
||||||
|
* @param base the base exception
|
||||||
|
*/
|
||||||
|
public JaxRsResponseException(BaseServerResponseException base) {
|
||||||
|
super(base.getStatusCode(), base.getMessage(), base.getCause(), base.getOperationOutcome());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.OperationMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that contains the method bindings defined by a ResourceProvider
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
public class JaxRsMethodBindings {
|
||||||
|
|
||||||
|
/** DEFAULT_METHOD_KEY="" */
|
||||||
|
public static final String DEFAULT_METHOD_KEY = "";
|
||||||
|
/** Static collection of bindings mapped to a class*/
|
||||||
|
private static final ConcurrentHashMap<Class<?>, JaxRsMethodBindings> classBindings = new ConcurrentHashMap<Class<?>, JaxRsMethodBindings>();
|
||||||
|
/** Static collection of operationBindings mapped to a class */
|
||||||
|
private ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String, BaseMethodBinding<?>>> operationBindings = new ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String,BaseMethodBinding<?>>>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constructor
|
||||||
|
* @param theProvider the provider which is an implementation of the theProviderClass
|
||||||
|
* @param theProviderClass the class definition contaning the operations
|
||||||
|
*/
|
||||||
|
public JaxRsMethodBindings(AbstractJaxRsProvider theProvider, Class<? extends AbstractJaxRsProvider> theProviderClass) {
|
||||||
|
for (final Method m : ReflectionUtil.getDeclaredMethods(theProviderClass)) {
|
||||||
|
final BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, theProvider.getFhirContext(), theProvider);
|
||||||
|
if (foundMethodBinding == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String bindingKey = getBindingKey(foundMethodBinding);
|
||||||
|
addMethodBinding(bindingKey, foundMethodBinding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the key for the baseMethodBinding. This is:
|
||||||
|
* <ul>
|
||||||
|
* <li>the compartName for SearchMethodBindings
|
||||||
|
* <li>the methodName for OperationMethodBindings
|
||||||
|
* <li> {@link #DEFAULT_METHOD_KEY} for all other MethodBindings
|
||||||
|
* </ul>
|
||||||
|
* @param theBinding the methodbinding
|
||||||
|
* @return the key for the methodbinding.
|
||||||
|
*/
|
||||||
|
private String getBindingKey(final BaseMethodBinding<?> theBinding) {
|
||||||
|
if (theBinding instanceof OperationMethodBinding) {
|
||||||
|
return ((OperationMethodBinding) theBinding).getName();
|
||||||
|
} else if (theBinding instanceof SearchMethodBinding) {
|
||||||
|
Search search = theBinding.getMethod().getAnnotation(Search.class);
|
||||||
|
return search.compartmentName();
|
||||||
|
} else {
|
||||||
|
return DEFAULT_METHOD_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMethodBinding(String key, BaseMethodBinding<?> binding) {
|
||||||
|
ConcurrentHashMap<String, BaseMethodBinding<?>> mapByOperation = getMapForOperation(binding.getRestOperationType());
|
||||||
|
if (mapByOperation.containsKey(key)) {
|
||||||
|
throw new IllegalArgumentException("Multiple Search Method Bindings Found : " + mapByOperation.get(key) + " -- " + binding.getMethod());
|
||||||
|
}
|
||||||
|
mapByOperation.put(key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the map for the given operation type. If no map exists for this operation type, create a new hashmap for this
|
||||||
|
* operation type and add it to the operation bindings.
|
||||||
|
*
|
||||||
|
* @param operationType the operation type.
|
||||||
|
* @return the map defined in the operation bindings
|
||||||
|
*/
|
||||||
|
private ConcurrentHashMap<String, BaseMethodBinding<?>> getMapForOperation(RestOperationTypeEnum operationType) {
|
||||||
|
ConcurrentHashMap<String, BaseMethodBinding<?>> result = operationBindings.get(operationType);
|
||||||
|
if(result == null) {
|
||||||
|
operationBindings.putIfAbsent(operationType, new ConcurrentHashMap<String, BaseMethodBinding<?>>());
|
||||||
|
return getMapForOperation(operationType);
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the binding
|
||||||
|
*
|
||||||
|
* @param operationType the type of operation
|
||||||
|
* @param theBindingKey the binding key
|
||||||
|
* @return the binding defined
|
||||||
|
* @throws NotImplementedOperationException cannot be found
|
||||||
|
*/
|
||||||
|
public BaseMethodBinding<?> getBinding(RestOperationTypeEnum operationType, String theBindingKey) {
|
||||||
|
String bindingKey = StringUtils.defaultIfBlank(theBindingKey, DEFAULT_METHOD_KEY);
|
||||||
|
ConcurrentHashMap<String, BaseMethodBinding<?>> map = operationBindings.get(operationType);
|
||||||
|
if(map == null || !map.containsKey(bindingKey)) {
|
||||||
|
throw new NotImplementedOperationException("Operation not implemented");
|
||||||
|
} else {
|
||||||
|
return map.get(bindingKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the method bindings for the given class. If this class is not yet contained in the classBindings, they will be added for this class
|
||||||
|
*
|
||||||
|
* @param theProvider the implementation class
|
||||||
|
* @param theProviderClass the provider class
|
||||||
|
* @return the methodBindings for this class
|
||||||
|
*/
|
||||||
|
public static JaxRsMethodBindings getMethodBindings(AbstractJaxRsProvider theProvider, Class<? extends AbstractJaxRsProvider> theProviderClass) {
|
||||||
|
if(!getClassBindings().containsKey(theProviderClass)) {
|
||||||
|
JaxRsMethodBindings foundBindings = new JaxRsMethodBindings(theProvider, theProviderClass);
|
||||||
|
getClassBindings().putIfAbsent(theProviderClass, foundBindings);
|
||||||
|
}
|
||||||
|
return getClassBindings().get(theProviderClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the classBindings
|
||||||
|
*/
|
||||||
|
static ConcurrentHashMap<Class<?>, JaxRsMethodBindings> getClassBindings() {
|
||||||
|
return classBindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IRestfulResponse;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JaxRsRequest is a jax-rs specific implementation of the RequestDetails.
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
public class JaxRsRequest extends RequestDetails {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of the builder pattern for the JaxRsRequest
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private String myResource;
|
||||||
|
private AbstractJaxRsProvider myServer;
|
||||||
|
private RequestTypeEnum myRequestType;
|
||||||
|
private RestOperationTypeEnum myRestOperation;
|
||||||
|
private String myId;
|
||||||
|
private String myVersion;
|
||||||
|
private String myCompartment;
|
||||||
|
private String myRequestUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility Constructor
|
||||||
|
* @param theServer the server
|
||||||
|
* @param theRequestType the request type
|
||||||
|
* @param theRestOperation the rest operation
|
||||||
|
* @param theRequestUrl
|
||||||
|
*/
|
||||||
|
public Builder(AbstractJaxRsProvider theServer, RequestTypeEnum theRequestType,
|
||||||
|
RestOperationTypeEnum theRestOperation, String theRequestUrl) {
|
||||||
|
this.myServer = theServer;
|
||||||
|
this.myRequestType = theRequestType;
|
||||||
|
this.myRestOperation = theRestOperation;
|
||||||
|
this.myRequestUrl = theRequestUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the resource
|
||||||
|
* @param resource the body contents of an http method
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder resource(String resource) {
|
||||||
|
this.myResource = resource;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the id
|
||||||
|
* @param id the resource id
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder id(String id) {
|
||||||
|
this.myId = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the id version
|
||||||
|
* @param version the version of the resource
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder version(String version) {
|
||||||
|
this.myVersion = version;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the compartment
|
||||||
|
* @param compartment the compartment
|
||||||
|
* @return the builder
|
||||||
|
*/
|
||||||
|
public Builder compartment(String compartment) {
|
||||||
|
this.myCompartment = compartment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the jax-rs request
|
||||||
|
* @return the jax-rs request
|
||||||
|
*/
|
||||||
|
public JaxRsRequest build() {
|
||||||
|
JaxRsRequest result = new JaxRsRequest(myServer, myResource, myRequestType, myRestOperation);
|
||||||
|
if ((StringUtils.isNotBlank(myVersion) || StringUtils.isNotBlank(myCompartment))
|
||||||
|
&& StringUtils.isBlank(myId)) {
|
||||||
|
throw new InvalidRequestException("Don't know how to handle request path: "
|
||||||
|
+ myServer.getUriInfo().getRequestUri().toASCIIString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(myVersion)) {
|
||||||
|
result.setId(
|
||||||
|
new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
|
||||||
|
} else if (StringUtils.isNotBlank(myId)) {
|
||||||
|
result.setId(new IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myRestOperation == RestOperationTypeEnum.UPDATE) {
|
||||||
|
String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||||
|
if (contentLocation != null) {
|
||||||
|
result.setId(new IdDt(contentLocation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setCompartmentName(myCompartment);
|
||||||
|
result.setCompleteUrl(myRequestUrl);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String theResourceString;
|
||||||
|
private HttpHeaders headers;
|
||||||
|
private AbstractJaxRsProvider myServer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility Constructor
|
||||||
|
* @param server the server
|
||||||
|
* @param resourceString the resource body
|
||||||
|
* @param requestType the request type
|
||||||
|
* @param restOperation the operation type
|
||||||
|
*/
|
||||||
|
public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType,
|
||||||
|
RestOperationTypeEnum restOperation) {
|
||||||
|
this.headers = server.getHeaders();
|
||||||
|
this.theResourceString = resourceString;
|
||||||
|
this.setRestOperationType(restOperation);
|
||||||
|
setServer(server);
|
||||||
|
setFhirServerBase(server.getBaseForServer());
|
||||||
|
setParameters(server.getParameters());
|
||||||
|
setRequestType(requestType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractJaxRsProvider getServer() {
|
||||||
|
return myServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the server
|
||||||
|
* @param theServer the server to set
|
||||||
|
*/
|
||||||
|
public void setServer(AbstractJaxRsProvider theServer) {
|
||||||
|
this.myServer = theServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String headerKey) {
|
||||||
|
List<String> requestHeader = getHeaders(headerKey);
|
||||||
|
return requestHeader.isEmpty() ? null : requestHeader.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getHeaders(String name) {
|
||||||
|
List<String> requestHeader = headers.getRequestHeader(name);
|
||||||
|
return requestHeader == null ? Collections.<String> emptyList() : requestHeader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServerBaseForRequest() {
|
||||||
|
return getServer().getServerAddressStrategy().determineServerBase(null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected byte[] getByteStreamRequestContents() {
|
||||||
|
return StringUtils.defaultIfEmpty(theResourceString, "")
|
||||||
|
.getBytes(ResourceParameter.determineRequestCharset(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IRestfulResponse getResponse() {
|
||||||
|
if (super.getResponse() == null) {
|
||||||
|
setResponse(new JaxRsResponse(this));
|
||||||
|
}
|
||||||
|
return super.getResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Reader getReader() throws IOException {
|
||||||
|
// not yet implemented
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() {
|
||||||
|
// not yet implemented
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.util;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.io.Writer;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.method.ParseAction;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulResponse;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The JaxRsResponse is a jax-rs specific implementation of the RestfulResponse.
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
public class JaxRsResponse extends RestfulResponse<JaxRsRequest> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The constructor
|
||||||
|
*
|
||||||
|
* @param request the JaxRs Request
|
||||||
|
*/
|
||||||
|
public JaxRsResponse(JaxRsRequest request) {
|
||||||
|
super(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response writer is a simple String Writer. All output is configured
|
||||||
|
* by the server.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip)
|
||||||
|
throws UnsupportedEncodingException, IOException {
|
||||||
|
return new StringWriter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response sendWriterResponse(int status, String contentType, String charset, Writer writer) {
|
||||||
|
String charContentType = contentType + "; charset="
|
||||||
|
+ StringUtils.defaultIfBlank(charset, Constants.CHARSET_NAME_UTF8);
|
||||||
|
return buildResponse(status).header(Constants.HEADER_CONTENT_TYPE, charContentType).entity(writer.toString())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response sendAttachmentResponse(IBaseBinary bin, int statusCode, String contentType) throws IOException {
|
||||||
|
ResponseBuilder response = buildResponse(statusCode);
|
||||||
|
if (bin.getContent() != null && bin.getContent().length > 0) {
|
||||||
|
response.header(Constants.HEADER_CONTENT_TYPE, contentType).entity(bin.getContent());
|
||||||
|
}
|
||||||
|
return response.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer,
|
||||||
|
MethodOutcome response, String resourceName) throws IOException {
|
||||||
|
StringWriter writer = new StringWriter();
|
||||||
|
if (outcome != null) {
|
||||||
|
FhirContext fhirContext = getRequestDetails().getServer().getFhirContext();
|
||||||
|
IParser parser = RestfulServerUtils.getNewParser(fhirContext, getRequestDetails());
|
||||||
|
outcome.execute(parser, writer);
|
||||||
|
}
|
||||||
|
return sendWriterResponse(operationStatus, getParserType(), null, writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getParserType() {
|
||||||
|
EncodingEnum encodingEnum = RestfulServerUtils.determineResponseEncodingWithDefault(getRequestDetails());
|
||||||
|
return encodingEnum == EncodingEnum.JSON ? MediaType.APPLICATION_JSON : MediaType.APPLICATION_XML;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseBuilder buildResponse(int statusCode) {
|
||||||
|
ResponseBuilder response = Response.status(statusCode);
|
||||||
|
for (Entry<String, String> header : getHeaders().entrySet()) {
|
||||||
|
response.header(header.getKey(), header.getValue());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.ws.rs.HttpMethod;
|
||||||
|
import javax.ws.rs.core.MultivaluedHashMap;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import org.glassfish.jersey.internal.MapPropertiesDelegate;
|
||||||
|
import org.glassfish.jersey.server.ContainerRequest;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
|
||||||
|
public class AbstractJaxRsConformanceProviderTest {
|
||||||
|
|
||||||
|
private static final String BASEURI = "http://basiuri";
|
||||||
|
private static final String REQUESTURI = BASEURI + "/metadata";
|
||||||
|
AbstractJaxRsConformanceProvider provider;
|
||||||
|
private ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers;
|
||||||
|
private ContainerRequest headers;
|
||||||
|
private MultivaluedHashMap<String, String> queryParameters;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
// headers
|
||||||
|
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null,
|
||||||
|
new MapPropertiesDelegate());
|
||||||
|
// uri info
|
||||||
|
queryParameters = new MultivaluedHashMap<String, String>();
|
||||||
|
|
||||||
|
|
||||||
|
providers = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
|
||||||
|
provider = createConformanceProvider(providers);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConformance() throws Exception {
|
||||||
|
providers.put(AbstractJaxRsConformanceProvider.class, provider);
|
||||||
|
providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider());
|
||||||
|
Response response = createConformanceProvider(providers).conformance();
|
||||||
|
System.out.println(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConformanceUsingOptions() throws Exception {
|
||||||
|
providers.put(AbstractJaxRsConformanceProvider.class, provider);
|
||||||
|
providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider());
|
||||||
|
Response response = createConformanceProvider(providers).conformanceUsingOptions();
|
||||||
|
System.out.println(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConformanceWithMethods() throws Exception {
|
||||||
|
providers.put(AbstractJaxRsConformanceProvider.class, provider);
|
||||||
|
providers.put(TestJaxRsMockPatientRestProvider.class, new TestJaxRsMockPatientRestProvider());
|
||||||
|
Response response = createConformanceProvider(providers).conformance();
|
||||||
|
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
|
||||||
|
assertTrue(response.getEntity().toString().contains("\"type\":\"Patient\""));
|
||||||
|
assertTrue(response.getEntity().toString().contains("\"$someCustomOperation"));
|
||||||
|
System.out.println(response);
|
||||||
|
System.out.println(response.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConformanceInXml() throws Exception {
|
||||||
|
queryParameters.put(Constants.PARAM_FORMAT, Arrays.asList(Constants.CT_XML));
|
||||||
|
providers.put(AbstractJaxRsConformanceProvider.class, provider);
|
||||||
|
providers.put(TestJaxRsMockPatientRestProvider.class, new TestJaxRsMockPatientRestProvider());
|
||||||
|
Response response = createConformanceProvider(providers).conformance();
|
||||||
|
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
|
||||||
|
System.out.println(response.getEntity());
|
||||||
|
assertTrue(response.getEntity().toString().contains(" <type value=\"Patient\"/>"));
|
||||||
|
assertTrue(response.getEntity().toString().contains("\"$someCustomOperation"));
|
||||||
|
System.out.println(response.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers)
|
||||||
|
throws Exception {
|
||||||
|
AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(null, null, null) {
|
||||||
|
@Override
|
||||||
|
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
|
||||||
|
return providers;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// mocks
|
||||||
|
UriInfo uriInfo = mock(UriInfo.class);
|
||||||
|
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
|
||||||
|
when(uriInfo.getBaseUri()).thenReturn(new URI(BASEURI));
|
||||||
|
when(uriInfo.getRequestUri()).thenReturn(new URI(BASEURI + "/foo"));
|
||||||
|
result.setUriInfo(uriInfo);
|
||||||
|
result.setHeaders(headers);
|
||||||
|
result.setUpPostConstruct();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,437 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.argThat;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Matchers.isNull;
|
||||||
|
import static org.mockito.Mockito.reset;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hamcrest.BaseMatcher;
|
||||||
|
import org.hamcrest.Description;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Matchers;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.RandomServerPortProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsConformanceRestProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPageProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
|
||||||
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Conformance;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.DateDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.UriDt;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
||||||
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
|
||||||
|
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
|
||||||
|
public class AbstractJaxRsResourceProviderTest {
|
||||||
|
|
||||||
|
private TestJaxRsMockPatientRestProvider mock;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(AbstractJaxRsResourceProviderTest.class);
|
||||||
|
|
||||||
|
|
||||||
|
private ArgumentCaptor<IdDt> idCaptor;
|
||||||
|
private ArgumentCaptor<Patient> patientCaptor;
|
||||||
|
|
||||||
|
private static IGenericClient client;
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forDstu2();
|
||||||
|
private static final String PATIENT_NAME = "Van Houte";
|
||||||
|
private static int ourPort;
|
||||||
|
private static String serverBase;
|
||||||
|
private static Server jettyServer;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUpClass() throws Exception {
|
||||||
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||||
|
context.setContextPath("/");
|
||||||
|
System.out.println(ourPort);
|
||||||
|
jettyServer = new Server(ourPort);
|
||||||
|
jettyServer.setHandler(context);
|
||||||
|
ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
|
||||||
|
jerseyServlet.setInitOrder(0);
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
|
||||||
|
StringUtils.join(Arrays.asList(
|
||||||
|
TestJaxRsMockPatientRestProvider.class.getCanonicalName(),
|
||||||
|
JaxRsExceptionInterceptor.class.getCanonicalName(),
|
||||||
|
TestJaxRsConformanceRestProvider.class.getCanonicalName(),
|
||||||
|
TestJaxRsMockPageProvider.class.getCanonicalName()
|
||||||
|
), ";"));
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
jettyServer.start();
|
||||||
|
|
||||||
|
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
|
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
|
||||||
|
serverBase = "http://localhost:" + ourPort + "/";
|
||||||
|
client = ourCtx.newRestfulGenericClient(serverBase);
|
||||||
|
client.setEncoding(EncodingEnum.JSON);
|
||||||
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownClass() throws Exception {
|
||||||
|
try {
|
||||||
|
jettyServer.destroy();
|
||||||
|
} catch (Exception e) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
this.mock = TestJaxRsMockPatientRestProvider.mock;
|
||||||
|
idCaptor = ArgumentCaptor.forClass(IdDt.class);
|
||||||
|
patientCaptor = ArgumentCaptor.forClass(Patient.class);
|
||||||
|
reset(mock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search/Query - Type */
|
||||||
|
@Test
|
||||||
|
public void testSearchUsingGenericClientBySearch() {
|
||||||
|
// Perform a search
|
||||||
|
when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)))
|
||||||
|
.thenReturn(Arrays.asList(createPatient(1)));
|
||||||
|
final ca.uhn.fhir.model.api.Bundle results = client.search().forResource(Patient.class)
|
||||||
|
.where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).execute();
|
||||||
|
verify(mock).search(any(StringParam.class), Matchers.isNull(StringAndListParam.class));
|
||||||
|
IResource resource = results.getEntries().get(0).getResource();
|
||||||
|
|
||||||
|
compareResultId(1, resource);
|
||||||
|
compareResultUrl("/Patient/1", resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Multi-valued Parameters (ANY/OR) */
|
||||||
|
@Test
|
||||||
|
public void testSearchUsingGenericClientBySearchWithMultiValues() {
|
||||||
|
when(mock.search(any(StringParam.class), Matchers.isNotNull(StringAndListParam.class)))
|
||||||
|
.thenReturn(Arrays.asList(createPatient(1)));
|
||||||
|
final ca.uhn.fhir.model.api.Bundle results = client.search().forResource(Patient.class)
|
||||||
|
.where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario"))
|
||||||
|
.and(Patient.ADDRESS.matches().values("Canada"))
|
||||||
|
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SHORTNAME", "TOYS")).execute();
|
||||||
|
IResource resource = results.getEntries().get(0).getResource();
|
||||||
|
|
||||||
|
compareResultId(1, resource);
|
||||||
|
compareResultUrl("/Patient/1", resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Paging */
|
||||||
|
@Test
|
||||||
|
public void testSearchWithPaging() {
|
||||||
|
// Perform a search
|
||||||
|
when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)))
|
||||||
|
.thenReturn(createPatients(1, 13));
|
||||||
|
final Bundle results = client.search().forResource(Patient.class).limitTo(8).returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertEquals(results.getEntry().size(), 8);
|
||||||
|
IResource resource = results.getEntry().get(0).getResource();
|
||||||
|
compareResultId(1, resource);
|
||||||
|
compareResultUrl("/Patient/1", resource);
|
||||||
|
compareResultId(8, results.getEntry().get(7).getResource());
|
||||||
|
|
||||||
|
// ourLog.info("Next: " + results.getLink("next").getUrl());
|
||||||
|
// String url = results.getLink("next").getUrl().replace("?", "Patient?");
|
||||||
|
// results.getLink("next").setUrl(url);
|
||||||
|
// ourLog.info("New Next: " + results.getLink("next").getUrl());
|
||||||
|
|
||||||
|
// load next page
|
||||||
|
final Bundle nextPage = client.loadPage().next(results).execute();
|
||||||
|
resource = nextPage.getEntry().get(0).getResource();
|
||||||
|
compareResultId(9, resource);
|
||||||
|
compareResultUrl("/Patient/9", resource);
|
||||||
|
assertNull(nextPage.getLink(Bundle.LINK_NEXT));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search using other query options */
|
||||||
|
public void testOther() {
|
||||||
|
// missing
|
||||||
|
}
|
||||||
|
|
||||||
|
/** */
|
||||||
|
@Test
|
||||||
|
public void testSearchPost() {
|
||||||
|
when(mock.search(any(StringParam.class), Matchers.isNull(StringAndListParam.class)))
|
||||||
|
.thenReturn(createPatients(1, 13));
|
||||||
|
Bundle result = client.search().forResource("Patient").usingStyle(SearchStyleEnum.POST)
|
||||||
|
.returnBundle(Bundle.class).execute();
|
||||||
|
IResource resource = result.getEntry().get(0).getResource();
|
||||||
|
compareResultId(1, resource);
|
||||||
|
compareResultUrl("/Patient/1", resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Compartments */
|
||||||
|
@Test
|
||||||
|
public void testSearchCompartements() {
|
||||||
|
when(mock.searchCompartment(any(IdDt.class))).thenReturn(Arrays.asList((IResource) createPatient(1)));
|
||||||
|
Bundle response = client.search().forResource(Patient.class).withIdAndCompartment("1", "Condition")
|
||||||
|
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class).execute();
|
||||||
|
IResource resource = response.getEntry().get(0).getResource();
|
||||||
|
compareResultId(1, resource);
|
||||||
|
compareResultUrl("/Patient/1", resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Subsetting (_summary and _elements) */
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testSummary() {
|
||||||
|
Object response = client.search().forResource(Patient.class)
|
||||||
|
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePatient() throws Exception {
|
||||||
|
Patient toCreate = createPatient(1);
|
||||||
|
MethodOutcome outcome = new MethodOutcome();
|
||||||
|
toCreate.getIdentifierFirstRep().setValue("myIdentifier");
|
||||||
|
outcome.setResource(toCreate);
|
||||||
|
|
||||||
|
when(mock.create(patientCaptor.capture(), isNull(String.class))).thenReturn(outcome);
|
||||||
|
client.setEncoding(EncodingEnum.JSON);
|
||||||
|
final MethodOutcome response = client.create().resource(toCreate).prefer(PreferReturnEnum.REPRESENTATION)
|
||||||
|
.execute();
|
||||||
|
IResource resource = (IResource) response.getResource();
|
||||||
|
compareResultId(1, resource);
|
||||||
|
assertEquals("myIdentifier", patientCaptor.getValue().getIdentifierFirstRep().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Conditional Creates */
|
||||||
|
@Test
|
||||||
|
public void testConditionalCreate() throws Exception {
|
||||||
|
Patient toCreate = createPatient(1);
|
||||||
|
MethodOutcome outcome = new MethodOutcome();
|
||||||
|
toCreate.getIdentifierFirstRep().setValue("myIdentifier");
|
||||||
|
outcome.setResource(toCreate);
|
||||||
|
|
||||||
|
when(mock.create(patientCaptor.capture(), eq("Patient?_format=json&identifier=2"))).thenReturn(outcome);
|
||||||
|
client.setEncoding(EncodingEnum.JSON);
|
||||||
|
|
||||||
|
MethodOutcome response = client.create().resource(toCreate).conditional()
|
||||||
|
.where(Patient.IDENTIFIER.exactly().identifier("2")).prefer(PreferReturnEnum.REPRESENTATION).execute();
|
||||||
|
|
||||||
|
assertEquals("myIdentifier", patientCaptor.getValue().getIdentifierFirstRep().getValue());
|
||||||
|
IResource resource = (IResource) response.getResource();
|
||||||
|
compareResultId(1, resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Find By Id */
|
||||||
|
@Test
|
||||||
|
public void findUsingGenericClientById() {
|
||||||
|
when(mock.find(any(IdDt.class))).thenReturn(createPatient(1));
|
||||||
|
Patient result = client.read(Patient.class, "1");
|
||||||
|
compareResultId(1, result);
|
||||||
|
compareResultUrl("/Patient/1", result);
|
||||||
|
reset(mock);
|
||||||
|
when(mock.find(withId(result.getId()))).thenReturn(createPatient(1));
|
||||||
|
result = (Patient) client.read(new UriDt(result.getId().getValue()));
|
||||||
|
compareResultId(1, result);
|
||||||
|
compareResultUrl("/Patient/1", result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateById() throws Exception {
|
||||||
|
when(mock.update(idCaptor.capture(), patientCaptor.capture())).thenReturn(new MethodOutcome());
|
||||||
|
client.update("1", createPatient(2));
|
||||||
|
assertEquals("1", idCaptor.getValue().getIdPart());
|
||||||
|
compareResultId(1, patientCaptor.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletePatient() {
|
||||||
|
when(mock.delete(idCaptor.capture())).thenReturn(new MethodOutcome());
|
||||||
|
final BaseOperationOutcome results = client.delete().resourceById("Patient", "1").execute();
|
||||||
|
assertEquals("1", idCaptor.getValue().getIdPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transaction - Server */
|
||||||
|
@Ignore
|
||||||
|
@Test
|
||||||
|
public void testTransaction() {
|
||||||
|
ca.uhn.fhir.model.api.Bundle bundle = new ca.uhn.fhir.model.api.Bundle();
|
||||||
|
BundleEntry entry = bundle.addEntry();
|
||||||
|
final Patient existing = new Patient();
|
||||||
|
existing.getNameFirstRep().addFamily("Created with bundle");
|
||||||
|
entry.setResource(existing);
|
||||||
|
|
||||||
|
BoundCodeDt<BundleEntryTransactionMethodEnum> theTransactionOperation = new BoundCodeDt(
|
||||||
|
BundleEntryTransactionMethodEnum.VALUESET_BINDER, BundleEntryTransactionMethodEnum.POST);
|
||||||
|
entry.setTransactionMethod(theTransactionOperation);
|
||||||
|
ca.uhn.fhir.model.api.Bundle response = client.transaction().withBundle(bundle).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Conformance - Server */
|
||||||
|
@Test
|
||||||
|
public void testConformance() {
|
||||||
|
final Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
|
||||||
|
assertEquals(conf.getRest().get(0).getResource().get(0).getType().toString(), "Patient");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extended Operations */
|
||||||
|
@Test
|
||||||
|
public void testExtendedOperations() {
|
||||||
|
// prepare mock
|
||||||
|
Parameters resultParameters = new Parameters();
|
||||||
|
resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue"));
|
||||||
|
when(mock.someCustomOperation(any(IdDt.class), eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters);
|
||||||
|
// Create the input parameters to pass to the server
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
|
||||||
|
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
|
||||||
|
inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue"));
|
||||||
|
//invoke
|
||||||
|
Parameters outParams = client.operation().onInstance(new IdDt("Patient", "1")).named("$someCustomOperation")
|
||||||
|
.withParameters(inParams).execute();
|
||||||
|
//verify
|
||||||
|
assertEquals("outputValue", ((StringDt)outParams.getParameter().get(0).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtendedOperationsUsingGet() {
|
||||||
|
// prepare mock
|
||||||
|
Parameters resultParameters = new Parameters();
|
||||||
|
resultParameters.addParameter().setName("return").setResource(createPatient(1)).setValue(new StringDt("outputValue"));
|
||||||
|
when(mock.someCustomOperation(any(IdDt.class), eq(new StringDt("myAwesomeDummyValue")))).thenReturn(resultParameters);
|
||||||
|
// Create the input parameters to pass to the server
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
|
||||||
|
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
|
||||||
|
inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue"));
|
||||||
|
|
||||||
|
// invoke
|
||||||
|
Parameters outParams = client.operation().onInstance(new IdDt("Patient", "1")).named("$someCustomOperation")
|
||||||
|
.withParameters(inParams).useHttpGet().execute();
|
||||||
|
// verify
|
||||||
|
assertEquals("outputValue", ((StringDt)outParams.getParameter().get(0).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVRead() {
|
||||||
|
when(mock.findHistory(idCaptor.capture())).thenReturn(createPatient(1));
|
||||||
|
final Patient patient = client.vread(Patient.class, "1", "2");
|
||||||
|
compareResultId(1, patient);
|
||||||
|
compareResultUrl("/Patient/1", patient);
|
||||||
|
assertEquals("1", idCaptor.getValue().getIdPart());
|
||||||
|
assertEquals("2", idCaptor.getValue().getVersionIdPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead() {
|
||||||
|
when(mock.find(idCaptor.capture())).thenReturn(createPatient(1));
|
||||||
|
final Patient patient = client.read(Patient.class, "1");
|
||||||
|
compareResultId(1, patient);
|
||||||
|
compareResultUrl("/Patient/1", patient);
|
||||||
|
assertEquals("1", idCaptor.getValue().getIdPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testXFindUnknownPatient() {
|
||||||
|
try {
|
||||||
|
JaxRsResponseException notFoundException = new JaxRsResponseException(new ResourceNotFoundException(new IdDt("999955541264")));
|
||||||
|
when(mock.find(idCaptor.capture())).thenThrow(notFoundException);
|
||||||
|
client.read(Patient.class, "999955541264");
|
||||||
|
fail();
|
||||||
|
} catch (final ResourceNotFoundException e) {
|
||||||
|
assertEquals(ResourceNotFoundException.STATUS_CODE, e.getStatusCode());
|
||||||
|
assertTrue(e.getMessage().contains("999955541264"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bundle getPatientBundle(int size) {
|
||||||
|
Bundle result = new Bundle();
|
||||||
|
for (long i = 0; i < size; i++) {
|
||||||
|
Patient patient = createPatient(i);
|
||||||
|
Entry entry = new Entry().setResource(patient);
|
||||||
|
result.addEntry(entry);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Patient> createPatients(int firstId, int lastId) {
|
||||||
|
List<Patient> result = new ArrayList<Patient>(lastId - firstId);
|
||||||
|
for (long i = firstId; i <= lastId; i++) {
|
||||||
|
result.add(createPatient(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Patient createPatient(long id) {
|
||||||
|
Patient theResource = new Patient();
|
||||||
|
theResource.setId(new IdDt(id));
|
||||||
|
return theResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compareResultId(int id, IResource resource) {
|
||||||
|
assertEquals(id, resource.getId().getIdPartAsLong().intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void compareResultUrl(String url, IResource resource) {
|
||||||
|
assertEquals(url, resource.getId().getValueAsString().substring(serverBase.length() - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T withId(final T id) {
|
||||||
|
return argThat(new BaseMatcher<T>() {
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object other) {
|
||||||
|
IdDt thisId;
|
||||||
|
IdDt otherId;
|
||||||
|
if (id instanceof IdDt) {
|
||||||
|
thisId = (IdDt) id;
|
||||||
|
otherId = (IdDt) other;
|
||||||
|
} else {
|
||||||
|
thisId = ((IResource) id).getId();
|
||||||
|
otherId = ((IResource) other).getId();
|
||||||
|
}
|
||||||
|
return thisId.getIdPartAsLong().equals(otherId.getIdPartAsLong());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void describeTo(Description arg0) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.interceptor;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.isNull;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import javax.interceptor.InvocationContext;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.ws.rs.core.HttpHeaders;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
|
||||||
|
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
|
||||||
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
|
||||||
|
|
||||||
|
public class JaxRsExceptionInterceptorTest {
|
||||||
|
|
||||||
|
JaxRsExceptionInterceptor interceptor = new JaxRsExceptionInterceptor();
|
||||||
|
private InvocationContext context;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
interceptor = new JaxRsExceptionInterceptor();
|
||||||
|
context = mock(InvocationContext.class);
|
||||||
|
TestJaxRsDummyPatientProvider provider = spy(TestJaxRsDummyPatientProvider.class);
|
||||||
|
when(context.getTarget()).thenReturn(provider);
|
||||||
|
doReturn("http://baseUri").when(provider).getBaseForServer();
|
||||||
|
doReturn(new HashMap<String, String[]>()).when(provider).getParameters();
|
||||||
|
doReturn(mock(HttpHeaders.class)).when(provider).getHeaders();
|
||||||
|
|
||||||
|
UriInfo uriInfo = mock(UriInfo.class);
|
||||||
|
when(uriInfo.getRequestUri()).thenReturn(new URI("http://base/foo"));
|
||||||
|
provider.setUriInfo(uriInfo);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInterceptWithBaseServerError() throws Throwable {
|
||||||
|
NotImplementedOperationException thrownException = new NotImplementedOperationException("not implemented");
|
||||||
|
when(context.proceed()).thenThrow(thrownException);
|
||||||
|
try {
|
||||||
|
interceptor.intercept(context);
|
||||||
|
fail();
|
||||||
|
} catch (BaseServerResponseException e) {
|
||||||
|
assertEquals(e.getMessage(), thrownException.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIntercepWithServletError() throws Throwable {
|
||||||
|
ExceptionHandlingInterceptor exceptionHandler = mock(ExceptionHandlingInterceptor.class);
|
||||||
|
when(exceptionHandler.preProcessOutgoingException(any(RequestDetails.class), any(Throwable.class),
|
||||||
|
isNull(HttpServletRequest.class))).thenThrow(new ServletException("someMessage"));
|
||||||
|
interceptor = new JaxRsExceptionInterceptor(exceptionHandler);
|
||||||
|
when(context.proceed()).thenThrow(new ServletException());
|
||||||
|
try {
|
||||||
|
interceptor.intercept(context);
|
||||||
|
fail();
|
||||||
|
} catch (BaseServerResponseException e) {
|
||||||
|
assertTrue(e.getMessage().contains("someMessage"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInterceptServletWithoutError() throws Throwable {
|
||||||
|
Object expected = new Object();
|
||||||
|
when(context.proceed()).thenReturn(expected);
|
||||||
|
Object result = interceptor.intercept(context);
|
||||||
|
assertSame(expected, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandleExceptionWithServletError() throws Throwable {
|
||||||
|
JaxRsRequest request = ((AbstractJaxRsProvider) context.getTarget()).getRequest(null, null).build();
|
||||||
|
|
||||||
|
ExceptionHandlingInterceptor exceptionHandler = spy(ExceptionHandlingInterceptor.class);
|
||||||
|
|
||||||
|
interceptor = new JaxRsExceptionInterceptor(exceptionHandler);
|
||||||
|
|
||||||
|
when(context.proceed()).thenThrow(new ServletException());
|
||||||
|
|
||||||
|
JaxRsResponseException thrownException = new JaxRsResponseException(new NotImplementedOperationException("not implemented"));
|
||||||
|
doThrow(new javax.servlet.ServletException("someMessage")).when(exceptionHandler).handleException(request, thrownException);
|
||||||
|
Response result = interceptor.convertExceptionIntoResponse(request, thrownException);
|
||||||
|
assertEquals(InternalErrorException.STATUS_CODE, result.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.interceptor;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
import javax.ejb.ApplicationException;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
|
||||||
|
|
||||||
|
public class JaxRsResponseExceptionTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testException() {
|
||||||
|
ForbiddenOperationException wrappedException = new ForbiddenOperationException("someMessage");
|
||||||
|
JaxRsResponseException response = new JaxRsResponseException(wrappedException);
|
||||||
|
assertEquals(response.getMessage(), wrappedException.getMessage());
|
||||||
|
assertEquals(response.getStatusCode(), wrappedException.getStatusCode());
|
||||||
|
assertNotNull(response.getClass().getAnnotation(ApplicationException.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides server ports
|
||||||
|
*/
|
||||||
|
public class RandomServerPortProvider {
|
||||||
|
|
||||||
|
private static List<Integer> ourPorts = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
public static int findFreePort() {
|
||||||
|
ServerSocket server;
|
||||||
|
try {
|
||||||
|
server = new ServerSocket(0);
|
||||||
|
int port = server.getLocalPort();
|
||||||
|
ourPorts.add(port);
|
||||||
|
server.close();
|
||||||
|
Thread.sleep(500);
|
||||||
|
return port;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> list() {
|
||||||
|
return ourPorts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.test;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A conformance provider exposes the mock patient and this provider
|
||||||
|
*/
|
||||||
|
@Path("")
|
||||||
|
@Stateless
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class TestJaxRsConformanceRestProvider extends AbstractJaxRsConformanceProvider {
|
||||||
|
|
||||||
|
public TestJaxRsConformanceRestProvider() {
|
||||||
|
super("description", "name", "version");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
|
||||||
|
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
|
||||||
|
map.put(TestJaxRsMockPatientRestProvider.class, new TestJaxRsMockPatientRestProvider());
|
||||||
|
map.put(TestJaxRsConformanceRestProvider.class, new TestJaxRsConformanceRestProvider());
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A dummy patient provider exposing no methods
|
||||||
|
*/
|
||||||
|
public class TestJaxRsDummyPatientProvider extends AbstractJaxRsResourceProvider<Patient> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.test;
|
||||||
|
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
@Stateless
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class TestJaxRsMockPageProvider extends AbstractJaxRsPageProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return TestJaxRsMockPatientRestProvider.PAGING_PROVIDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,136 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Delete;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test server delegating each call to a mock
|
||||||
|
*/
|
||||||
|
@Path(TestJaxRsMockPatientRestProvider.PATH)
|
||||||
|
@Stateless
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class TestJaxRsMockPatientRestProvider extends AbstractJaxRsResourceProvider<Patient> {
|
||||||
|
|
||||||
|
static final String PATH = "/Patient";
|
||||||
|
|
||||||
|
public static final TestJaxRsMockPatientRestProvider mock = Mockito.mock(TestJaxRsMockPatientRestProvider.class);
|
||||||
|
|
||||||
|
public static final FifoMemoryPagingProvider PAGING_PROVIDER;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
PAGING_PROVIDER = new FifoMemoryPagingProvider(10);
|
||||||
|
PAGING_PROVIDER.setDefaultPageSize(10);
|
||||||
|
PAGING_PROVIDER.setMaximumPageSize(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public TestJaxRsMockPatientRestProvider() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name, @RequiredParam(name=Patient.SP_ADDRESS) StringAndListParam theAddressParts) {
|
||||||
|
return mock.search(name, theAddressParts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Update
|
||||||
|
public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient) throws Exception {
|
||||||
|
return mock.update(theId, patient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Read
|
||||||
|
public Patient find(@IdParam final IdDt theId) {
|
||||||
|
return mock.find(theId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Read(version = true)
|
||||||
|
public Patient findHistory(@IdParam final IdDt theId) {
|
||||||
|
return mock.findHistory(theId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional)
|
||||||
|
throws Exception {
|
||||||
|
return mock.create(patient, theConditional);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
public MethodOutcome delete(@IdParam final IdDt theId) {
|
||||||
|
return mock.delete(theId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search(compartmentName = "Condition")
|
||||||
|
public List<IResource> searchCompartment(@IdParam IdDt thePatientId) {
|
||||||
|
return mock.searchCompartment(thePatientId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/$someCustomOperation")
|
||||||
|
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
|
||||||
|
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
|
||||||
|
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{id}/$someCustomOperation")
|
||||||
|
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
|
||||||
|
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
|
||||||
|
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
|
||||||
|
@OperationParam(name = "return", type = StringDt.class) })
|
||||||
|
public Parameters someCustomOperation(@IdParam IdDt myId, @OperationParam(name = "dummy") StringDt dummyInput) {
|
||||||
|
return mock.someCustomOperation(myId, dummyInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return PAGING_PROVIDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,128 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.FixMethodOrder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runners.MethodSorters;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
|
|
||||||
|
@FixMethodOrder(MethodSorters.DEFAULT)
|
||||||
|
public class JaxRsMethodBindingsTest {
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
JaxRsMethodBindings.getClassBindings().clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = NotImplementedOperationException.class)
|
||||||
|
public void testFindMethodsForProviderNotDefinedMappingMethods() {
|
||||||
|
new TestJaxRsDummyPatientProvider().getBindings().getBinding(RestOperationTypeEnum.UPDATE, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindMethodsForProviderWithMethods() {
|
||||||
|
class TestFindPatientProvider extends TestJaxRsDummyPatientProvider {
|
||||||
|
@Search
|
||||||
|
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new TestFindPatientProvider();
|
||||||
|
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getDeclaringClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindMethodsFor2ProvidersWithMethods() {
|
||||||
|
class TestFindPatientProvider extends TestJaxRsDummyPatientProvider {
|
||||||
|
@Search
|
||||||
|
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class TestUpdatePatientProvider extends TestJaxRsDummyPatientProvider {
|
||||||
|
@Update
|
||||||
|
public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(TestFindPatientProvider.class, new TestFindPatientProvider().getBindings().getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getDeclaringClass());
|
||||||
|
assertEquals(TestUpdatePatientProvider.class, new TestUpdatePatientProvider().getBindings().getBinding(RestOperationTypeEnum.UPDATE, "").getMethod().getDeclaringClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindMethodsWithDoubleMethodsDeclaration() {
|
||||||
|
class TestDoubleSearchProvider extends TestJaxRsDummyPatientProvider {
|
||||||
|
@Search
|
||||||
|
public List<Patient> search1(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<Patient> search2(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new TestDoubleSearchProvider();
|
||||||
|
fail();
|
||||||
|
} catch(IllegalArgumentException e) {
|
||||||
|
assertTrue(e.getMessage().contains("search1"));
|
||||||
|
assertTrue(e.getMessage().contains("search2"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFindMethodsWithMultipleMethods() {
|
||||||
|
class TestFindPatientProvider extends TestJaxRsDummyPatientProvider {
|
||||||
|
@Search
|
||||||
|
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Update
|
||||||
|
public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Operation(name = "firstMethod", idempotent = true, returnParameters = { @OperationParam(name = "return", type = StringDt.class) })
|
||||||
|
public Parameters firstMethod(@OperationParam(name = "dummy") StringDt dummyInput) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
@Operation(name = "secondMethod", returnParameters = { @OperationParam(name = "return", type = StringDt.class) })
|
||||||
|
public Parameters secondMethod(@OperationParam(name = "dummy") StringDt dummyInput) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JaxRsMethodBindings bindings = new TestFindPatientProvider().getBindings();
|
||||||
|
assertEquals("search", bindings.getBinding(RestOperationTypeEnum.SEARCH_TYPE, "").getMethod().getName());
|
||||||
|
assertEquals("update", bindings.getBinding(RestOperationTypeEnum.UPDATE, "").getMethod().getName());
|
||||||
|
assertEquals("firstMethod", bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$firstMethod").getMethod().getName());
|
||||||
|
assertEquals("secondMethod", bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$secondMethod").getMethod().getName());
|
||||||
|
try {
|
||||||
|
bindings.getBinding(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE, "$thirdMethod");
|
||||||
|
fail();
|
||||||
|
} catch(NotImplementedOperationException e){
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,116 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import javax.ws.rs.HttpMethod;
|
||||||
|
import javax.ws.rs.core.MultivaluedHashMap;
|
||||||
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.glassfish.jersey.internal.MapPropertiesDelegate;
|
||||||
|
import org.glassfish.jersey.server.ContainerRequest;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
|
||||||
|
public class JaxRsRequestTest {
|
||||||
|
|
||||||
|
private static final String RESOURCE_STRING = "</Patient>";
|
||||||
|
private static final String BASEURI = "http://baseuri";
|
||||||
|
private static final String REQUESTURI = "http://baseuri/test";
|
||||||
|
|
||||||
|
private JaxRsRequest details;
|
||||||
|
private MultivaluedMap<String, String> queryParameters = new MultivaluedHashMap<String, String>();
|
||||||
|
private ContainerRequest headers;
|
||||||
|
private TestJaxRsDummyPatientProvider provider;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws URISyntaxException {
|
||||||
|
details = createRequestDetails();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetHeader() {
|
||||||
|
String headerKey = "key";
|
||||||
|
String headerValue = "location_value";
|
||||||
|
String headerValue2 = "location_value_2";
|
||||||
|
assertTrue(StringUtils.isBlank(details.getHeader(headerKey)));
|
||||||
|
headers.header(headerKey, headerValue);
|
||||||
|
assertEquals(headerValue, details.getHeader(headerKey));
|
||||||
|
assertEquals(Arrays.asList(headerValue), details.getHeaders(headerKey));
|
||||||
|
|
||||||
|
headers.header(headerKey, headerValue2);
|
||||||
|
assertEquals(headerValue, details.getHeader(headerKey));
|
||||||
|
assertEquals(Arrays.asList(headerValue, headerValue2), details.getHeaders(headerKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetByteStreamRequestContents() {
|
||||||
|
assertEquals(RESOURCE_STRING, new String(details.getByteStreamRequestContents()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testServerBaseForRequest() {
|
||||||
|
assertEquals(BASEURI, new String(details.getServerBaseForRequest()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetResponse() {
|
||||||
|
JaxRsResponse response = (JaxRsResponse) details.getResponse();
|
||||||
|
assertEquals(details, response.getRequestDetails());
|
||||||
|
assertTrue(response == details.getResponse());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnsupportedOperationException.class)
|
||||||
|
public void testGetReader() throws IOException {
|
||||||
|
details.getReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnsupportedOperationException.class)
|
||||||
|
public void testGetInputStream() {
|
||||||
|
details.getInputStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetServerBaseForRequest() {
|
||||||
|
assertEquals(JaxRsRequestTest.BASEURI, details.getFhirServerBase());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetServer() {
|
||||||
|
assertEquals(this.provider, details.getServer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public JaxRsRequest createRequestDetails() throws URISyntaxException {
|
||||||
|
//headers
|
||||||
|
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null, new MapPropertiesDelegate());
|
||||||
|
|
||||||
|
//uri info
|
||||||
|
UriInfo uriInfo = mock(UriInfo.class);
|
||||||
|
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
|
||||||
|
|
||||||
|
//mocks
|
||||||
|
provider = spy(TestJaxRsDummyPatientProvider.class);
|
||||||
|
doReturn(uriInfo).when(provider).getUriInfo();
|
||||||
|
doReturn(BASEURI).when(provider).getBaseForRequest();
|
||||||
|
doReturn(BASEURI).when(provider).getBaseForServer();
|
||||||
|
doReturn(headers).when(provider).getHeaders();
|
||||||
|
|
||||||
|
return new JaxRsRequest(provider, RESOURCE_STRING, RequestTypeEnum.GET, RestOperationTypeEnum.HISTORY_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,166 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Binary;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
|
import ca.uhn.fhir.rest.method.ParseAction;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||||
|
|
||||||
|
public class JaxRsResponseTest {
|
||||||
|
|
||||||
|
private JaxRsResponse response;
|
||||||
|
private JaxRsRequest request;
|
||||||
|
private Bundle bundle;
|
||||||
|
private Set<SummaryEnum> theSummaryMode;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws URISyntaxException {
|
||||||
|
request = new JaxRsRequestTest().createRequestDetails();
|
||||||
|
this.response = (JaxRsResponse) request.getResponse();
|
||||||
|
bundle = getSinglePatientResource();
|
||||||
|
theSummaryMode = Collections.<SummaryEnum>emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetResponseWriterNoZipNoBrowser() throws IOException {
|
||||||
|
boolean theRequestIsBrowser = false;
|
||||||
|
boolean respondGzip = false;
|
||||||
|
Set<SummaryEnum> theSummaryMode = Collections.<SummaryEnum>emptySet();
|
||||||
|
Response result = (Response) RestfulServerUtils.streamResponseAsBundle(request.getServer(), bundle, theSummaryMode, theRequestIsBrowser, respondGzip, request);
|
||||||
|
assertEquals(200, result.getStatus());
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
assertTrue(result.getEntity().toString().contains("Patient"));
|
||||||
|
assertTrue(result.getEntity().toString().contains("15"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetResponseWriterBrowserNoZip() throws IOException {
|
||||||
|
boolean theRequestIsBrowser = true;
|
||||||
|
boolean respondGzip = false;
|
||||||
|
Response result = (Response) RestfulServerUtils.streamResponseAsBundle(request.getServer(), bundle, theSummaryMode, theRequestIsBrowser, respondGzip, request);
|
||||||
|
assertEquals(200, result.getStatus());
|
||||||
|
assertEquals(Constants.CT_JSON+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
assertTrue(result.getEntity().toString().contains("Patient"));
|
||||||
|
assertTrue(result.getEntity().toString().contains("15"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSendAttachmentResponse() throws IOException {
|
||||||
|
boolean theRequestIsBrowser = true;
|
||||||
|
boolean respondGzip = true;
|
||||||
|
IBaseBinary binary = new Binary();
|
||||||
|
String contentType = "foo";
|
||||||
|
byte[] content = new byte[] { 1, 2, 3, 4 };
|
||||||
|
binary.setContentType(contentType);
|
||||||
|
binary.setContent(content);
|
||||||
|
boolean theAddContentLocationHeader = false;
|
||||||
|
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theRequestIsBrowser, theSummaryMode, 200, respondGzip, theAddContentLocationHeader, respondGzip, this.request);
|
||||||
|
assertEquals(200, result.getStatus());
|
||||||
|
assertEquals(contentType, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
assertEquals(content, result.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSendAttachmentResponseNoContent() throws IOException {
|
||||||
|
boolean theRequestIsBrowser = true;
|
||||||
|
boolean respondGzip = true;
|
||||||
|
IBaseBinary binary = new Binary();
|
||||||
|
binary.setContent(new byte[]{});
|
||||||
|
boolean theAddContentLocationHeader = false;
|
||||||
|
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theRequestIsBrowser, theSummaryMode, 200, respondGzip, theAddContentLocationHeader, respondGzip, this.request);
|
||||||
|
assertEquals(200, result.getStatus());
|
||||||
|
assertEquals(null, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
assertEquals(null, result.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSendAttachmentResponseEmptyContent() throws IOException {
|
||||||
|
boolean theRequestIsBrowser = true;
|
||||||
|
boolean respondGzip = true;
|
||||||
|
IBaseBinary binary = new Binary();
|
||||||
|
boolean theAddContentLocationHeader = false;
|
||||||
|
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), binary, theRequestIsBrowser, theSummaryMode, 200, respondGzip, theAddContentLocationHeader, respondGzip, this.request);
|
||||||
|
assertEquals(200, result.getStatus());
|
||||||
|
assertEquals(null, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
assertEquals(null, result.getEntity());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReturnResponse() throws IOException {
|
||||||
|
IdDt theId = new IdDt(15L);
|
||||||
|
ParseAction<?> outcome = ParseAction.create(createPatient());
|
||||||
|
int operationStatus = 200;
|
||||||
|
boolean allowPrefer = true;
|
||||||
|
String resourceName = "Patient";
|
||||||
|
MethodOutcome methodOutcome = new MethodOutcome(theId);
|
||||||
|
Response result = response.returnResponse(outcome, operationStatus, allowPrefer, methodOutcome, resourceName);
|
||||||
|
assertEquals(200, result.getStatus());
|
||||||
|
assertEquals(Constants.CT_JSON+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
System.out.println(result.getEntity().toString());
|
||||||
|
assertTrue(result.getEntity().toString().contains("resourceType\":\"Patient"));
|
||||||
|
assertTrue(result.getEntity().toString().contains("15"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReturnResponseAsXml() throws IOException {
|
||||||
|
IdDt theId = new IdDt(15L);
|
||||||
|
ParseAction<?> outcome = ParseAction.create(createPatient());
|
||||||
|
int operationStatus = 200;
|
||||||
|
boolean allowPrefer = true;
|
||||||
|
String resourceName = "Patient";
|
||||||
|
MethodOutcome methodOutcome = new MethodOutcome(theId);
|
||||||
|
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML});
|
||||||
|
Response result = response.returnResponse(outcome, operationStatus, allowPrefer, methodOutcome, resourceName);
|
||||||
|
assertEquals(200, result.getStatus());
|
||||||
|
assertEquals(Constants.CT_XML+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
assertTrue(result.getEntity().toString().contains("<Patient"));
|
||||||
|
assertTrue(result.getEntity().toString().contains("15"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoOutcomeXml() throws IOException {
|
||||||
|
ParseAction<?> outcome = ParseAction.create((IBaseResource) null);
|
||||||
|
int operationStatus = Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||||
|
boolean allowPrefer = true;
|
||||||
|
String resourceName = "Patient";
|
||||||
|
MethodOutcome methodOutcome = new MethodOutcome(null);
|
||||||
|
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML});
|
||||||
|
Response result = response.returnResponse(outcome, operationStatus, allowPrefer, methodOutcome, resourceName);
|
||||||
|
assertEquals(204, result.getStatus());
|
||||||
|
assertEquals(Constants.CT_XML+Constants.CHARSET_UTF8_CTSUFFIX, result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Bundle getSinglePatientResource() {
|
||||||
|
Patient theResource = createPatient();
|
||||||
|
Bundle bundle = Bundle.withSingleResource(theResource);
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Patient createPatient() {
|
||||||
|
Patient theResource = new Patient();
|
||||||
|
theResource.setId(new IdDt(15L));
|
||||||
|
return theResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
<configuration>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="org.eclipse" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.apache" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.thymeleaf" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
106
hapi-fhir-jaxrsserver-example/pom.xml
Normal file
106
hapi-fhir-jaxrsserver-example/pom.xml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<!-- Note: HAPI projects use the "hapi-fhir" POM as their base to provide easy management. You do not need to use this in your own projects, so the "parent" tag and it's contents below may be removed
|
||||||
|
if you are using this file as a basis for your own project. -->
|
||||||
|
<parent>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir</artifactId>
|
||||||
|
<version>1.4-SNAPSHOT</version>
|
||||||
|
<relativePath>../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>hapi-fhir-jaxrsserver-example</artifactId>
|
||||||
|
<packaging>war</packaging>
|
||||||
|
|
||||||
|
<name>HAPI FHIR JAX-RS Server - Example</name>
|
||||||
|
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>oss-snapshots</id>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
|
||||||
|
<!-- This dependency includes the core HAPI-FHIR classes -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
|
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
|
||||||
|
<version>1.4-SNAPSHOT</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.ws.rs</groupId>
|
||||||
|
<artifactId>javax.ws.rs-api</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.ejb</groupId>
|
||||||
|
<artifactId>ejb-api</artifactId>
|
||||||
|
<version>3.0</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-server</artifactId>
|
||||||
|
<version>${jetty_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-servlet</artifactId>
|
||||||
|
<version>${jetty_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.core</groupId>
|
||||||
|
<artifactId>jersey-server</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.containers</groupId>
|
||||||
|
<artifactId>jersey-container-servlet-core</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.containers</groupId>
|
||||||
|
<artifactId>jersey-container-jetty-http</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.glassfish.jersey.media</groupId>
|
||||||
|
<artifactId>jersey-media-moxy</artifactId>
|
||||||
|
<version>${jersey_version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<finalName>hapi-fhir-jaxrsserver-example</finalName>
|
||||||
|
<!-- This is to run the integration tests -->
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-failsafe-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>integration-test</goal>
|
||||||
|
<goal>verify</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
@ -0,0 +1,15 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.example;
|
||||||
|
|
||||||
|
import javax.ws.rs.ApplicationPath;
|
||||||
|
import javax.ws.rs.core.Application;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fhir Patient Demo Application
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
@ApplicationPath(value=FhirPatientDemoApplication.PATH)
|
||||||
|
public class FhirPatientDemoApplication extends Application {
|
||||||
|
/** The demo application path */
|
||||||
|
public final static String PATH = "/jaxrs-demo";
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.example;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsConformanceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conformance Rest Service
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
@Path("")
|
||||||
|
@Stateless
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class JaxRsConformanceProvider extends AbstractJaxRsConformanceProvider {
|
||||||
|
private static final String SERVER_VERSION = "1.0.0";
|
||||||
|
private static final String SERVER_DESCRIPTION = "Jax-Rs Test Example Description";
|
||||||
|
private static final String SERVER_NAME = "Jax-Rs Test Example";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
private JaxRsPatientRestProvider patientProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard Constructor
|
||||||
|
*/
|
||||||
|
public JaxRsConformanceProvider() {
|
||||||
|
super(SERVER_VERSION, SERVER_DESCRIPTION, SERVER_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
|
||||||
|
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> map = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
|
||||||
|
map.put(JaxRsConformanceProvider.class, this);
|
||||||
|
map.put(JaxRsPatientRestProvider.class, patientProvider);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.example;
|
||||||
|
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsPageProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
|
||||||
|
@Path("/")
|
||||||
|
@Stateless
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class JaxRsPageProvider extends AbstractJaxRsPageProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return JaxRsPatientRestProvider.PAGE_PROVIDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,256 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.example;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import javax.ejb.Local;
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Condition;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Delete;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
|
import ca.uhn.fhir.rest.server.AddProfileTagEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.BundleInclusionRule;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A demo JaxRs Patient Rest Provider
|
||||||
|
*
|
||||||
|
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||||
|
*/
|
||||||
|
@Local
|
||||||
|
@Path(JaxRsPatientRestProvider.PATH)
|
||||||
|
@Stateless
|
||||||
|
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||||
|
public class JaxRsPatientRestProvider extends AbstractJaxRsResourceProvider<Patient> {
|
||||||
|
|
||||||
|
private static Long counter = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HAPI paging provider for this server
|
||||||
|
*/
|
||||||
|
public static final IPagingProvider PAGE_PROVIDER;
|
||||||
|
|
||||||
|
static final String PATH = "/Patient";
|
||||||
|
private static final ConcurrentHashMap<String, List<Patient>> patients = new ConcurrentHashMap<String, List<Patient>>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
PAGE_PROVIDER = new FifoMemoryPagingProvider(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
patients.put(String.valueOf(counter), createPatient("Van Houte"));
|
||||||
|
patients.put(String.valueOf(counter), createPatient("Agnew"));
|
||||||
|
for (int i = 0; i < 20; i++) {
|
||||||
|
patients.put(String.valueOf(counter), createPatient("Random Patient " + counter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JaxRsPatientRestProvider() {
|
||||||
|
super(JaxRsPatientRestProvider.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional) throws Exception {
|
||||||
|
patients.put("" + counter, createPatient(patient));
|
||||||
|
final MethodOutcome result = new MethodOutcome().setCreated(true);
|
||||||
|
result.setResource(patient);
|
||||||
|
result.setId(patient.getId());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
public MethodOutcome delete(@IdParam final IdDt theId) {
|
||||||
|
final Patient deletedPatient = find(theId);
|
||||||
|
patients.remove(deletedPatient.getId().getIdPart());
|
||||||
|
final MethodOutcome result = new MethodOutcome().setCreated(true);
|
||||||
|
result.setResource(deletedPatient);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Read
|
||||||
|
public Patient find(@IdParam final IdDt theId) {
|
||||||
|
if (patients.containsKey(theId.getIdPart())) {
|
||||||
|
return getLast(patients.get(theId.getIdPart()));
|
||||||
|
} else {
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Read(version = true)
|
||||||
|
public Patient findHistory(@IdParam final IdDt theId) {
|
||||||
|
if (patients.containsKey(theId.getIdPart())) {
|
||||||
|
final List<Patient> list = patients.get(theId.getIdPart());
|
||||||
|
for (final Patient patient : list) {
|
||||||
|
if (patient.getId().getVersionIdPartAsLong().equals(theId.getVersionIdPartAsLong())) {
|
||||||
|
return patient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(name = "firstVersion", idempotent = true, returnParameters = { @OperationParam(name = "return", type = StringDt.class) })
|
||||||
|
public Parameters firstVersion(@IdParam final IdDt theId, @OperationParam(name = "dummy") StringDt dummyInput) {
|
||||||
|
Parameters parameters = new Parameters();
|
||||||
|
Patient patient = find(new IdDt(theId.getResourceType(), theId.getIdPart(), "0"));
|
||||||
|
parameters.addParameter().setName("return").setResource(patient).setValue(new StringDt((counter - 1) + "" + "inputVariable [ " + dummyInput.getValue() + "]"));
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AddProfileTagEnum getAddProfileTag() {
|
||||||
|
return AddProfileTagEnum.NEVER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BundleInclusionRule getBundleInclusionRule() {
|
||||||
|
return BundleInclusionRule.BASED_ON_INCLUDES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ETagSupportEnum getETagSupport() {
|
||||||
|
return ETagSupportEnum.DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** THE DEFAULTS */
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IServerInterceptor> getInterceptors() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Patient getLast(final List<Patient> list) {
|
||||||
|
return list.get(list.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPagingProvider getPagingProvider() {
|
||||||
|
return PAGE_PROVIDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<Patient> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefaultPrettyPrint() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUseBrowserFriendlyContentTypes() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Path("/{id}/$firstVersion")
|
||||||
|
public Response operationFirstVersionUsingGet(@PathParam("id") String id) throws IOException {
|
||||||
|
return customOperation(null, RequestTypeEnum.GET, id, "$firstVersion", RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@POST
|
||||||
|
@Path("/{id}/$firstVersion")
|
||||||
|
public Response operationFirstVersionUsingGet(@PathParam("id") String id, final String resource) throws Exception {
|
||||||
|
return customOperation(resource, RequestTypeEnum.POST, id, "$firstVersion", RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search
|
||||||
|
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name) {
|
||||||
|
final List<Patient> result = new LinkedList<Patient>();
|
||||||
|
for (final List<Patient> patientIterator : patients.values()) {
|
||||||
|
Patient single = null;
|
||||||
|
for (Patient patient : patientIterator) {
|
||||||
|
if (name == null || patient.getNameFirstRep().getFamilyFirstRep().getValueNotNull().equals(name.getValueNotNull())) {
|
||||||
|
single = patient;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (single != null) {
|
||||||
|
result.add(single);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Search(compartmentName = "Condition")
|
||||||
|
public List<IResource> searchCompartment(@IdParam IdDt thePatientId) {
|
||||||
|
List<IResource> retVal = new ArrayList<IResource>();
|
||||||
|
Condition condition = new Condition();
|
||||||
|
condition.setId(new IdDt("665577"));
|
||||||
|
retVal.add(condition);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Update
|
||||||
|
public MethodOutcome update(@IdParam final IdDt theId, @ResourceParam final Patient patient) {
|
||||||
|
final String idPart = theId.getIdPart();
|
||||||
|
if (patients.containsKey(idPart)) {
|
||||||
|
final List<Patient> patientList = patients.get(idPart);
|
||||||
|
final Patient lastPatient = getLast(patientList);
|
||||||
|
patient.setId(createId(theId.getIdPartAsLong(), lastPatient.getId().getVersionIdPartAsLong() + 1));
|
||||||
|
patientList.add(patient);
|
||||||
|
final MethodOutcome result = new MethodOutcome().setCreated(false);
|
||||||
|
result.setResource(patient);
|
||||||
|
result.setId(patient.getId());
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
throw new ResourceNotFoundException(theId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IdDt createId(final Long id, final Long theVersionId) {
|
||||||
|
return new IdDt("Patient", "" + id, "" + theVersionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Patient> createPatient(final Patient patient) {
|
||||||
|
patient.setId(createId(counter, 1L));
|
||||||
|
final LinkedList<Patient> list = new LinkedList<Patient>();
|
||||||
|
list.add(patient);
|
||||||
|
counter++;
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Patient> createPatient(final String name) {
|
||||||
|
final Patient patient = new Patient();
|
||||||
|
patient.getNameFirstRep().addFamily(name);
|
||||||
|
return createPatient(patient);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
version="3.0" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ./xsd/web-app_3_0.xsd">
|
||||||
|
|
||||||
|
<!-- This filters provide support for Cross Origin Resource Sharing (CORS) -->
|
||||||
|
<!--
|
||||||
|
<filter>
|
||||||
|
<filter-name>CORS Filter</filter-name>
|
||||||
|
<filter-class>org.ebaysf.web.cors.CORSFilter</filter-class>
|
||||||
|
<init-param>
|
||||||
|
<description>A comma separated list of allowed origins. Note: An '*' cannot be used for an allowed origin when using credentials.</description>
|
||||||
|
<param-name>cors.allowed.origins</param-name>
|
||||||
|
<param-value>*</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<description>A comma separated list of HTTP verbs, using which a CORS request can be made.</description>
|
||||||
|
<param-name>cors.allowed.methods</param-name>
|
||||||
|
<param-value>GET,POST,PUT,DELETE,OPTIONS</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<description>A comma separated list of allowed headers when making a non simple CORS request.</description>
|
||||||
|
<param-name>cors.allowed.headers</param-name>
|
||||||
|
<param-value>X-FHIR-Starter,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<description>A comma separated list non-standard response headers that will be exposed to XHR2 object.</description>
|
||||||
|
<param-name>cors.exposed.headers</param-name>
|
||||||
|
<param-value>Location,Content-Location</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<description>A flag that suggests if CORS is supported with cookies</description>
|
||||||
|
<param-name>cors.support.credentials</param-name>
|
||||||
|
<param-value>true</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<description>A flag to control logging</description>
|
||||||
|
<param-name>cors.logging.enabled</param-name>
|
||||||
|
<param-value>true</param-value>
|
||||||
|
</init-param>
|
||||||
|
<init-param>
|
||||||
|
<description>Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache.</description>
|
||||||
|
<param-name>cors.preflight.maxage</param-name>
|
||||||
|
<param-value>300</param-value>
|
||||||
|
</init-param>
|
||||||
|
</filter>
|
||||||
|
<filter-mapping>
|
||||||
|
<filter-name>CORS Filter</filter-name>
|
||||||
|
<url-pattern>/*</url-pattern>
|
||||||
|
</filter-mapping>
|
||||||
|
-->
|
||||||
|
|
||||||
|
</web-app>
|
@ -0,0 +1,311 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.example;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
|
import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Conformance;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.DateDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
|
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
|
public class JaxRsPatientProviderTest {
|
||||||
|
|
||||||
|
private static IGenericClient client;
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forDstu2();
|
||||||
|
private static final String PATIENT_NAME = "Van Houte";
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server jettyServer;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void setUpClass()
|
||||||
|
throws Exception {
|
||||||
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
|
||||||
|
context.setContextPath("/");
|
||||||
|
System.out.println(ourPort);
|
||||||
|
jettyServer = new Server(ourPort);
|
||||||
|
jettyServer.setHandler(context);
|
||||||
|
ServletHolder jerseyServlet = context.addServlet(org.glassfish.jersey.servlet.ServletContainer.class, "/*");
|
||||||
|
jerseyServlet.setInitOrder(0);
|
||||||
|
//@formatter:off
|
||||||
|
jerseyServlet.setInitParameter("jersey.config.server.provider.classnames",
|
||||||
|
StringUtils.join(Arrays.asList(
|
||||||
|
JaxRsConformanceProvider.class.getCanonicalName(),
|
||||||
|
JaxRsPatientRestProvider.class.getCanonicalName(),
|
||||||
|
JaxRsPageProvider.class.getCanonicalName()
|
||||||
|
), ";"));
|
||||||
|
//@formatter:on
|
||||||
|
jettyServer.start();
|
||||||
|
|
||||||
|
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
|
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
|
||||||
|
client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort + "/");
|
||||||
|
client.setEncoding(EncodingEnum.JSON);
|
||||||
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void tearDownClass()
|
||||||
|
throws Exception {
|
||||||
|
try {
|
||||||
|
jettyServer.destroy();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search/Query - Type */
|
||||||
|
@Test
|
||||||
|
public void findUsingGenericClientBySearch() {
|
||||||
|
// Perform a search
|
||||||
|
final ca.uhn.fhir.model.api.Bundle results = client.search().forResource(Patient.class)
|
||||||
|
.where(Patient.NAME.matchesExactly().value(PATIENT_NAME)).execute();
|
||||||
|
System.out.println(results.getEntries().get(0));
|
||||||
|
assertEquals(results.getEntries().size(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Multi-valued Parameters (ANY/OR) */
|
||||||
|
@Test
|
||||||
|
public void findUsingGenericClientBySearchWithMultiValues() {
|
||||||
|
final ca.uhn.fhir.model.api.Bundle response = client.search().forResource(Patient.class)
|
||||||
|
.where(Patient.ADDRESS.matches().values("Toronto")).and(Patient.ADDRESS.matches().values("Ontario"))
|
||||||
|
.and(Patient.ADDRESS.matches().values("Canada"))
|
||||||
|
.where(Patient.IDENTIFIER.exactly().systemAndIdentifier("SHORTNAME", "TOYS")).execute();
|
||||||
|
System.out.println(response.getEntries().get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Paging */
|
||||||
|
@Test
|
||||||
|
public void findWithPaging() {
|
||||||
|
// Perform a search
|
||||||
|
final Bundle results = client.search().forResource(Patient.class).limitTo(8).returnBundle(Bundle.class).execute();
|
||||||
|
System.out.println(results.getEntry().size());
|
||||||
|
|
||||||
|
if (results.getLink(Bundle.LINK_NEXT) != null) {
|
||||||
|
|
||||||
|
// load next page
|
||||||
|
final Bundle nextPage = client.loadPage().next(results).execute();
|
||||||
|
System.out.println(nextPage.getEntry().size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search using other query options */
|
||||||
|
public void testOther() {
|
||||||
|
//missing
|
||||||
|
}
|
||||||
|
|
||||||
|
/** */
|
||||||
|
@Test
|
||||||
|
public void testSearchPost() {
|
||||||
|
Bundle response = client.search()
|
||||||
|
.forResource("Patient")
|
||||||
|
.usingStyle(SearchStyleEnum.POST)
|
||||||
|
.returnBundle(Bundle.class)
|
||||||
|
.execute();
|
||||||
|
assertTrue(response.getEntry().size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Compartments */
|
||||||
|
@Test
|
||||||
|
public void testSearchCompartements() {
|
||||||
|
Bundle response = client.search()
|
||||||
|
.forResource(Patient.class)
|
||||||
|
.withIdAndCompartment("1", "Condition")
|
||||||
|
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||||
|
.execute();
|
||||||
|
assertTrue(response.getEntry().size() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search - Subsetting (_summary and _elements) */
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testSummary() {
|
||||||
|
client.search()
|
||||||
|
.forResource(Patient.class)
|
||||||
|
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreatePatient() {
|
||||||
|
final Patient existing = new Patient();
|
||||||
|
existing.setId((IdDt) null);
|
||||||
|
existing.getNameFirstRep().addFamily("Created Patient 54");
|
||||||
|
client.setEncoding(EncodingEnum.JSON);
|
||||||
|
final MethodOutcome results = client.create().resource(existing).prefer(PreferReturnEnum.REPRESENTATION).execute();
|
||||||
|
System.out.println(results.getId());
|
||||||
|
final Patient patient = (Patient) results.getResource();
|
||||||
|
System.out.println(patient);
|
||||||
|
assertNotNull(client.read(patient.getId()));
|
||||||
|
client.setEncoding(EncodingEnum.JSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Conditional Creates */
|
||||||
|
@Test
|
||||||
|
public void testConditionalCreate() {
|
||||||
|
final Patient existing = new Patient();
|
||||||
|
existing.setId((IdDt) null);
|
||||||
|
existing.getNameFirstRep().addFamily("Created Patient 54");
|
||||||
|
client.setEncoding(EncodingEnum.XML);
|
||||||
|
final MethodOutcome results = client.create().resource(existing).prefer(PreferReturnEnum.REPRESENTATION).execute();
|
||||||
|
System.out.println(results.getId());
|
||||||
|
final Patient patient = (Patient) results.getResource();
|
||||||
|
|
||||||
|
client.create()
|
||||||
|
.resource(patient)
|
||||||
|
.conditional()
|
||||||
|
.where(Patient.IDENTIFIER.exactly().identifier(patient.getIdentifierFirstRep()))
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Find By Id */
|
||||||
|
@Test
|
||||||
|
public void findUsingGenericClientById() {
|
||||||
|
final Patient results = client.read(Patient.class, "1");
|
||||||
|
assertEquals(results.getId().getIdPartAsLong().longValue(), 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateById() {
|
||||||
|
final Patient existing = client.read(Patient.class, "1");
|
||||||
|
final List<HumanNameDt> name = existing.getName();
|
||||||
|
name.get(0).addSuffix("The Second");
|
||||||
|
existing.setName(name);
|
||||||
|
client.setEncoding(EncodingEnum.XML);
|
||||||
|
final MethodOutcome results = client.update("1", existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeletePatient() {
|
||||||
|
final Patient existing = new Patient();
|
||||||
|
existing.getNameFirstRep().addFamily("Created Patient XYZ");
|
||||||
|
final MethodOutcome results = client.create().resource(existing).prefer(PreferReturnEnum.REPRESENTATION).execute();
|
||||||
|
System.out.println(results.getId());
|
||||||
|
final Patient patient = (Patient) results.getResource();
|
||||||
|
client.delete(Patient.class, patient.getId());
|
||||||
|
try {
|
||||||
|
client.read(patient.getId());
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (final Exception e) {
|
||||||
|
//assertEquals(e.getStatusCode(), Constants.STATUS_HTTP_404_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transaction - Server */
|
||||||
|
@Ignore
|
||||||
|
@Test
|
||||||
|
public void testTransaction() {
|
||||||
|
ca.uhn.fhir.model.api.Bundle bundle = new ca.uhn.fhir.model.api.Bundle();
|
||||||
|
BundleEntry entry = bundle.addEntry();
|
||||||
|
final Patient existing = new Patient();
|
||||||
|
existing.getNameFirstRep().addFamily("Created with bundle");
|
||||||
|
entry.setResource(existing);
|
||||||
|
|
||||||
|
BoundCodeDt<BundleEntryTransactionMethodEnum> theTransactionOperation =
|
||||||
|
new BoundCodeDt(
|
||||||
|
BundleEntryTransactionMethodEnum.VALUESET_BINDER,
|
||||||
|
BundleEntryTransactionMethodEnum.POST);
|
||||||
|
entry.setTransactionMethod(theTransactionOperation);
|
||||||
|
ca.uhn.fhir.model.api.Bundle response = client.transaction().withBundle(bundle).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Conformance - Server */
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testConformance() {
|
||||||
|
final Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
|
||||||
|
System.out.println(conf.getRest().get(0).getResource().get(0).getType());
|
||||||
|
assertEquals(conf.getRest().get(0).getResource().get(0).getType().toString(), "Patient");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extended Operations */
|
||||||
|
// Create a client to talk to the HeathIntersections server
|
||||||
|
@Test
|
||||||
|
public void testExtendedOperations() {
|
||||||
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
|
|
||||||
|
// Create the input parameters to pass to the server
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
|
||||||
|
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
|
||||||
|
inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue"));
|
||||||
|
|
||||||
|
// Invoke $everything on "Patient/1"
|
||||||
|
Parameters outParams = client
|
||||||
|
.operation()
|
||||||
|
.onInstance(new IdDt("Patient", "1"))
|
||||||
|
.named("$firstVersion")
|
||||||
|
.withParameters(inParams)
|
||||||
|
//.useHttpGet() // Use HTTP GET instead of POST
|
||||||
|
.execute();
|
||||||
|
String resultValue = outParams.getParameter().get(0).getValue().toString();
|
||||||
|
System.out.println(resultValue);
|
||||||
|
assertEquals("expected but found : "+ resultValue, resultValue.contains("myAwesomeDummyValue"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtendedOperationsUsingGet() {
|
||||||
|
// Create the input parameters to pass to the server
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("start").setValue(new DateDt("2001-01-01"));
|
||||||
|
inParams.addParameter().setName("end").setValue(new DateDt("2015-03-01"));
|
||||||
|
inParams.addParameter().setName("dummy").setValue(new StringDt("myAwesomeDummyValue"));
|
||||||
|
|
||||||
|
// Invoke $everything on "Patient/1"
|
||||||
|
Parameters outParams = client
|
||||||
|
.operation()
|
||||||
|
.onInstance(new IdDt("Patient", "1"))
|
||||||
|
.named("$firstVersion")
|
||||||
|
.withParameters(inParams)
|
||||||
|
.useHttpGet() // Use HTTP GET instead of POST
|
||||||
|
.execute();
|
||||||
|
String resultValue = outParams.getParameter().get(0).getValue().toString();
|
||||||
|
System.out.println(resultValue);
|
||||||
|
assertEquals("expected but found : "+ resultValue, resultValue.contains("myAwesomeDummyValue"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVRead() {
|
||||||
|
final Patient patient = client.vread(Patient.class, "1", "1");
|
||||||
|
System.out.println(patient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRead() {
|
||||||
|
final Patient patient = client.read(Patient.class, "1");
|
||||||
|
System.out.println(patient);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package ca.uhn.fhir.jaxrs.server.example;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides server ports
|
||||||
|
*/
|
||||||
|
public class RandomServerPortProvider {
|
||||||
|
|
||||||
|
private static List<Integer> ourPorts = new ArrayList<Integer>();
|
||||||
|
|
||||||
|
public static int findFreePort() {
|
||||||
|
ServerSocket server;
|
||||||
|
try {
|
||||||
|
server = new ServerSocket(0);
|
||||||
|
int port = server.getLocalPort();
|
||||||
|
ourPorts.add(port);
|
||||||
|
server.close();
|
||||||
|
Thread.sleep(500);
|
||||||
|
return port;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> list() {
|
||||||
|
return ourPorts;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
|||||||
|
<configuration>
|
||||||
|
|
||||||
|
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
|
<encoder>
|
||||||
|
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] - %msg%n
|
||||||
|
</pattern>
|
||||||
|
</encoder>
|
||||||
|
</appender>
|
||||||
|
|
||||||
|
<logger name="org.eclipse" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.apache" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="org.thymeleaf" additivity="false" level="warn">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<root level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</root>
|
||||||
|
|
||||||
|
</configuration>
|
@ -55,6 +55,7 @@ import com.google.common.collect.ArrayListMultimap;
|
|||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||||
|
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
@ -84,6 +85,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
import ca.uhn.fhir.rest.server.exceptions.NotModifiedException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
||||||
@ -119,7 +121,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||||||
subRequestBundle.setType(BundleTypeEnum.TRANSACTION);
|
subRequestBundle.setType(BundleTypeEnum.TRANSACTION);
|
||||||
subRequestBundle.addEntry(nextRequestEntry);
|
subRequestBundle.addEntry(nextRequestEntry);
|
||||||
|
|
||||||
Bundle subResponseBundle = transaction(theRequestDetails, subRequestBundle, "Batch sub-request");
|
Bundle subResponseBundle = transaction((ServletRequestDetails) theRequestDetails, subRequestBundle, "Batch sub-request");
|
||||||
return subResponseBundle;
|
return subResponseBundle;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -242,11 +244,11 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||||||
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
|
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
|
||||||
|
|
||||||
String actionName = "Transaction";
|
String actionName = "Transaction";
|
||||||
return transaction(theRequestDetails, theRequest, actionName);
|
return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
|
private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
|
||||||
BundleTypeEnum transactionType = theRequest.getTypeElement().getValueAsEnum();
|
BundleTypeEnum transactionType = theRequest.getTypeElement().getValueAsEnum();
|
||||||
if (transactionType == BundleTypeEnum.BATCH) {
|
if (transactionType == BundleTypeEnum.BATCH) {
|
||||||
return batch(theRequestDetails, theRequest);
|
return batch(theRequestDetails, theRequest);
|
||||||
@ -491,7 +493,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
|||||||
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
|
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
|
||||||
Entry nextRespEntry = response.getEntry().get(originalOrder);
|
Entry nextRespEntry = response.getEntry().get(originalOrder);
|
||||||
|
|
||||||
RequestDetails requestDetails = new RequestDetails();
|
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
|
||||||
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
|
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
|
||||||
requestDetails.setRequestType(RequestTypeEnum.GET);
|
requestDetails.setRequestType(RequestTypeEnum.GET);
|
||||||
requestDetails.setServer(theRequestDetails.getServer());
|
requestDetails.setServer(theRequestDetails.getServer());
|
||||||
|
@ -31,6 +31,7 @@ import org.jboss.logging.MDC;
|
|||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
public class BaseJpaProvider {
|
public class BaseJpaProvider {
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ public class BaseJpaProvider {
|
|||||||
MDC.remove(REMOTE_UA);
|
MDC.remove(REMOTE_UA);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endRequest(RequestDetails theRequest) {
|
public void endRequest(ServletRequestDetails theRequest) {
|
||||||
endRequest(theRequest.getServletRequest());
|
endRequest(theRequest.getServletRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +93,7 @@ public class BaseJpaProvider {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startRequest(RequestDetails theRequest) {
|
public void startRequest(ServletRequestDetails theRequest) {
|
||||||
startRequest(theRequest.getServletRequest());
|
startRequest(theRequest.getServletRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,16 +26,17 @@ import ca.uhn.fhir.model.api.IResource;
|
|||||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
public class JpaSystemProviderDstu1 extends BaseJpaSystemProvider<List<IResource>> {
|
public class JpaSystemProviderDstu1 extends BaseJpaSystemProvider<List<IResource>> {
|
||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
public List<IResource> transaction(RequestDetails theRequestDetails, @TransactionParam List<IResource> theResources) {
|
public List<IResource> transaction(RequestDetails theRequestDetails, @TransactionParam List<IResource> theResources) {
|
||||||
startRequest(theRequestDetails);
|
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||||
try {
|
try {
|
||||||
return getDao().transaction(theRequestDetails, theResources);
|
return getDao().transaction(theRequestDetails, theResources);
|
||||||
} finally {
|
} finally {
|
||||||
endRequest(theRequestDetails);
|
endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,6 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
@ -50,6 +48,7 @@ import ca.uhn.fhir.rest.annotation.Transaction;
|
|||||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider<Bundle> {
|
public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider<Bundle> {
|
||||||
|
|
||||||
@ -227,11 +226,11 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider<Bundle> {
|
|||||||
|
|
||||||
@Transaction
|
@Transaction
|
||||||
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
|
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
|
||||||
startRequest(theRequestDetails);
|
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||||
try {
|
try {
|
||||||
return getDao().transaction(theRequestDetails, theResources);
|
return getDao().transaction(theRequestDetails, theResources);
|
||||||
} finally {
|
} finally {
|
||||||
endRequest(theRequestDetails);
|
endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
|
public class ServletSubRequestDetails extends ServletRequestDetails {
|
||||||
|
|
||||||
|
private Map<String, ArrayList<String>> myHeaders = new HashMap<String, ArrayList<String>>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String theName) {
|
||||||
|
ArrayList<String> list = myHeaders.get(theName.toLowerCase());
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return list.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getHeaders(String theName) {
|
||||||
|
ArrayList<String> list = myHeaders.get(theName.toLowerCase());
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHeader(String theName, String theValue) {
|
||||||
|
String lowerCase = theName.toLowerCase();
|
||||||
|
ArrayList<String> list = myHeaders.get(lowerCase);
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<String>();
|
||||||
|
myHeaders.put(lowerCase, list);
|
||||||
|
}
|
||||||
|
list.add(theValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -12,17 +12,17 @@ import javax.servlet.http.HttpServletRequest;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.rp.dstu2.PatientResourceProvider;
|
import ca.uhn.fhir.jpa.rp.dstu2.PatientResourceProvider;
|
||||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
|
||||||
public abstract class BaseJpaDstu2SystemTest extends BaseJpaDstu2Test {
|
public abstract class BaseJpaDstu2SystemTest extends BaseJpaDstu2Test {
|
||||||
protected RequestDetails myRequestDetails;
|
protected ServletRequestDetails myRequestDetails;
|
||||||
private RestfulServer myServer;
|
private RestfulServer myServer;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Before
|
@Before
|
||||||
public void before() throws ServletException {
|
public void before() throws ServletException {
|
||||||
myRequestDetails = mock(RequestDetails.class);
|
myRequestDetails = mock(ServletRequestDetails.class);
|
||||||
|
|
||||||
if (myServer == null) {
|
if (myServer == null) {
|
||||||
myServer = new RestfulServer(myFhirCtx);
|
myServer = new RestfulServer(myFhirCtx);
|
||||||
|
@ -55,6 +55,12 @@
|
|||||||
<artifactId>phloc-commons</artifactId>
|
<artifactId>phloc-commons</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Don't force OSGi users to use a newer version of SLF4j than we need -->
|
<!-- Don't force OSGi users to use a newer version of SLF4j than we need -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>xmlunit</groupId>
|
<groupId>xmlunit</groupId>
|
||||||
<artifactId>xmlunit</artifactId>
|
<artifactId>xmlunit</artifactId>
|
||||||
<version>1.5</version>
|
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -197,7 +197,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
|
public void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes) {
|
||||||
int numToReturn;
|
int numToReturn;
|
||||||
String searchId = null;
|
String searchId = null;
|
||||||
List<IBaseResource> resourceList;
|
List<IBaseResource> resourceList;
|
||||||
|
@ -1,34 +1,16 @@
|
|||||||
package ca.uhn.fhir.rest.server.provider;
|
package ca.uhn.fhir.rest.server.provider;
|
||||||
|
|
||||||
/*
|
import java.util.ArrayList;
|
||||||
* #%L
|
import java.util.Collections;
|
||||||
* HAPI FHIR Structures - DSTU1 (FHIR v0.80)
|
import java.util.Comparator;
|
||||||
* %%
|
import java.util.HashSet;
|
||||||
* Copyright (C) 2014 - 2015 University Health Network
|
import java.util.List;
|
||||||
* %%
|
import java.util.Set;
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
import java.util.TreeSet;
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import javax.servlet.ServletContext;
|
||||||
import java.io.InputStream;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.jar.Manifest;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
@ -49,6 +31,7 @@ import ca.uhn.fhir.model.primitive.CodeDt;
|
|||||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
|
import ca.uhn.fhir.rest.method.DynamicSearchMethodBinding;
|
||||||
@ -59,10 +42,9 @@ import ca.uhn.fhir.rest.server.Constants;
|
|||||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.ResourceBinding;
|
import ca.uhn.fhir.rest.server.ResourceBinding;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
|
||||||
import ca.uhn.fhir.util.ExtensionConstants;
|
import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
|
* Server FHIR Provider which serves the conformance statement for a RESTful server implementation
|
||||||
*
|
*
|
||||||
@ -78,12 +60,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
private boolean myCache = true;
|
private boolean myCache = true;
|
||||||
private volatile Conformance myConformance;
|
private volatile Conformance myConformance;
|
||||||
private String myPublisher = "Not provided";
|
private String myPublisher = "Not provided";
|
||||||
private RestfulServer myRestfulServer;
|
private RestulfulServerConfiguration myServerConfiguration;
|
||||||
|
|
||||||
public ServerConformanceProvider(RestfulServer theRestfulServer) {
|
|
||||||
myRestfulServer = theRestfulServer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a no-arg constructor and seetter so that the
|
* Add a no-arg constructor and seetter so that the
|
||||||
* ServerConfirmanceProvider can be Spring-wired with
|
* ServerConfirmanceProvider can be Spring-wired with
|
||||||
@ -95,9 +73,13 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setRestfulServer (RestfulServer theRestfulServer) {
|
public void setRestfulServer (RestfulServer theRestfulServer) {
|
||||||
myRestfulServer = theRestfulServer;
|
myServerConfiguration = theRestfulServer.createConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServerConformanceProvider(RestfulServer theRestfulServer) {
|
||||||
|
myServerConfiguration = theRestfulServer.createConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this
|
* Gets the value of the "publisher" that will be placed in the generated conformance statement. As this
|
||||||
* is a mandatory element, the value should not be null (although this is not enforced). The value defaults
|
* is a mandatory element, the value should not be null (although this is not enforced). The value defaults
|
||||||
@ -126,9 +108,9 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
retVal.setFhirVersion("0.0.82-3059"); // TODO: pull from model
|
retVal.setFhirVersion("0.0.82-3059"); // TODO: pull from model
|
||||||
retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
|
retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
|
||||||
|
|
||||||
retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription());
|
retVal.getImplementation().setDescription(myServerConfiguration.getImplementationDescription());
|
||||||
retVal.getSoftware().setName(myRestfulServer.getServerName());
|
retVal.getSoftware().setName(myServerConfiguration.getServerName());
|
||||||
retVal.getSoftware().setVersion(myRestfulServer.getServerVersion());
|
retVal.getSoftware().setVersion(myServerConfiguration.getServerVersion());
|
||||||
retVal.addFormat(Constants.CT_FHIR_XML);
|
retVal.addFormat(Constants.CT_FHIR_XML);
|
||||||
retVal.addFormat(Constants.CT_FHIR_JSON);
|
retVal.addFormat(Constants.CT_FHIR_JSON);
|
||||||
|
|
||||||
@ -137,7 +119,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
|
|
||||||
Set<RestfulOperationSystemEnum> systemOps = new HashSet<RestfulOperationSystemEnum>();
|
Set<RestfulOperationSystemEnum> systemOps = new HashSet<RestfulOperationSystemEnum>();
|
||||||
|
|
||||||
List<ResourceBinding> bindings = new ArrayList<ResourceBinding>(myRestfulServer.getResourceBindings());
|
List<ResourceBinding> bindings = new ArrayList<ResourceBinding>(myServerConfiguration.getResourceBindings());
|
||||||
Collections.sort(bindings, new Comparator<ResourceBinding>() {
|
Collections.sort(bindings, new Comparator<ResourceBinding>() {
|
||||||
@Override
|
@Override
|
||||||
public int compare(ResourceBinding theArg0, ResourceBinding theArg1) {
|
public int compare(ResourceBinding theArg0, ResourceBinding theArg1) {
|
||||||
@ -151,9 +133,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
RestResource resource = rest.addResource();
|
RestResource resource = rest.addResource();
|
||||||
|
|
||||||
String resourceName = next.getResourceName();
|
String resourceName = next.getResourceName();
|
||||||
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
RuntimeResourceDefinition def = myServerConfiguration.getFhirContext().getResourceDefinition(resourceName);
|
||||||
resource.getType().setValue(def.getName());
|
resource.getType().setValue(def.getName());
|
||||||
resource.getProfile().setReference(new IdDt(def.getResourceProfile(myRestfulServer.getServerBaseForRequest(theRequest))));
|
ServletContext servletContext = theRequest == null ? null : theRequest.getServletContext();
|
||||||
|
String serverBase = myServerConfiguration.getServerAddressStrategy().determineServerBase(servletContext, theRequest);
|
||||||
|
resource.getProfile().setReference(new IdDt(def.getResourceProfile(serverBase)));
|
||||||
|
|
||||||
TreeSet<String> includes = new TreeSet<String>();
|
TreeSet<String> includes = new TreeSet<String>();
|
||||||
|
|
||||||
@ -215,7 +199,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
}
|
}
|
||||||
|
|
||||||
private DateTimeDt conformanceDate() {
|
private DateTimeDt conformanceDate() {
|
||||||
String buildDate = getBuildDateFromManifest();
|
String buildDate = myServerConfiguration.getConformanceDate();
|
||||||
if (buildDate != null) {
|
if (buildDate != null) {
|
||||||
try {
|
try {
|
||||||
return new DateTimeDt(buildDate);
|
return new DateTimeDt(buildDate);
|
||||||
@ -226,21 +210,6 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
return DateTimeDt.withCurrentTime();
|
return DateTimeDt.withCurrentTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getBuildDateFromManifest() {
|
|
||||||
if (myRestfulServer != null && myRestfulServer.getServletContext() != null) {
|
|
||||||
InputStream inputStream = myRestfulServer.getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF");
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
Manifest manifest = new Manifest(inputStream);
|
|
||||||
return manifest.getMainAttributes().getValue("Build-Time");
|
|
||||||
} catch (IOException e) {
|
|
||||||
// fall through
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
|
private void handleDynamicSearchMethodBinding(RestResource resource, RuntimeResourceDefinition def, TreeSet<String> includes, DynamicSearchMethodBinding searchMethodBinding) {
|
||||||
includes.addAll(searchMethodBinding.getIncludes());
|
includes.addAll(searchMethodBinding.getIncludes());
|
||||||
|
|
||||||
@ -348,7 +317,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||||||
param.setDocumentation(nextParamDescription);
|
param.setDocumentation(nextParamDescription);
|
||||||
param.getTypeElement().setValue(nextParameter.getParamType().getCode());
|
param.getTypeElement().setValue(nextParameter.getParamType().getCode());
|
||||||
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
||||||
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
|
RuntimeResourceDefinition targetDef = myServerConfiguration.getFhirContext().getResourceDefinition(nextTarget);
|
||||||
if (targetDef != null) {
|
if (targetDef != null) {
|
||||||
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
|
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
|
@ -62,10 +62,10 @@ public class InterceptorTest {
|
|||||||
public void testInterceptorFires() throws Exception {
|
public void testInterceptorFires() throws Exception {
|
||||||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||||
when(myInterceptor2.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
when(myInterceptor2.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
when(myInterceptor2.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
when(myInterceptor2.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||||
|
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||||
HttpResponse status = ourClient.execute(httpGet);
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
@ -79,8 +79,8 @@ public class InterceptorTest {
|
|||||||
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||||
order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||||
|
|
||||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||||
verifyNoMoreInteractions(myInterceptor1);
|
verifyNoMoreInteractions(myInterceptor1);
|
||||||
verifyNoMoreInteractions(myInterceptor2);
|
verifyNoMoreInteractions(myInterceptor2);
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user