mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-02-08 05:58:27 +00:00
Merge branch 'petervanhoute-master'
This commit is contained in:
commit
c6dede1447
@ -40,6 +40,24 @@
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</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>
|
||||
|
||||
<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.server.Constants;
|
||||
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.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
@ -154,8 +155,8 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
Object[] params = createParametersForServerRequest(theRequest, null);
|
||||
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
Object[] params = createParametersForServerRequest(theRequest);
|
||||
|
||||
params[myIdParamIndex] = theRequest.getId();
|
||||
|
||||
@ -164,7 +165,7 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
||||
}
|
||||
|
||||
IParser parser = createAppropriateParserForParsingServerRequest(theRequest);
|
||||
Reader reader = theRequest.getServletRequest().getReader();
|
||||
Reader reader = theRequest.getReader();
|
||||
try {
|
||||
TagList tagList = parser.parseTagList(reader);
|
||||
params[myTagListParamIndex] = tagList;
|
||||
@ -175,21 +176,13 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest);
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
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();
|
||||
|
||||
return theRequest.getResponse().returnResponse(null, Constants.STATUS_HTTP_200_OK, false, null, null);
|
||||
}
|
||||
|
||||
@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.Delete;
|
||||
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.History;
|
||||
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.IDynamicSearchResourceProvider;
|
||||
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.InternalErrorException;
|
||||
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.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
||||
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
|
||||
|
||||
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 Method myMethod;
|
||||
private List<IParameter> myParameters;
|
||||
@ -131,7 +129,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||
}
|
||||
|
||||
protected IParser createAppropriateParserForParsingServerRequest(RequestDetails theRequest) {
|
||||
String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type");
|
||||
String contentTypeHeader = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
EncodingEnum encoding;
|
||||
if (isBlank(contentTypeHeader)) {
|
||||
encoding = EncodingEnum.XML;
|
||||
@ -151,14 +149,14 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||
return parser;
|
||||
}
|
||||
|
||||
protected Object[] createParametersForServerRequest(RequestDetails theRequest, byte[] theRequestContents) {
|
||||
protected Object[] createParametersForServerRequest(RequestDetails theRequest) {
|
||||
Object[] params = new Object[getParameters().size()];
|
||||
for (int i = 0; i < getParameters().size(); i++) {
|
||||
IParameter param = getParameters().get(i);
|
||||
if (param == null) {
|
||||
continue;
|
||||
}
|
||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theRequestContents, this);
|
||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, this);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
@ -251,9 +249,9 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||
|
||||
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
|
||||
RestOperationTypeEnum operationType = getRestOperationType(theRequest);
|
||||
if (operationType != null) {
|
||||
@ -293,41 +291,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||
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
|
||||
* interceptors.
|
||||
@ -421,12 +384,17 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
||||
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
||||
Transaction transaction = theMethod.getAnnotation(Transaction.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 (!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;
|
||||
}
|
||||
|
||||
if (getPage != null) {
|
||||
return new PageMethodBinding(theContext, theMethod);
|
||||
}
|
||||
|
||||
Class<? extends IBaseResource> returnType;
|
||||
|
||||
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
|
||||
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
|
||||
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.");
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.PreferReturnEnum;
|
||||
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.server.Constants;
|
||||
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.RestfulServerUtils;
|
||||
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);
|
||||
|
||||
/**
|
||||
@ -137,21 +122,18 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
byte[] requestContents = loadRequestContents(theRequest);
|
||||
|
||||
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
// if (requestContainsResource()) {
|
||||
// requestContents = parseIncomingServerResource(theRequest);
|
||||
// } else {
|
||||
// requestContents = null;
|
||||
// }
|
||||
|
||||
Object[] params = createParametersForServerRequest(theRequest, requestContents);
|
||||
Object[] params = createParametersForServerRequest(theRequest);
|
||||
addParametersForServerRequest(theRequest, params);
|
||||
|
||||
HttpServletResponse servletResponse = theRequest.getServletResponse();
|
||||
|
||||
/*
|
||||
* 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;
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; 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) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean allowPrefer = false;
|
||||
return returnResponse(theRequest, response, outcome, resource);
|
||||
}
|
||||
|
||||
private int getOperationStatus(MethodOutcome response) {
|
||||
switch (getRestOperationType()) {
|
||||
case CREATE:
|
||||
if (response == null) {
|
||||
@ -196,23 +181,17 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||
+ " returned null, which is not allowed for create operation");
|
||||
}
|
||||
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
|
||||
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
} else {
|
||||
servletResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
addContentLocationHeaders(theRequest, servletResponse, response);
|
||||
allowPrefer = true;
|
||||
break;
|
||||
|
||||
case UPDATE:
|
||||
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 {
|
||||
servletResponse.setStatus(Constants.STATUS_HTTP_201_CREATED);
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
}
|
||||
addContentLocationHeaders(theRequest, servletResponse, response);
|
||||
allowPrefer = true;
|
||||
break;
|
||||
|
||||
case VALIDATE:
|
||||
case DELETE:
|
||||
@ -221,21 +200,29 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||
if (isReturnVoid() == false) {
|
||||
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 {
|
||||
if (response.getOperationOutcome() == null) {
|
||||
servletResponse.setStatus(Constants.STATUS_HTTP_204_NO_CONTENT);
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
} 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) {
|
||||
String prefer = theRequest.getServletRequest().getHeader(Constants.HEADER_PREFER);
|
||||
String prefer = theRequest.getHeader(Constants.HEADER_PREFER);
|
||||
PreferReturnEnum preferReturn = RestfulServerUtils.parsePreferHeader(prefer);
|
||||
if (preferReturn != null) {
|
||||
if (preferReturn == PreferReturnEnum.REPRESENTATION) {
|
||||
@ -244,31 +231,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
||||
}
|
||||
}
|
||||
|
||||
if (outcome != null) {
|
||||
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);
|
||||
}
|
||||
return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
|
||||
}
|
||||
|
||||
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.FhirContext;
|
||||
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.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
|
@ -35,8 +35,6 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
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.EncodingEnum;
|
||||
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.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
@ -234,9 +232,10 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
@ -245,19 +244,20 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; 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) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
isAddContentLocationHeader(), theRequest);
|
||||
|
||||
return theRequest.getResponse().streamResponseAsResource(responseObject.getResource(), prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(),
|
||||
isAddContentLocationHeader());
|
||||
|
||||
} else {
|
||||
// Is this request coming from a browser
|
||||
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
||||
String uaHeader = theRequest.getHeader("user-agent");
|
||||
boolean requestIsBrowser = false;
|
||||
if (uaHeader != null && uaHeader.contains("Mozilla")) {
|
||||
requestIsBrowser = true;
|
||||
@ -265,26 +265,24 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; 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) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
RestfulServerUtils.streamResponseAsBundle(theServer, response, responseObject.getDstu1Bundle(), theRequest.getFhirServerBase(), summaryMode, theRequest.isRespondGzip(), requestIsBrowser,
|
||||
theRequest);
|
||||
return theRequest.getResponse().streamResponseAsBundle(responseObject.getDstu1Bundle(), summaryMode, theRequest.isRespondGzip(), requestIsBrowser);
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceOrDstu1Bundle invokeServer(RestfulServer theServer, RequestDetails theRequest, byte[] requestContents) {
|
||||
public ResourceOrDstu1Bundle invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, byte[] requestContents) {
|
||||
// Method params
|
||||
Object[] params = new Object[getParameters().size()];
|
||||
for (int i = 0; i < getParameters().size(); i++) {
|
||||
IParameter param = getParameters().get(i);
|
||||
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
|
||||
*/
|
||||
String serverBase = theServer.getServerBaseForRequest(theRequest.getServletRequest());
|
||||
String serverBase = theRequest.getServerBaseForRequest();
|
||||
String linkSelf;
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(serverBase);
|
||||
@ -349,6 +347,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
||||
|
||||
responseObject = new ResourceOrDstu1Bundle(resource);
|
||||
break;
|
||||
|
||||
} else {
|
||||
Set<Include> includes = getRequestIncludesFromParams(params);
|
||||
|
||||
@ -356,14 +355,20 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
||||
if (count == null) {
|
||||
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();
|
||||
|
||||
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;
|
||||
|
||||
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();
|
||||
if (bundle != null) {
|
||||
responseObject = new ResourceOrDstu1Bundle(bundle);
|
||||
@ -393,7 +398,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
||||
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
|
||||
|
@ -61,10 +61,10 @@ class ConditionalParamBinder implements IParameter {
|
||||
}
|
||||
|
||||
@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) {
|
||||
String retVal = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||
String retVal = theRequest.getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||
if (isBlank(retVal)) {
|
||||
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.RestOperationTypeEnum;
|
||||
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.SimpleBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
@ -72,7 +73,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
||||
}
|
||||
|
||||
@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);
|
||||
return new SimpleBundleProvider(conf);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public class CountParameter implements IParameter {
|
||||
}
|
||||
|
||||
@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);
|
||||
if (sinceParams != null) {
|
||||
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.IBundleProvider;
|
||||
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.InvalidRequestException;
|
||||
|
||||
@ -86,7 +86,7 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi
|
||||
}
|
||||
|
||||
@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) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public class DynamicSearchParameter implements IParameter {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@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();
|
||||
|
||||
for (String next : theRequest.getParameters().keySet()) {
|
||||
|
@ -71,7 +71,7 @@ public class ElementsParameter implements IParameter {
|
||||
|
||||
@Override
|
||||
@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);
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
|
@ -21,15 +21,12 @@ package ca.uhn.fhir.rest.method;
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
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.client.BaseHttpClientInvocation;
|
||||
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.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
@ -168,8 +163,8 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
Object[] params = createParametersForServerRequest(theRequest, null);
|
||||
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
Object[] params = createParametersForServerRequest(theRequest);
|
||||
|
||||
if (myIdParamIndex != null) {
|
||||
params[myIdParamIndex] = theRequest.getId();
|
||||
@ -182,29 +177,13 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; 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) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
|
||||
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();
|
||||
}
|
||||
return theRequest.getResponse().returnResponse(ParseAction.create(resp), Constants.STATUS_HTTP_200_OK, false, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.IBundleProvider;
|
||||
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.InvalidRequestException;
|
||||
|
||||
@ -155,7 +154,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
}
|
||||
|
||||
@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) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public interface IParameter {
|
||||
* @param theMethodBinding TODO
|
||||
* @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);
|
||||
|
||||
|
@ -0,0 +1,5 @@
|
||||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
public interface IRestfulHeader {
|
||||
|
||||
}
|
@ -39,7 +39,7 @@ class NullParameter implements IParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
// nothing
|
||||
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.client.BaseHttpClientInvocation;
|
||||
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.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
@ -246,7 +246,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
}
|
||||
|
||||
@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) {
|
||||
// always ok
|
||||
} else if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
|
@ -24,7 +24,6 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -156,7 +155,7 @@ public class OperationParameter implements IParameter {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@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>();
|
||||
|
||||
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.ETagSupportEnum;
|
||||
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.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
@ -212,7 +212,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
||||
}
|
||||
|
||||
@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);
|
||||
if (myVersionIdIndex != null) {
|
||||
theMethodParams[myVersionIdIndex] = new IdDt(theRequest.getId().getVersionIdPart());
|
||||
@ -222,7 +222,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
||||
IBundleProvider retVal = toResourceList(response);
|
||||
|
||||
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)) {
|
||||
List<IBaseResource> responseResources = retVal.getResources(0, 1);
|
||||
IBaseResource responseResource = responseResources.get(0);
|
||||
|
@ -1,5 +1,10 @@
|
||||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
@ -25,37 +30,31 @@ import java.util.Collections;
|
||||
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 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.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 myCompleteUrl;
|
||||
private String myFhirServerBase;
|
||||
private IdDt myId;
|
||||
private String myOperation;
|
||||
private Map<String, String[]> myParameters;
|
||||
private byte[] myRawRequest;
|
||||
private String myRequestPath;
|
||||
private RequestTypeEnum myRequestType;
|
||||
private String myResourceName;
|
||||
private boolean myRespondGzip;
|
||||
private RestOperationTypeEnum myRestOperationType;
|
||||
private String mySecondaryOperation;
|
||||
private RestfulServer myServer;
|
||||
private HttpServletRequest myServletRequest;
|
||||
private HttpServletResponse myServletResponse;
|
||||
private Map<String, List<String>> myUnqualifiedToQualifiedNames;
|
||||
|
||||
private IRestfulResponse myResponse;
|
||||
|
||||
public String getCompartmentName() {
|
||||
return myCompartmentName;
|
||||
}
|
||||
@ -64,6 +63,10 @@ public class RequestDetails {
|
||||
return myCompleteUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* The fhir server base url, independant of the query being executed
|
||||
* @return the fhir server base url
|
||||
*/
|
||||
public String getFhirServerBase() {
|
||||
return myFhirServerBase;
|
||||
}
|
||||
@ -80,10 +83,6 @@ public class RequestDetails {
|
||||
return myParameters;
|
||||
}
|
||||
|
||||
public byte[] getRawRequest() {
|
||||
return myRawRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of the request URL that comes after the server base.
|
||||
* <p>
|
||||
@ -110,17 +109,7 @@ public class RequestDetails {
|
||||
return mySecondaryOperation;
|
||||
}
|
||||
|
||||
public RestfulServer getServer() {
|
||||
return myServer;
|
||||
}
|
||||
|
||||
public HttpServletRequest getServletRequest() {
|
||||
return myServletRequest;
|
||||
}
|
||||
|
||||
public HttpServletResponse getServletResponse() {
|
||||
return myServletResponse;
|
||||
}
|
||||
public abstract IRestfulServerDefaults getServer();
|
||||
|
||||
public Map<String, List<String>> getUnqualifiedToQualifiedNames() {
|
||||
return myUnqualifiedToQualifiedNames;
|
||||
@ -178,10 +167,6 @@ public class RequestDetails {
|
||||
|
||||
}
|
||||
|
||||
public void setRawRequest(byte[] theRawRequest) {
|
||||
myRawRequest = theRawRequest;
|
||||
}
|
||||
|
||||
public void setRequestPath(String theRequestPath) {
|
||||
assert theRequestPath.length() == 0 || theRequestPath.charAt(0) != '/';
|
||||
myRequestPath = theRequestPath;
|
||||
@ -207,46 +192,66 @@ public class RequestDetails {
|
||||
mySecondaryOperation = theSecondaryOperation;
|
||||
}
|
||||
|
||||
public void setServer(RestfulServer theServer) {
|
||||
myServer = theServer;
|
||||
}
|
||||
public IRestfulResponse getResponse() {
|
||||
return myResponse;
|
||||
}
|
||||
|
||||
public void setServletRequest(HttpServletRequest theRequest) {
|
||||
myServletRequest = theRequest;
|
||||
}
|
||||
public void setResponse(IRestfulResponse theResponse) {
|
||||
this.myResponse = theResponse;
|
||||
}
|
||||
|
||||
public void setServletResponse(HttpServletResponse theServletResponse) {
|
||||
myServletResponse = theServletResponse;
|
||||
}
|
||||
|
||||
public static RequestDetails withResourceAndParams(String theResourceName, RequestTypeEnum theRequestType, Set<String> theParamNames) {
|
||||
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]);
|
||||
public abstract String getHeader(String name);
|
||||
|
||||
public final byte[] loadRequestContents(RequestDetails theRequest) {
|
||||
if (myRequestContents == null) {
|
||||
myRequestContents = getByteStreamRequestContents();
|
||||
}
|
||||
retVal.setParameters(paramNames);
|
||||
return retVal;
|
||||
return myRequestContents;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
protected abstract byte[] getByteStreamRequestContents();
|
||||
|
||||
public String getFirstHeader(String theName) {
|
||||
ArrayList<String> list = myHeaders.get(theName.toLowerCase());
|
||||
if (list == null || list.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return list.get(0);
|
||||
}
|
||||
public abstract List<String> getHeaders(String name);
|
||||
|
||||
/**
|
||||
* Retrieves the body of the request as character data using
|
||||
* a <code>BufferedReader</code>. The reader translates the character
|
||||
* 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
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
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.server.Constants;
|
||||
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.InvalidRequestException;
|
||||
|
||||
@ -266,7 +266,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
}
|
||||
|
||||
@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) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ class ServerBaseParamBinder implements IParameter {
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
class ServletRequestParameter implements IParameter {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class);
|
||||
@ -43,8 +44,8 @@ class ServletRequestParameter implements IParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return theRequest.getServletRequest();
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return ((ServletRequestDetails) theRequest).getServletRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
class ServletResponseParameter implements IParameter {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
|
||||
@ -43,8 +44,8 @@ class ServletResponseParameter implements IParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return theRequest.getServletResponse();
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return ((ServletRequestDetails) theRequest).getServletResponse();
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,7 +54,7 @@ class SinceParameter implements IParameter {
|
||||
}
|
||||
|
||||
@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);
|
||||
if (sinceParams != null) {
|
||||
if (sinceParams.length > 0) {
|
||||
|
@ -65,7 +65,7 @@ public class SortParameter implements IParameter {
|
||||
}
|
||||
|
||||
@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_ASC)) {
|
||||
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) {
|
||||
|
@ -67,7 +67,7 @@ public class SummaryEnumParameter implements IParameter {
|
||||
|
||||
@Override
|
||||
@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);
|
||||
if (value == null || value.isEmpty()) {
|
||||
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.ParamStyle;
|
||||
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.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
@ -119,7 +120,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@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
|
||||
|
@ -37,7 +37,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
|
||||
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
|
||||
* 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();
|
||||
if (isNotBlank(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)) {
|
||||
ifMatchValue = MethodUtil.parseETagValue(ifMatchValue);
|
||||
if (id != null && id.hasVersionIdPart() == false) {
|
||||
|
@ -133,7 +133,7 @@ public abstract class BaseQueryParameter implements IParameter {
|
||||
}
|
||||
|
||||
@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>();
|
||||
String name = getName();
|
||||
|
@ -30,7 +30,6 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -102,38 +101,37 @@ public class ResourceParameter implements IParameter {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding)
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding)
|
||||
throws InternalErrorException, InvalidRequestException {
|
||||
switch (myMode) {
|
||||
case BODY:
|
||||
try {
|
||||
return IOUtils.toString(createRequestReader(theRequest, theRequestContents));
|
||||
} catch (IOException e) {
|
||||
// Shouldn't happen since we're reading from a byte array
|
||||
throw new InternalErrorException("Failed to load request");
|
||||
switch (myMode) {
|
||||
case BODY:
|
||||
try {
|
||||
return IOUtils.toString(createRequestReader(theRequest));
|
||||
}
|
||||
catch (IOException e) {
|
||||
// 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) {
|
||||
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequestContents), charset);
|
||||
|
||||
public static Reader createRequestReader(RequestDetails theRequest, Charset charset) {
|
||||
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.loadRequestContents(theRequest)), charset);
|
||||
return requestReader;
|
||||
}
|
||||
|
||||
static Reader createRequestReader(RequestDetails theRequest, byte[] theRequestContents) {
|
||||
return createRequestReader(theRequestContents, determineRequestCharset(theRequest));
|
||||
}
|
||||
public static Reader createRequestReader(RequestDetails theRequest) throws IOException {
|
||||
return createRequestReader(theRequest, determineRequestCharset(theRequest));
|
||||
}
|
||||
|
||||
static Charset determineRequestCharset(RequestDetails theRequest) {
|
||||
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
public static Charset determineRequestCharset(RequestDetails theRequest) {
|
||||
String ct = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
|
||||
Charset charset = null;
|
||||
if (isNotBlank(ct)) {
|
||||
@ -144,20 +142,21 @@ public class ResourceParameter implements IParameter {
|
||||
charset = Charset.forName("UTF-8");
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
final Charset charset = determineRequestCharset(theRequest);
|
||||
Reader requestReader = createRequestReader(theRequest.getRawRequest(), charset);
|
||||
Reader requestReader = createRequestReader(theRequest, charset);
|
||||
|
||||
RestOperationTypeEnum restOperationType = theMethodBinding != null ? theMethodBinding.getRestOperationType() : null;
|
||||
|
||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest);
|
||||
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.startsWith("application/x-www-form-urlencoded")) {
|
||||
String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
|
||||
@ -171,7 +170,7 @@ public class ResourceParameter implements IParameter {
|
||||
String body;
|
||||
try {
|
||||
body = IOUtils.toString(requestReader);
|
||||
} catch (IOException e) {
|
||||
} catch (IOException e) {
|
||||
// This shouldn't happen since we're reading from a byte array..
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
@ -180,7 +179,7 @@ public class ResourceParameter implements IParameter {
|
||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", restOperationType);
|
||||
throw new InvalidRequestException(msg);
|
||||
} else {
|
||||
requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.getRawRequest()), charset);
|
||||
requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.loadRequestContents(theRequest)), charset);
|
||||
}
|
||||
} else {
|
||||
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)) {
|
||||
TagList tagList = new TagList();
|
||||
for (Enumeration<String> enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) {
|
||||
String nextTagComplete = enumeration.nextElement();
|
||||
for (String nextTagComplete : theRequest.getHeaders(Constants.HEADER_CATEGORY)) {
|
||||
MethodUtil.parseTagValue(tagList, nextTagComplete);
|
||||
}
|
||||
if (tagList.isEmpty() == false) {
|
||||
@ -212,23 +210,23 @@ public class ResourceParameter implements IParameter {
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType) {
|
||||
IBaseResource retVal;
|
||||
if (IBaseBinary.class.isAssignableFrom(theResourceType)) {
|
||||
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();
|
||||
binary.setContentType(ct);
|
||||
binary.setContent(theRequest.getRawRequest());
|
||||
binary.setContent(theRequest.loadRequestContents(theRequest));
|
||||
|
||||
retVal = binary;
|
||||
} else {
|
||||
retVal = loadResourceFromRequest(theRequest, theMethodBinding, theResourceType);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
BODY, ENCODING, RESOURCE
|
||||
|
@ -1,5 +1,7 @@
|
||||
package ca.uhn.fhir.rest.param;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
@ -48,6 +50,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class TransactionParameter implements IParameter {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TransactionParameter.class);
|
||||
private FhirContext myContext;
|
||||
private ParamStyle myParamStyle;
|
||||
private Class<? extends IBaseResource> myResourceBundleType;
|
||||
@ -96,35 +99,41 @@ public class TransactionParameter implements IParameter {
|
||||
// nothing
|
||||
|
||||
}
|
||||
|
||||
|
||||
@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!
|
||||
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) {
|
||||
case DSTU1_BUNDLE: {
|
||||
Bundle bundle;
|
||||
bundle = parser.parseBundle(reader);
|
||||
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());
|
||||
}
|
||||
case DSTU1_BUNDLE: {
|
||||
Bundle bundle;
|
||||
bundle = parser.parseBundle(reader);
|
||||
return bundle;
|
||||
}
|
||||
return resourceList;
|
||||
}
|
||||
case RESOURCE_BUNDLE:
|
||||
return parser.parseResource(myResourceBundleType, reader);
|
||||
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_BUNDLE:
|
||||
return parser.parseResource(myResourceBundleType, reader);
|
||||
}
|
||||
|
||||
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 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);
|
||||
|
||||
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%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Writer;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
@ -31,15 +32,13 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.UnavailableException;
|
||||
@ -47,6 +46,7 @@ import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
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.ProvidedResourceScanner;
|
||||
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.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.Destroy;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
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.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
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.server.exceptions.AuthenticationException;
|
||||
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.interceptor.ExceptionHandlingInterceptor;
|
||||
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.UrlUtil;
|
||||
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 long serialVersionUID = 1L;
|
||||
@ -319,15 +319,12 @@ public class RestfulServer extends HttpServlet {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setting for automatically adding profile tags
|
||||
*
|
||||
* @see #setAddProfileTag(AddProfileTagEnum)
|
||||
*/
|
||||
@Override
|
||||
public AddProfileTagEnum getAddProfileTag() {
|
||||
return myAddProfileTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BundleInclusionRule getBundleInclusionRule() {
|
||||
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
|
||||
* in the request. The default is {@link EncodingEnum#XML}. Will not return null.
|
||||
*/
|
||||
@Override
|
||||
public EncodingEnum getDefaultResponseEncoding() {
|
||||
return myDefaultResponseEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server support for ETags (will not be <code>null</code>). Default is {@link #DEFAULT_ETAG_SUPPORT}
|
||||
*/
|
||||
@Override
|
||||
public ETagSupportEnum getETagSupport() {
|
||||
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
|
||||
* creating their own.
|
||||
*/
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
if (myFhirContext == null) {
|
||||
myFhirContext = new FhirContext();
|
||||
@ -365,10 +362,12 @@ public class RestfulServer extends HttpServlet {
|
||||
/**
|
||||
* Returns a ist of all registered server interceptors
|
||||
*/
|
||||
@Override
|
||||
public List<IServerInterceptor> getInterceptors() {
|
||||
return Collections.unmodifiableList(myInterceptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPagingProvider getPagingProvider() {
|
||||
return myPagingProvider;
|
||||
}
|
||||
@ -414,7 +413,7 @@ public class RestfulServer extends HttpServlet {
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
return myServerAddressStrategy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the server base URL (with no trailing '/') for a given request
|
||||
*/
|
||||
@ -466,99 +465,15 @@ public class RestfulServer extends HttpServlet {
|
||||
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 {
|
||||
String fhirServerBase = null;
|
||||
boolean requestIsBrowser = requestIsBrowser(theRequest);
|
||||
RequestDetails requestDetails = new RequestDetails();
|
||||
ServletRequestDetails requestDetails = new ServletRequestDetails();
|
||||
requestDetails.setServer(this);
|
||||
requestDetails.setRequestType(theRequestType);
|
||||
requestDetails.setServletRequest(theRequest);
|
||||
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 {
|
||||
|
||||
@ -625,19 +540,19 @@ public class RestfulServer extends HttpServlet {
|
||||
requestDetails.setFhirServerBase(fhirServerBase);
|
||||
requestDetails.setCompleteUrl(completeUrl);
|
||||
|
||||
String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
|
||||
if (getPagingProvider() != null && isNotBlank(pagingAction)) {
|
||||
requestDetails.setRestOperationType(RestOperationTypeEnum.GET_PAGE);
|
||||
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
|
||||
* someone comes up with a reason for needing it.
|
||||
*/
|
||||
throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(RestfulServer.class, "getPagesNonHttpGet"));
|
||||
}
|
||||
handlePagingRequest(requestDetails, theResponse, pagingAction);
|
||||
return;
|
||||
}
|
||||
// String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
|
||||
// if (getPagingProvider() != null && isNotBlank(pagingAction)) {
|
||||
// requestDetails.setRestOperationType(RestOperationTypeEnum.GET_PAGE);
|
||||
// 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
|
||||
// * someone comes up with a reason for needing it.
|
||||
// */
|
||||
// throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(RestfulServer.class, "getPagesNonHttpGet"));
|
||||
// }
|
||||
// handlePagingRequest(requestDetails, theResponse, pagingAction);
|
||||
// return;
|
||||
// }
|
||||
|
||||
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath);
|
||||
|
||||
@ -892,6 +807,13 @@ public class RestfulServer extends HttpServlet {
|
||||
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;
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public boolean isDefaultPrettyPrint() {
|
||||
return myDefaultPrettyPrint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return myUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
@ -1369,4 +1293,86 @@ public class RestfulServer extends HttpServlet {
|
||||
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 java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.net.URLEncoder;
|
||||
@ -33,17 +32,15 @@ import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.utils.DateUtils;
|
||||
@ -215,10 +212,10 @@ public class RestfulServerUtils {
|
||||
|
||||
public static EncodingEnum determineRequestEncodingNoDefault(RequestDetails theReq) {
|
||||
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) {
|
||||
while (acceptValues.hasMoreElements() && retVal == null) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
while (acceptValues.hasNext() && retVal == null) {
|
||||
String nextAcceptHeaderValue = acceptValues.next();
|
||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
||||
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.
|
||||
*/
|
||||
public static EncodingEnum determineResponseEncodingNoDefault(HttpServletRequest theReq, EncodingEnum thePrefer) {
|
||||
String[] format = theReq.getParameterValues(Constants.PARAM_FORMAT);
|
||||
public static EncodingEnum determineResponseEncodingNoDefault(RequestDetails theReq, EncodingEnum thePrefer) {
|
||||
String[] format = theReq.getParameters().get(Constants.PARAM_FORMAT);
|
||||
if (format != null) {
|
||||
for (String nextFormat : format) {
|
||||
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
|
||||
|
||||
Enumeration<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||
if (acceptValues != null) {
|
||||
float bestQ = -1f;
|
||||
EncodingEnum retVal = null;
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
List<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||
float bestQ = -1f;
|
||||
EncodingEnum retVal = null;
|
||||
if (acceptValues != null) {
|
||||
for (String nextAcceptHeaderValue : acceptValues) {
|
||||
StringTokenizer tok = new StringTokenizer(nextAcceptHeaderValue, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
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.
|
||||
*/
|
||||
public static EncodingEnum determineResponseEncodingWithDefault(RestfulServer theServer, HttpServletRequest theReq) {
|
||||
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq, theServer.getDefaultResponseEncoding());
|
||||
public static EncodingEnum determineResponseEncodingWithDefault(RequestDetails theReq) {
|
||||
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq, theReq.getServer().getDefaultResponseEncoding());
|
||||
if (retVal == null) {
|
||||
retVal = theServer.getDefaultResponseEncoding();
|
||||
retVal = theReq.getServer().getDefaultResponseEncoding();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
@ -420,14 +416,13 @@ public class RestfulServerUtils {
|
||||
}
|
||||
|
||||
public static Integer extractCountParameter(RequestDetails theRequest) {
|
||||
String paramName = Constants.PARAM_COUNT;
|
||||
return tryToExtractNamedParameter(theRequest, paramName);
|
||||
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
|
||||
}
|
||||
|
||||
public static IParser getNewParser(FhirContext theContext, RequestDetails theRequestDetails) {
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails.getServer(), theRequestDetails.getServletRequest());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails);
|
||||
IParser parser;
|
||||
switch (responseEncoding) {
|
||||
case JSON:
|
||||
@ -444,17 +439,6 @@ public class RestfulServerUtils {
|
||||
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) {
|
||||
Set<String> retVal = new HashSet<String>();
|
||||
|
||||
@ -476,7 +460,8 @@ public class RestfulServerUtils {
|
||||
try {
|
||||
q = Float.parseFloat(value);
|
||||
q = Math.max(q, 0.0f);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
ourLog.debug("Invalid Accept header q value: {}", value);
|
||||
}
|
||||
}
|
||||
@ -536,7 +521,7 @@ public class RestfulServerUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean prettyPrintResponse(RestfulServer theServer, RequestDetails theRequest) {
|
||||
public static boolean prettyPrintResponse(IRestfulServerDefaults theServer, RequestDetails theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] pretty = requestParams.get(Constants.PARAM_PRETTY);
|
||||
boolean prettyPrint;
|
||||
@ -548,10 +533,9 @@ public class RestfulServerUtils {
|
||||
}
|
||||
} else {
|
||||
prettyPrint = theServer.isDefaultPrettyPrint();
|
||||
Enumeration<String> acceptValues = theRequest.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
|
||||
List<String> acceptValues = theRequest.getHeaders(Constants.HEADER_ACCEPT);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
for (String nextAcceptHeaderValue : acceptValues) {
|
||||
if (nextAcceptHeaderValue.contains("pretty=true")) {
|
||||
prettyPrint = true;
|
||||
}
|
||||
@ -561,54 +545,59 @@ public class RestfulServerUtils {
|
||||
return prettyPrint;
|
||||
}
|
||||
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, Bundle bundle, String theServerBase, Set<SummaryEnum> theSummaryMode, boolean theRespondGzip,
|
||||
boolean theRequestIsBrowser, RequestDetails theRequestDetails) throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
public static Object streamResponseAsBundle(IRestfulServerDefaults theServer, Bundle bundle, Set<SummaryEnum> theSummaryMode,
|
||||
boolean theRequestIsBrowser, boolean respondGzip, RequestDetails theRequestDetails)
|
||||
throws IOException {
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
int status = 200;
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequestDetails.getServletRequest());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails);
|
||||
|
||||
String contentType;
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
||||
contentType = responseEncoding.getBrowserFriendlyBundleContentType();
|
||||
} 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 {
|
||||
IParser parser = RestfulServerUtils.getNewParser(theServer.getFhirContext(), theRequestDetails);
|
||||
if (theSummaryMode.contains(SummaryEnum.TEXT)) {
|
||||
parser.setEncodeElements(TEXT_ENCODE_ELEMENTS);
|
||||
}
|
||||
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,
|
||||
int stausCode, boolean theRespondGzip, boolean theAddContentLocationHeader, RequestDetails theRequestDetails) throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, boolean theRequestIsBrowser, Set<SummaryEnum> theSummaryMode,
|
||||
int stausCode, boolean theRespondGzip, boolean theAddContentLocationHeader, boolean respondGzip,
|
||||
RequestDetails theRequestDetails)
|
||||
throws IOException {
|
||||
IRestfulResponse restUtil = theRequestDetails.getResponse();
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequestDetails.getServletRequest(), theServer.getDefaultResponseEncoding());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequestDetails,
|
||||
theServer.getDefaultResponseEncoding());
|
||||
|
||||
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();
|
||||
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 (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) {
|
||||
IBaseBinary bin = (IBaseBinary) theResource;
|
||||
if (isNotBlank(bin.getContentType())) {
|
||||
theHttpResponse.setContentType(bin.getContentType());
|
||||
contentType = bin.getContentType();
|
||||
} 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
|
||||
// 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);
|
||||
ServletOutputStream oos = theHttpResponse.getOutputStream();
|
||||
oos.write(bin.getContent());
|
||||
oos.close();
|
||||
return;
|
||||
return restUtil.sendAttachmentResponse(bin, stausCode, contentType);
|
||||
}
|
||||
|
||||
// Ok, we're not serving a binary resource, so apply default encoding
|
||||
@ -656,38 +638,36 @@ public class RestfulServerUtils {
|
||||
}
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
||||
contentType = responseEncoding.getBrowserFriendlyBundleContentType();
|
||||
} else if (encodingDomainResourceAsText) {
|
||||
theHttpResponse.setContentType(Constants.CT_HTML);
|
||||
contentType = Constants.CT_HTML;
|
||||
} else {
|
||||
theHttpResponse.setContentType(responseEncoding.getResourceContentType());
|
||||
contentType = responseEncoding.getResourceContentType();
|
||||
}
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
String charset = Constants.CHARSET_NAME_UTF8;
|
||||
|
||||
if (theResource instanceof IResource) {
|
||||
InstantDt lastUpdated = ResourceMetadataKeyEnum.UPDATED.get((IResource) theResource);
|
||||
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);
|
||||
if (list != null) {
|
||||
for (Tag tag : list) {
|
||||
if (StringUtils.isNotBlank(tag.getTerm())) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
|
||||
restUtil.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Date lastUpdated = ((IAnyResource) theResource).getMeta().getLastUpdated();
|
||||
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 {
|
||||
if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
||||
writer.append(((IResource) theResource).getText().getDiv().getValueAsString());
|
||||
@ -695,9 +675,11 @@ public class RestfulServerUtils {
|
||||
IParser parser = getNewParser(theServer.getFhirContext(), theRequestDetails);
|
||||
parser.encodeResourceToWriter(theResource, writer);
|
||||
}
|
||||
} finally {
|
||||
writer.close();
|
||||
} catch (Exception e) {
|
||||
//always send a response, even if the parsing went wrong
|
||||
}
|
||||
return restUtil.sendWriterResponse(stausCode, contentType, charset, writer);
|
||||
|
||||
}
|
||||
|
||||
// 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.rest.api.SummaryEnum;
|
||||
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.InternalErrorException;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
@ -48,8 +48,14 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
|
||||
private Class<?>[] myReturnStackTracesForExceptionTypes;
|
||||
|
||||
@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 {
|
||||
IRestfulResponse response = theRequestDetails.getResponse();
|
||||
|
||||
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
||||
|
||||
@ -67,22 +73,19 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
|
||||
if (isNotBlank(next.getKey()) && next.getValue() != null) {
|
||||
String nextKey = next.getKey();
|
||||
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);
|
||||
// theRequestDetails.getServer().addHeadersToResponse(theResponse);
|
||||
// theResponse.setContentType("text/plain");
|
||||
// theResponse.setCharacterEncoding("UTF-8");
|
||||
// theResponse.getWriter().append(theException.getMessage());
|
||||
// theResponse.getWriter().close();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -148,6 +148,26 @@ public interface IServerInterceptor {
|
||||
* client.
|
||||
*/
|
||||
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
|
||||
@ -167,6 +187,20 @@ public interface IServerInterceptor {
|
||||
* client.
|
||||
*/
|
||||
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
|
||||
@ -189,6 +223,22 @@ public interface IServerInterceptor {
|
||||
*/
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource 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, 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
|
||||
@ -211,6 +261,22 @@ public interface IServerInterceptor {
|
||||
*/
|
||||
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
|
||||
* {@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.server.exceptions.AuthenticationException;
|
||||
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
|
||||
@ -66,21 +67,45 @@ public class InterceptorAdapter implements IServerInterceptor {
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle bundle) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, bundle, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(theRequestDetails, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
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")) {
|
||||
return StringUtils.defaultString(myRequest.getRemoteAddr());
|
||||
} else if (theKey.equals("responseEncodingNoDefault")) {
|
||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingNoDefault(myRequest, myRequestDetails.getServer().getDefaultResponseEncoding());
|
||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingNoDefault(myRequestDetails, myRequestDetails.getServer().getDefaultResponseEncoding());
|
||||
if (encoding != null) {
|
||||
return encoding.name();
|
||||
} else {
|
||||
|
@ -171,7 +171,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
||||
/*
|
||||
* 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) {
|
||||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||
}
|
||||
@ -284,7 +284,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
||||
/*
|
||||
* It's not a browser...
|
||||
*/
|
||||
Set<String> accept = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theRequestDetails.getServletRequest());
|
||||
Set<String> accept = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theServletRequest);
|
||||
if (!accept.contains(Constants.CT_HTML)) {
|
||||
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.jpa.entity.ResourceTable;
|
||||
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.model.api.IResource;
|
||||
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.NotModifiedException;
|
||||
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.UrlUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil.UrlParts;
|
||||
@ -119,7 +121,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||
subRequestBundle.setType(BundleTypeEnum.TRANSACTION);
|
||||
subRequestBundle.addEntry(nextRequestEntry);
|
||||
|
||||
Bundle subResponseBundle = transaction(theRequestDetails, subRequestBundle, "Batch sub-request");
|
||||
Bundle subResponseBundle = transaction((ServletRequestDetails) theRequestDetails, subRequestBundle, "Batch sub-request");
|
||||
return subResponseBundle;
|
||||
}
|
||||
};
|
||||
@ -242,11 +244,11 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||
notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails);
|
||||
|
||||
String actionName = "Transaction";
|
||||
return transaction(theRequestDetails, theRequest, actionName);
|
||||
return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName);
|
||||
}
|
||||
|
||||
@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();
|
||||
if (transactionType == BundleTypeEnum.BATCH) {
|
||||
return batch(theRequestDetails, theRequest);
|
||||
@ -491,7 +493,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
|
||||
Integer originalOrder = originalRequestOrder.get(nextReqEntry);
|
||||
Entry nextRespEntry = response.getEntry().get(originalOrder);
|
||||
|
||||
RequestDetails requestDetails = new RequestDetails();
|
||||
ServletSubRequestDetails requestDetails = new ServletSubRequestDetails();
|
||||
requestDetails.setServletRequest(theRequestDetails.getServletRequest());
|
||||
requestDetails.setRequestType(RequestTypeEnum.GET);
|
||||
requestDetails.setServer(theRequestDetails.getServer());
|
||||
|
@ -31,6 +31,7 @@ import org.jboss.logging.MDC;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
public class BaseJpaProvider {
|
||||
|
||||
@ -45,7 +46,7 @@ public class BaseJpaProvider {
|
||||
MDC.remove(REMOTE_UA);
|
||||
}
|
||||
|
||||
public void endRequest(RequestDetails theRequest) {
|
||||
public void endRequest(ServletRequestDetails theRequest) {
|
||||
endRequest(theRequest.getServletRequest());
|
||||
}
|
||||
|
||||
@ -92,7 +93,7 @@ public class BaseJpaProvider {
|
||||
|
||||
}
|
||||
|
||||
public void startRequest(RequestDetails theRequest) {
|
||||
public void startRequest(ServletRequestDetails theRequest) {
|
||||
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.TransactionParam;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
public class JpaSystemProviderDstu1 extends BaseJpaSystemProvider<List<IResource>> {
|
||||
|
||||
@Transaction
|
||||
public List<IResource> transaction(RequestDetails theRequestDetails, @TransactionParam List<IResource> theResources) {
|
||||
startRequest(theRequestDetails);
|
||||
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||
try {
|
||||
return getDao().transaction(theRequestDetails, theResources);
|
||||
} finally {
|
||||
endRequest(theRequestDetails);
|
||||
endRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,6 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider<Bundle> {
|
||||
|
||||
@ -227,11 +226,11 @@ public class JpaSystemProviderDstu2 extends BaseJpaSystemProvider<Bundle> {
|
||||
|
||||
@Transaction
|
||||
public Bundle transaction(RequestDetails theRequestDetails, @TransactionParam Bundle theResources) {
|
||||
startRequest(theRequestDetails);
|
||||
startRequest(((ServletRequestDetails) theRequestDetails).getServletRequest());
|
||||
try {
|
||||
return getDao().transaction(theRequestDetails, theResources);
|
||||
} 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 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.servlet.ServletRequestDetails;
|
||||
|
||||
public abstract class BaseJpaDstu2SystemTest extends BaseJpaDstu2Test {
|
||||
protected RequestDetails myRequestDetails;
|
||||
protected ServletRequestDetails myRequestDetails;
|
||||
private RestfulServer myServer;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void before() throws ServletException {
|
||||
myRequestDetails = mock(RequestDetails.class);
|
||||
myRequestDetails = mock(ServletRequestDetails.class);
|
||||
|
||||
if (myServer == null) {
|
||||
myServer = new RestfulServer(myFhirCtx);
|
||||
|
@ -55,6 +55,12 @@
|
||||
<artifactId>phloc-commons</artifactId>
|
||||
</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 -->
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
|
@ -30,7 +30,6 @@
|
||||
<dependency>
|
||||
<groupId>xmlunit</groupId>
|
||||
<artifactId>xmlunit</artifactId>
|
||||
<version>1.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -197,7 +197,7 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
|
||||
}
|
||||
|
||||
@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;
|
||||
String searchId = null;
|
||||
List<IBaseResource> resourceList;
|
||||
|
@ -1,34 +1,16 @@
|
||||
package ca.uhn.fhir.rest.server.provider;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR Structures - DSTU1 (FHIR v0.80)
|
||||
* %%
|
||||
* 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%
|
||||
*/
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.jar.Manifest;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
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.IdDt;
|
||||
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.method.BaseMethodBinding;
|
||||
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.ResourceBinding;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestulfulServerConfiguration;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 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 volatile Conformance myConformance;
|
||||
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
|
||||
* ServerConfirmanceProvider can be Spring-wired with
|
||||
@ -95,9 +73,13 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
||||
}
|
||||
|
||||
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
|
||||
* 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.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.getSoftware().setName(myRestfulServer.getServerName());
|
||||
retVal.getSoftware().setVersion(myRestfulServer.getServerVersion());
|
||||
retVal.getImplementation().setDescription(myServerConfiguration.getImplementationDescription());
|
||||
retVal.getSoftware().setName(myServerConfiguration.getServerName());
|
||||
retVal.getSoftware().setVersion(myServerConfiguration.getServerVersion());
|
||||
retVal.addFormat(Constants.CT_FHIR_XML);
|
||||
retVal.addFormat(Constants.CT_FHIR_JSON);
|
||||
|
||||
@ -137,7 +119,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
||||
|
||||
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>() {
|
||||
@Override
|
||||
public int compare(ResourceBinding theArg0, ResourceBinding theArg1) {
|
||||
@ -151,9 +133,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
||||
RestResource resource = rest.addResource();
|
||||
|
||||
String resourceName = next.getResourceName();
|
||||
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
||||
RuntimeResourceDefinition def = myServerConfiguration.getFhirContext().getResourceDefinition(resourceName);
|
||||
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>();
|
||||
|
||||
@ -215,7 +199,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
||||
}
|
||||
|
||||
private DateTimeDt conformanceDate() {
|
||||
String buildDate = getBuildDateFromManifest();
|
||||
String buildDate = myServerConfiguration.getConformanceDate();
|
||||
if (buildDate != null) {
|
||||
try {
|
||||
return new DateTimeDt(buildDate);
|
||||
@ -226,21 +210,6 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
||||
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) {
|
||||
includes.addAll(searchMethodBinding.getIncludes());
|
||||
|
||||
@ -348,7 +317,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
||||
param.setDocumentation(nextParamDescription);
|
||||
param.getTypeElement().setValue(nextParameter.getParamType().getCode());
|
||||
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
||||
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
|
||||
RuntimeResourceDefinition targetDef = myServerConfiguration.getFhirContext().getResourceDefinition(nextTarget);
|
||||
if (targetDef != null) {
|
||||
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
|
||||
if (code != null) {
|
||||
|
@ -62,10 +62,10 @@ public class InterceptorTest {
|
||||
public void testInterceptorFires() throws Exception {
|
||||
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.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.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");
|
||||
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(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(myInterceptor1, 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));
|
||||
verifyNoMoreInteractions(myInterceptor1);
|
||||
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