Merge branch 'petervanhoute-master'

This commit is contained in:
jamesagnew 2015-11-27 15:00:21 -05:00
commit c6dede1447
109 changed files with 5458 additions and 692 deletions

View File

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

View 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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.method;
public interface IRestfulHeader {
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,6 @@
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>

View File

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

View File

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

View File

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