create seperate response and server interfaces and classes for servlets and jaxrs
This commit is contained in:
parent
b4df6f9612
commit
7f910974a2
|
@ -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
|
||||
|
|
|
@ -69,7 +69,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;
|
||||
|
@ -86,10 +86,6 @@ 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 +127,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 +147,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 +247,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 +289,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.
|
||||
|
@ -643,17 +604,17 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
/**
|
||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
||||
*/
|
||||
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)
|
||||
*/
|
||||
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.");
|
||||
|
@ -663,7 +624,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
/**
|
||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
||||
*/
|
||||
private static interface IRequestReader {
|
||||
public static interface IRequestReader {
|
||||
InputStream getInputStream(RequestDetails theRequestDetails) throws IOException;
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.io.IOException;
|
|||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -41,9 +42,11 @@ import ca.uhn.fhir.parser.IParser;
|
|||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -68,24 +71,6 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
}
|
||||
}
|
||||
|
||||
private void addLocationHeader(RequestDetails theRequest, HttpServletResponse theResponse, MethodOutcome response, String headerLocation) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theRequest.getFhirServerBase());
|
||||
b.append('/');
|
||||
b.append(getResourceName());
|
||||
b.append('/');
|
||||
b.append(response.getId().getIdPart());
|
||||
if (response.getId().hasVersionIdPart()) {
|
||||
b.append("/" + Constants.PARAM_HISTORY + "/");
|
||||
b.append(response.getId().getVersionIdPart());
|
||||
} else if (response.getVersionId() != null && response.getVersionId().isEmpty() == false) {
|
||||
b.append("/" + Constants.PARAM_HISTORY + "/");
|
||||
b.append(response.getVersionId().getValue());
|
||||
}
|
||||
theResponse.addHeader(headerLocation, b.toString());
|
||||
|
||||
}
|
||||
|
||||
protected abstract void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams);
|
||||
|
||||
/**
|
||||
|
@ -139,19 +124,16 @@ 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
theServer.addHeadersToResponse(servletResponse);
|
||||
|
||||
if (resource != null && allowPrefer) {
|
||||
String prefer = theRequest.getServletRequest().getHeader(Constants.HEADER_PREFER);
|
||||
String prefer = theRequest.getHeader(Constants.HEADER_PREFER);
|
||||
PreferReturnEnum preferReturn = RestfulServerUtils.parsePreferHeader(prefer);
|
||||
if (preferReturn != null) {
|
||||
if (preferReturn == PreferReturnEnum.REPRESENTATION) {
|
||||
|
@ -244,31 +231,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
}
|
||||
}
|
||||
|
||||
if (outcome != null) {
|
||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
servletResponse.setContentType(encoding.getResourceContentType());
|
||||
Writer writer = servletResponse.getWriter();
|
||||
IParser parser = encoding.newParser(getContext());
|
||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theServer, theRequest));
|
||||
try {
|
||||
parser.encodeResourceToWriter(outcome, writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
} else {
|
||||
servletResponse.setContentType(Constants.CT_TEXT_WITH_UTF8);
|
||||
Writer writer = servletResponse.getWriter();
|
||||
writer.close();
|
||||
}
|
||||
|
||||
// getMethod().in
|
||||
}
|
||||
|
||||
private void addContentLocationHeaders(RequestDetails theRequest, HttpServletResponse servletResponse, MethodOutcome response) {
|
||||
if (response != null && response.getId() != null) {
|
||||
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_LOCATION);
|
||||
addLocationHeader(theRequest, servletResponse, response, Constants.HEADER_CONTENT_LOCATION);
|
||||
}
|
||||
return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
|
||||
}
|
||||
|
||||
public boolean isReturnVoid() {
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
|
|
|
@ -35,8 +35,6 @@ import java.util.Map;
|
|||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
|
@ -55,8 +53,8 @@ import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
@ -233,10 +231,10 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
throw new IllegalStateException("Should not get here!");
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
|
||||
// Pretty print
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
|
@ -245,38 +243,36 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest(), theServer.getDefaultResponseEncoding());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
byte[] requestContents = loadRequestContents(theRequest);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
Object resultObj = invokeServer(theServer, theRequest, params);
|
||||
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE: {
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
@ -325,16 +321,15 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resource, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resource);
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip,
|
||||
isAddContentLocationHeader(), theRequest);
|
||||
break;
|
||||
return theRequest.getResponse().streamResponseAsResource(resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip,
|
||||
isAddContentLocationHeader());
|
||||
} else {
|
||||
Set<Include> includes = getRequestIncludesFromParams(params);
|
||||
|
||||
|
@ -350,28 +345,26 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
if (bundle != null) {
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, bundle);
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, theRequest.getFhirServerBase(), summaryMode, respondGzip, requestIsBrowser, theRequest);
|
||||
return theRequest.getResponse().streamResponseAsBundle(bundle, summaryMode, respondGzip, requestIsBrowser);
|
||||
} else {
|
||||
IBaseResource resBundle = bundleFactory.getResourceBundle();
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resBundle, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resBundle);
|
||||
if (!continueProcessing) {
|
||||
ourLog.debug("Interceptor {} returned false, not continuing processing");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resBundle, prettyPrint, summaryMode,
|
||||
Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), isAddContentLocationHeader(), theRequest);
|
||||
return theRequest.getResponse().streamResponseAsResource(resBundle, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK,
|
||||
theRequest.isRespondGzip(), isAddContentLocationHeader());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
case RESOURCE: {
|
||||
|
@ -386,17 +379,16 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resource, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resource);
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip,
|
||||
isAddContentLocationHeader(), theRequest);
|
||||
break;
|
||||
return theRequest.getResponse().streamResponseAsResource(resource, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, respondGzip, isAddContentLocationHeader());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,10 +61,10 @@ class ConditionalParamBinder implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
|
||||
if (myOperationType == RestOperationTypeEnum.CREATE) {
|
||||
String retVal = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||
String retVal = theRequest.getHeader(Constants.HEADER_IF_NONE_EXIST);
|
||||
if (isBlank(retVal)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -72,7 +73,7 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
IBaseResource conf = (IBaseResource) invokeServerMethod(theServer, theRequest, theMethodParams);
|
||||
return new SimpleBundleProvider(conf);
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ public class CountParameter implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_COUNT);
|
||||
if (sinceParams != null) {
|
||||
if (sinceParams.length > 0) {
|
||||
|
|
|
@ -37,7 +37,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -86,7 +86,7 @@ public class DynamicSearchMethodBinding extends BaseResourceReturningMethodBindi
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
if (myIdParamIndex != null) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public class DynamicSearchParameter implements IParameter {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
SearchParameterMap retVal = new SearchParameterMap();
|
||||
|
||||
for (String next : theRequest.getParameters().keySet()) {
|
||||
|
|
|
@ -71,7 +71,7 @@ public class ElementsParameter implements IParameter {
|
|||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
Set<String> value = getElementsValueOrNull(theRequest);
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
|
|
|
@ -21,15 +21,12 @@ package ca.uhn.fhir.rest.method;
|
|||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
|
@ -44,10 +41,8 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
|||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
|
@ -168,8 +163,8 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void invokeServer(RestfulServer theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
Object[] params = createParametersForServerRequest(theRequest, null);
|
||||
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
Object[] params = createParametersForServerRequest(theRequest);
|
||||
|
||||
if (myIdParamIndex != null) {
|
||||
params[myIdParamIndex] = theRequest.getId();
|
||||
|
@ -182,29 +177,13 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resp, theRequest.getServletRequest(), theRequest.getServletResponse());
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, resp);
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
response.setContentType(responseEncoding.getResourceContentType());
|
||||
response.setStatus(Constants.STATUS_HTTP_200_OK);
|
||||
response.setCharacterEncoding(Constants.CHARSET_NAME_UTF8);
|
||||
|
||||
theServer.addHeadersToResponse(response);
|
||||
|
||||
IParser parser = responseEncoding.newParser(getContext());
|
||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theServer, theRequest));
|
||||
PrintWriter writer = response.getWriter();
|
||||
try {
|
||||
parser.encodeTagListToWriter(resp, writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
return theRequest.getResponse().returnResponse(ParseAction.create(resp), Constants.STATUS_HTTP_200_OK, false, null, null);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.lang.reflect.Method;
|
|||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
|
@ -43,7 +42,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -155,7 +154,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
if (myIdParamIndex != null) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ public interface IParameter {
|
|||
* @param theMethodBinding TODO
|
||||
* @return Returns the argument object as it will be passed to the {@link IResourceProvider} method.
|
||||
*/
|
||||
Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException;
|
||||
Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException;
|
||||
|
||||
void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,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;
|
||||
|
@ -223,7 +223,7 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.POST) {
|
||||
// always ok
|
||||
} else if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.lang.reflect.Method;
|
|||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -156,7 +155,7 @@ public class OperationParameter implements IParameter {
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
List<Object> matchingParamValues = new ArrayList<Object>();
|
||||
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
|
|
|
@ -0,0 +1,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 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -49,7 +49,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;
|
||||
|
@ -204,7 +204,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());
|
||||
|
@ -214,7 +214,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
|
|||
IBundleProvider retVal = toResourceList(response);
|
||||
|
||||
if (theRequest.getServer().getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||
String ifNoneMatch = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_NONE_MATCH_LC);
|
||||
String ifNoneMatch = theRequest.getHeader(Constants.HEADER_IF_NONE_MATCH_LC);
|
||||
if (retVal.size() == 1 && StringUtils.isNotBlank(ifNoneMatch)) {
|
||||
List<IBaseResource> responseResources = retVal.getResources(0, 1);
|
||||
IBaseResource responseResource = responseResources.get(0);
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -25,35 +29,30 @@ 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 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;
|
||||
|
@ -79,10 +78,6 @@ public class RequestDetails {
|
|||
return myParameters;
|
||||
}
|
||||
|
||||
public byte[] getRawRequest() {
|
||||
return myRawRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* The part of the request URL that comes after the server base.
|
||||
* <p>
|
||||
|
@ -109,17 +104,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;
|
||||
|
@ -177,10 +162,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;
|
||||
|
@ -206,28 +187,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 abstract String getHeader(String name);
|
||||
|
||||
public final byte[] loadRequestContents(RequestDetails theRequest) {
|
||||
if (myRequestContents == null) {
|
||||
myRequestContents = getByteStreamRequestContents();
|
||||
}
|
||||
return myRequestContents;
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
retVal.setParameters(paramNames);
|
||||
return retVal;
|
||||
}
|
||||
protected abstract byte[] getByteStreamRequestContents();
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
|
|
@ -48,7 +48,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;
|
||||
|
||||
|
@ -283,7 +283,7 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public IBundleProvider invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public IBundleProvider invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
if (myIdParamIndex != null) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class ServerBaseParamBinder implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return theRequest.getFhirServerBase();
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
class ServletRequestParameter implements IParameter {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class);
|
||||
|
@ -43,8 +44,8 @@ class ServletRequestParameter implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return theRequest.getServletRequest();
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return ((ServletRequestDetails) theRequest).getServletRequest();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
class ServletResponseParameter implements IParameter {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
|
||||
|
@ -43,8 +44,8 @@ class ServletResponseParameter implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return theRequest.getServletResponse();
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return ((ServletRequestDetails) theRequest).getServletResponse();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ class SinceParameter implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
String[] sinceParams = theRequest.getParameters().remove(Constants.PARAM_SINCE);
|
||||
if (sinceParams != null) {
|
||||
if (sinceParams.length > 0) {
|
||||
|
|
|
@ -65,7 +65,7 @@ public class SortParameter implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) {
|
||||
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) {
|
||||
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) {
|
||||
|
|
|
@ -67,7 +67,7 @@ public class SummaryEnumParameter implements IParameter {
|
|||
|
||||
@Override
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
Set<SummaryEnum> value = getSummaryValueOrNull(theRequest);
|
||||
if (value == null || value.isEmpty()) {
|
||||
return null;
|
||||
|
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.param.TransactionParameter;
|
||||
import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -119,7 +120,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Object invokeServer(RestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
public Object invokeServer(IRestfulServer theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
|
||||
/*
|
||||
* The design of HAPI's transaction method for DSTU1 support assumed that a transaction was just an update on a
|
||||
|
|
|
@ -37,7 +37,7 @@ import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
|
||||
private Integer myIdParameterIndex;
|
||||
|
||||
|
@ -58,7 +58,7 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||
* We are being a bit lenient here, since technically the client is supposed to include the version in the
|
||||
* Content-Location header, but we allow it in the PUT URL as well..
|
||||
*/
|
||||
String locationHeader = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||
String locationHeader = theRequest.getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||
IdDt id = theRequest.getId();
|
||||
if (isNotBlank(locationHeader)) {
|
||||
id = new IdDt(locationHeader);
|
||||
|
@ -69,7 +69,7 @@ class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceP
|
|||
}
|
||||
}
|
||||
|
||||
String ifMatchValue = theRequest.getServletRequest().getHeader(Constants.HEADER_IF_MATCH);
|
||||
String ifMatchValue = theRequest.getHeader(Constants.HEADER_IF_MATCH);
|
||||
if (isNotBlank(ifMatchValue)) {
|
||||
ifMatchValue = MethodUtil.parseETagValue(ifMatchValue);
|
||||
if (id != null && id.hasVersionIdPart() == false) {
|
||||
|
|
|
@ -133,7 +133,7 @@ public abstract class BaseQueryParameter implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
|
||||
List<QualifiedParamList> paramList = new ArrayList<QualifiedParamList>();
|
||||
String name = getName();
|
||||
|
|
|
@ -30,7 +30,6 @@ import java.lang.reflect.Method;
|
|||
import java.lang.reflect.Modifier;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -102,38 +101,37 @@ public class ResourceParameter implements IParameter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, byte[] theRequestContents, BaseMethodBinding<?> theMethodBinding)
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding)
|
||||
throws InternalErrorException, InvalidRequestException {
|
||||
switch (myMode) {
|
||||
case BODY:
|
||||
try {
|
||||
return IOUtils.toString(createRequestReader(theRequest, theRequestContents));
|
||||
} catch (IOException e) {
|
||||
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:
|
||||
break;
|
||||
default:
|
||||
return parseResourceFromRequest(theRequest, theMethodBinding, myResourceType);
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
@ -147,17 +145,18 @@ public class ResourceParameter implements IParameter {
|
|||
}
|
||||
|
||||
@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());
|
||||
|
@ -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) {
|
||||
|
@ -218,10 +216,10 @@ public class ResourceParameter implements IParameter {
|
|||
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 {
|
||||
|
|
|
@ -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;
|
||||
|
@ -98,14 +101,20 @@ public class TransactionParameter 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 {
|
||||
// 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 = ResourceParameter.createRequestReader(theRequest, theRequestContents);
|
||||
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()));
|
||||
}
|
||||
|
||||
switch (myParamStyle) {
|
||||
case DSTU1_BUNDLE: {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -2,7 +2,7 @@ package ca.uhn.fhir.rest.server;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
|
||||
/*
|
||||
|
@ -24,14 +24,12 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
public interface IRestfulServer {
|
||||
public interface IRestfulServer<T extends RequestDetails> extends 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.
|
||||
*/
|
||||
public FhirContext getFhirContext();
|
||||
List<IServerInterceptor> getInterceptors();
|
||||
|
||||
public List<IServerInterceptor> getInterceptors();
|
||||
IPagingProvider getPagingProvider();
|
||||
|
||||
BundleInclusionRule getBundleInclusionRule();
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,15 @@
|
|||
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.
|
||||
|
@ -11,6 +19,28 @@ public interface IRestfulServerDefaults {
|
|||
*
|
||||
* @return Returns the default pretty print setting
|
||||
*/
|
||||
public boolean isDefaultPrettyPrint();
|
||||
boolean isDefaultPrettyPrint();
|
||||
|
||||
/**
|
||||
* @return Returns the server support for ETags (will not be <code>null</code>). Default is {@link #DEFAULT_ETAG_SUPPORT}
|
||||
*/
|
||||
ETagSupportEnum getETagSupport();
|
||||
|
||||
/**
|
||||
* @return Returns the setting for automatically adding profile tags
|
||||
*/
|
||||
AddProfileTagEnum getAddProfileTag();
|
||||
|
||||
/**
|
||||
* @return Returns the default encoding to return (XML/JSON) if an incoming request does not specify a preference (either with the <code>_format</code> URL parameter, or with an <code>Accept</code> header
|
||||
* in the request. The default is {@link EncodingEnum#XML}. Will not return null.
|
||||
*/
|
||||
EncodingEnum getDefaultResponseEncoding();
|
||||
|
||||
/**
|
||||
* @return If <code>true</code> the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
|
||||
* instead of a FHIR
|
||||
*/
|
||||
boolean isUseBrowserFriendlyContentTypes();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter.Mode;
|
||||
import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle;
|
||||
|
||||
public interface IRestfulServerUtil {
|
||||
|
||||
Object getResourceParameter(
|
||||
RequestDetails requestDetails,
|
||||
Mode myMode,
|
||||
BaseMethodBinding<?> theMethodBinding,
|
||||
Class<? extends IBaseResource> myResourceType);
|
||||
|
||||
Object getRequestResource(RequestDetails theRequest, ParamStyle myParamStyle, Class<? extends IBaseResource> myResourceBundleType);
|
||||
|
||||
<T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<T> theResourceType);
|
||||
|
||||
IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType);
|
||||
|
||||
}
|
|
@ -40,7 +40,7 @@ public interface IVersionSpecificBundleFactory {
|
|||
|
||||
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, Integer theTotalResults, BundleTypeEnum theBundleType, IPrimitiveType<Date> theLastUpdated);
|
||||
|
||||
void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
|
||||
void initializeBundleFromBundleProvider(IRestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
|
||||
int theOffset, Integer theCount, String theSearchId, BundleTypeEnum theBundleType, Set<Include> theIncludes);
|
||||
|
||||
Bundle getDstu1Bundle();
|
||||
|
|
|
@ -0,0 +1,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;
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,8 @@ 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;
|
||||
|
@ -39,12 +41,14 @@ 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.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;
|
||||
|
@ -56,14 +60,17 @@ 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.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.ParseAction;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -71,11 +78,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 implements IRestfulServerDefaults, IRestfulServer {
|
||||
public class RestfulServer extends HttpServlet implements IRestfulServer<ServletRequestDetails> {
|
||||
|
||||
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
@ -316,15 +324,12 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
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;
|
||||
}
|
||||
|
@ -337,9 +342,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
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;
|
||||
}
|
||||
|
@ -463,7 +466,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
return myServerVersion;
|
||||
}
|
||||
|
||||
private void handlePagingRequest(RequestDetails theRequest, HttpServletResponse theResponse, String thePagingAction) throws IOException {
|
||||
private void handlePagingRequest(ServletRequestDetails theRequest, HttpServletResponse theResponse, String thePagingAction) throws IOException {
|
||||
IBundleProvider resultList = getPagingProvider().retrieveResultList(thePagingAction);
|
||||
if (resultList == null) {
|
||||
ourLog.info("Client requested unknown paging ID[{}]", thePagingAction);
|
||||
|
@ -476,21 +479,21 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
return;
|
||||
}
|
||||
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
||||
if (count == null) {
|
||||
count = getPagingProvider().getDefaultPageSize();
|
||||
} else if (count > getPagingProvider().getMaximumPageSize()) {
|
||||
count = getPagingProvider().getMaximumPageSize();
|
||||
}
|
||||
|
||||
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest.getServletRequest(), Constants.PARAM_PAGINGOFFSET);
|
||||
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());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, getDefaultResponseEncoding());
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(this, theRequest);
|
||||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
||||
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
|
||||
|
@ -506,7 +509,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
}
|
||||
}
|
||||
|
||||
String linkSelfBase = getServerAddressStrategy().determineServerBase(getServletContext(), theRequest.getServletRequest());
|
||||
String linkSelfBase = myServerAddressStrategy.determineServerBase(getServletContext(), theRequest.getServletRequest());
|
||||
String linkSelf = linkSelfBase + theRequest.getCompleteUrl().substring(theRequest.getCompleteUrl().indexOf('?'));
|
||||
|
||||
bundleFactory.initializeBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), linkSelf, prettyPrint, start, count, thePagingAction, null, includes);
|
||||
|
@ -521,7 +524,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
return;
|
||||
}
|
||||
}
|
||||
RestfulServerUtils.streamResponseAsBundle(this, theResponse, bundle, theRequest.getFhirServerBase(), summaryMode, respondGzip, requestIsBrowser, theRequest);
|
||||
theRequest.getResponse().streamResponseAsBundle(bundle, summaryMode, respondGzip, requestIsBrowser);
|
||||
} else {
|
||||
IBaseResource resBundle = bundleFactory.getResourceBundle();
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
|
@ -532,14 +535,14 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
return;
|
||||
}
|
||||
}
|
||||
RestfulServerUtils.streamResponseAsResource(this, theResponse, resBundle, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), false, theRequest);
|
||||
theRequest.getResponse().streamResponseAsResource(resBundle, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), false);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -1338,4 +1341,86 @@ public class RestfulServer extends HttpServlet implements IRestfulServerDefaults
|
|||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,262 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.MethodUtil;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding.IRequestReader;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter.Mode;
|
||||
import ca.uhn.fhir.rest.param.TransactionParameter.ParamStyle;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class RestfulServerUtil implements IRestfulServerUtil {
|
||||
|
||||
/**
|
||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
||||
*/
|
||||
private static volatile IRequestReader ourRequestReader;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerUtil.class);
|
||||
private byte[] requestContents;
|
||||
|
||||
@Override
|
||||
public Object getResourceParameter(RequestDetails theRequest, Mode myMode, BaseMethodBinding<?> theMethodBinding,
|
||||
Class<? extends IBaseResource> myResourceType) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected byte[] loadRequestContents(RequestDetails theRequest) {
|
||||
if(requestContents != null) {
|
||||
return requestContents;
|
||||
}
|
||||
/*
|
||||
* 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(theRequest);
|
||||
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()));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Reader createRequestReader(RequestDetails theRequest, Charset charset) {
|
||||
Reader requestReader = new InputStreamReader(new ByteArrayInputStream(loadRequestContents(theRequest)), charset);
|
||||
return requestReader;
|
||||
}
|
||||
|
||||
public Reader createRequestReader(RequestDetails theRequest) throws IOException {
|
||||
return createRequestReader(theRequest, determineRequestCharset(theRequest));
|
||||
}
|
||||
|
||||
public static Charset determineRequestCharset(RequestDetails theRequest) {
|
||||
String ct = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||
|
||||
Charset charset = null;
|
||||
if (isNotBlank(ct)) {
|
||||
ContentType parsedCt = ContentType.parse(ct);
|
||||
charset = parsedCt.getCharset();
|
||||
}
|
||||
if (charset == null) {
|
||||
charset = Charset.forName("UTF-8");
|
||||
}
|
||||
return charset;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <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, 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);
|
||||
if (ctValue != null) {
|
||||
if (ctValue.startsWith("application/x-www-form-urlencoded")) {
|
||||
String msg = theRequest.getServer().getFhirContext().getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType());
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
if (isBlank(ctValue)) {
|
||||
/*
|
||||
* If the client didn't send a content type, try to guess
|
||||
*/
|
||||
String body;
|
||||
try {
|
||||
body = IOUtils.toString(requestReader);
|
||||
} catch (IOException e) {
|
||||
// This shouldn't happen since we're reading from a byte array..
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
encoding = MethodUtil.detectEncodingNoDefault(body);
|
||||
if (encoding == null) {
|
||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", restOperationType);
|
||||
throw new InvalidRequestException(msg);
|
||||
} else {
|
||||
requestReader = new InputStreamReader(new ByteArrayInputStream(loadRequestContents(theRequest)), charset);
|
||||
}
|
||||
} else {
|
||||
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, restOperationType);
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
IParser parser = encoding.newParser(ctx);
|
||||
|
||||
T retVal;
|
||||
if (theResourceType != null) {
|
||||
retVal = parser.parseResource(theResourceType, requestReader);
|
||||
} else {
|
||||
retVal = (T) parser.parseResource(requestReader);
|
||||
}
|
||||
|
||||
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {
|
||||
retVal.setId(theRequest.getId());
|
||||
}
|
||||
|
||||
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();
|
||||
MethodUtil.parseTagValue(tagList, nextTagComplete);
|
||||
}
|
||||
if (tagList.isEmpty() == false) {
|
||||
((IResource) retVal).getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public 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);
|
||||
IBaseBinary binary = (IBaseBinary) ctx.getResourceDefinition("Binary").newInstance();
|
||||
binary.setContentType(ct);
|
||||
binary.setContent(loadRequestContents(theRequest));
|
||||
|
||||
retVal = binary;
|
||||
} else {
|
||||
retVal = loadResourceFromRequest(theRequest, theMethodBinding, theResourceType);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getRequestResource(RequestDetails theRequest, ParamStyle myParamStyle, Class<? extends IBaseResource> myResourceBundleType) {
|
||||
// TODO: don't use a default encoding, just fail!
|
||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
|
||||
|
||||
IParser parser = encoding.newParser(theRequest.getServer().getFhirContext());
|
||||
|
||||
Reader reader;
|
||||
try {
|
||||
reader = 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()));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
return resourceList;
|
||||
}
|
||||
case RESOURCE_BUNDLE:
|
||||
return parser.parseResource(myResourceBundleType, reader);
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Unknown type: " + myParamStyle); // should not happen
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
@ -32,17 +31,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;
|
||||
|
@ -189,10 +186,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(';');
|
||||
|
@ -218,8 +215,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);
|
||||
|
@ -234,12 +231,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) {
|
||||
List<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||
float bestQ = -1f;
|
||||
EncodingEnum retVal = null;
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
if (acceptValues != null) {
|
||||
for (String nextAcceptHeaderValue : acceptValues) {
|
||||
StringTokenizer tok = new StringTokenizer(nextAcceptHeaderValue, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String nextToken = tok.nextToken();
|
||||
|
@ -350,10 +346,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;
|
||||
}
|
||||
|
@ -394,14 +390,14 @@ public class RestfulServerUtils {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public static Integer extractCountParameter(HttpServletRequest theRequest) {
|
||||
public static Integer extractCountParameter(RequestDetails theRequest) {
|
||||
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:
|
||||
|
@ -418,17 +414,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>();
|
||||
|
||||
|
@ -450,7 +435,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);
|
||||
}
|
||||
}
|
||||
|
@ -522,10 +508,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;
|
||||
}
|
||||
|
@ -535,54 +520,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() + '"');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,26 +583,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
|
||||
|
@ -630,38 +613,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());
|
||||
|
@ -669,17 +650,19 @@ 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) {
|
||||
String countString = theRequest.getParameter(name);
|
||||
static Integer tryToExtractNamedParameter(RequestDetails theRequest, String name) {
|
||||
String[] countString = theRequest.getParameters().get(name);
|
||||
Integer count = null;
|
||||
if (isNotBlank(countString)) {
|
||||
if (countString != null && countString.length > 0 && isNotBlank(countString[0])) {
|
||||
try {
|
||||
count = Integer.parseInt(countString);
|
||||
count = Integer.parseInt(countString[0]);
|
||||
} catch (NumberFormatException e) {
|
||||
ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
|
||||
}
|
||||
|
|
|
@ -1,67 +1,27 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
|
||||
public class RestulfulServerConfiguration {
|
||||
|
||||
private List<ResourceBinding> resourceBindings;
|
||||
private Collection<ResourceBinding> resourceBindings;
|
||||
private List<BaseMethodBinding<?>> serverBindings;
|
||||
private String implementationDescription;
|
||||
private String serverVersion;
|
||||
private String serverName;
|
||||
private FhirContext fhirContext;
|
||||
private ServletContext servletContext;
|
||||
private IServerAddressStrategy serverAddressStrategy;
|
||||
private String conformanceDate;
|
||||
|
||||
public RestulfulServerConfiguration() {
|
||||
}
|
||||
|
||||
public RestulfulServerConfiguration(RestfulServer theRestfulServer) {
|
||||
this.resourceBindings = new LinkedList<ResourceBinding>(theRestfulServer.getResourceBindings());
|
||||
this.serverBindings = theRestfulServer.getServerBindings();
|
||||
this.implementationDescription = theRestfulServer.getImplementationDescription();
|
||||
this.serverVersion = theRestfulServer.getServerVersion();
|
||||
this.serverName = theRestfulServer.getServerName();
|
||||
this.fhirContext = theRestfulServer.getFhirContext();
|
||||
this.serverAddressStrategy= theRestfulServer.getServerAddressStrategy();
|
||||
this.servletContext = theRestfulServer.getServletContext();
|
||||
if (servletContext != null) {
|
||||
InputStream inputStream = null;
|
||||
try {
|
||||
inputStream = getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF");
|
||||
if (inputStream != null) {
|
||||
Manifest manifest = new Manifest(inputStream);
|
||||
this.conformanceDate = manifest.getMainAttributes().getValue("Build-Time");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// fall through
|
||||
}
|
||||
finally {
|
||||
if (inputStream != null) {
|
||||
IOUtils.closeQuietly(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resourceBindings
|
||||
* @return the resourceBindings
|
||||
*/
|
||||
public List<ResourceBinding> getResourceBindings() {
|
||||
public Collection<ResourceBinding> getResourceBindings() {
|
||||
return resourceBindings;
|
||||
}
|
||||
|
||||
|
@ -69,7 +29,7 @@ public class RestulfulServerConfiguration {
|
|||
* Set the resourceBindings
|
||||
* @param resourceBindings the resourceBindings to set
|
||||
*/
|
||||
public RestulfulServerConfiguration setResourceBindings(List<ResourceBinding> resourceBindings) {
|
||||
public RestulfulServerConfiguration setResourceBindings(Collection<ResourceBinding> resourceBindings) {
|
||||
this.resourceBindings = resourceBindings;
|
||||
return this;
|
||||
}
|
||||
|
@ -142,23 +102,6 @@ public class RestulfulServerConfiguration {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the servletContext
|
||||
* @return the servletContext
|
||||
*/
|
||||
public ServletContext getServletContext() {
|
||||
return servletContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the servletContext
|
||||
* @param servletContext the servletContext to set
|
||||
*/
|
||||
public RestulfulServerConfiguration setServletContext(ServletContext servletContext) {
|
||||
this.servletContext = servletContext;
|
||||
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.
|
||||
|
@ -209,7 +152,4 @@ public class RestulfulServerConfiguration {
|
|||
this.conformanceDate = conformanceDate;
|
||||
}
|
||||
|
||||
public String getServerBaseForRequest(HttpServletRequest theRequest) {
|
||||
return getServerAddressStrategy().determineServerBase(getServletContext(), theRequest);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ 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.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
|
@ -71,7 +70,7 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), theResponse, oo, true, Collections.singleton(SummaryEnum.FALSE), statusCode, false, false, theRequestDetails);
|
||||
theRequestDetails.getResponse().streamResponseAsResource(oo, true, Collections.singleton(SummaryEnum.FALSE), statusCode, false, false);
|
||||
|
||||
// theResponse.setStatus(statusCode);
|
||||
// theRequestDetails.getServer().addHeadersToResponse(theResponse);
|
||||
|
|
|
@ -31,10 +31,6 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
|||
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
|
@ -149,6 +145,26 @@ public interface IServerInterceptor {
|
|||
*/
|
||||
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
|
||||
*
|
||||
|
@ -174,6 +190,20 @@ public interface IServerInterceptor {
|
|||
* @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
|
||||
*
|
||||
* @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
|
||||
* @param theServletRequest
|
||||
|
@ -190,6 +220,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
|
||||
*
|
||||
|
@ -212,6 +258,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
|
||||
* the end of the list is reached), after which {@link #handleException(RequestDetails, BaseServerResponseException, HttpServletRequest, HttpServletResponse)} is called for each interceptor.
|
||||
|
|
|
@ -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
|
||||
|
@ -67,21 +68,45 @@ public class InterceptorAdapter implements IServerInterceptor {
|
|||
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 {
|
||||
return null;
|
||||
|
|
|
@ -198,7 +198,7 @@ public class LoggingInterceptor extends InterceptorAdapter {
|
|||
} else if (theKey.startsWith("remoteAddr")) {
|
||||
return StringUtils.defaultString(myRequest.getRemoteAddr());
|
||||
} else if (theKey.equals("responseEncodingNoDefault")) {
|
||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingNoDefault(myRequest, myRequestDetails.getServer().getDefaultResponseEncoding());
|
||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingNoDefault(myRequestDetails, myRequestDetails.getServer().getDefaultResponseEncoding());
|
||||
if (encoding != null) {
|
||||
return encoding.name();
|
||||
} else {
|
||||
|
|
|
@ -171,7 +171,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
/*
|
||||
* It's not a browser...
|
||||
*/
|
||||
Set<String> highestRankedAcceptValues = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theRequestDetails.getServletRequest());
|
||||
Set<String> highestRankedAcceptValues = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theServletRequest);
|
||||
if (highestRankedAcceptValues.contains(Constants.CT_HTML) == false) {
|
||||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
/*
|
||||
* It's not a browser...
|
||||
*/
|
||||
Set<String> accept = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theRequestDetails.getServletRequest());
|
||||
Set<String> accept = RestfulServerUtils.parseAcceptHeaderAndReturnHighestRankedOptions(theServletRequest);
|
||||
if (!accept.contains(Constants.CT_HTML)) {
|
||||
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
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 HttpServletRequest myServletRequest;
|
||||
private HttpServletResponse myServletResponse;
|
||||
/**
|
||||
* @see BaseMethodBinding#loadRequestContents(RequestDetails)
|
||||
*/
|
||||
private static volatile IRequestReader ourRequestReader;
|
||||
private byte[] requestContents;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestDetails.class);
|
||||
private RestfulServer myServer;
|
||||
|
||||
public ServletRequestDetails() {
|
||||
super();
|
||||
setResponse(new ServletRestfulResponse(this));
|
||||
}
|
||||
|
||||
public RestfulServer getServer() {
|
||||
return myServer;
|
||||
}
|
||||
|
||||
public void setServer(RestfulServer theServer) {
|
||||
this.myServer = theServer;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
public HttpServletRequest getServletRequest() {
|
||||
return myServletRequest;
|
||||
}
|
||||
|
||||
public void setServletRequest(HttpServletRequest myServletRequest) {
|
||||
this.myServletRequest = myServletRequest;
|
||||
}
|
||||
|
||||
public HttpServletResponse getServletResponse() {
|
||||
return myServletResponse;
|
||||
}
|
||||
|
||||
public void setServletResponse(HttpServletResponse myServletResponse) {
|
||||
this.myServletResponse = myServletResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader getReader() throws IOException {
|
||||
return getServletRequest().getReader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
return getServletRequest().getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerBaseForRequest() {
|
||||
return getServer().getServerBaseForRequest(getServletRequest());
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -23,6 +23,8 @@ import ca.uhn.fhir.context.ConfigurationException;
|
|||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
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.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
|
||||
|
@ -35,7 +37,7 @@ import ca.uhn.fhir.util.ReflectionUtil;
|
|||
* Conformance Rest Service
|
||||
* @author Peter Van Houte
|
||||
*/
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
public abstract class AbstractConformanceRestServer extends AbstractJaxRsRestServer implements IConformanceRestServer {
|
||||
|
||||
public static final String PATH = "/";
|
||||
|
@ -73,9 +75,8 @@ public abstract class AbstractConformanceRestServer extends AbstractJaxRsRestSer
|
|||
@GET
|
||||
@OPTIONS
|
||||
@Path("/metadata")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response conformance(String string) {
|
||||
String conformanceString = getParser().encodeResourceToString(myConformance);
|
||||
String conformanceString = getParser(createRequestDetails(null, RequestTypeEnum.OPTIONS, RestOperationTypeEnum.METADATA)).encodeResourceToString(myConformance);
|
||||
ResponseBuilder entity = Response.status(Constants.STATUS_HTTP_200_OK).entity(conformanceString);
|
||||
entity.header("Access-Control-Allow-Origin", "*");
|
||||
return entity.build();
|
||||
|
|
|
@ -1,49 +1,40 @@
|
|||
package ca.uhn.fhir.jaxrs.server;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.HttpHeaders;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequestDetails;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
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;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
|
||||
/**
|
||||
* Abstract Jax Rs Rest Server
|
||||
* @author axmpm
|
||||
* @author Peter Van Houte
|
||||
*
|
||||
*/
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
|
||||
public abstract class AbstractJaxRsRestServer {
|
||||
public abstract class AbstractJaxRsRestServer implements IRestfulServerDefaults {
|
||||
|
||||
private static Logger ourLog = LoggerFactory.getLogger(AbstractJaxRsRestServer.class);
|
||||
public static FhirContext CTX = FhirContext.forDstu2();
|
||||
|
||||
@Context
|
||||
protected UriInfo info;
|
||||
private UriInfo info;
|
||||
@Context
|
||||
HttpHeaders headers;
|
||||
private HttpHeaders headers;
|
||||
|
||||
private IParser jsonParser = getFhirContext().newJsonParser();
|
||||
private IParser xmlParser = getFhirContext().newXmlParser();
|
||||
private String baseUri;
|
||||
|
||||
public static FhirContext getFhirContext() {
|
||||
public FhirContext getFhirContext() {
|
||||
return CTX;
|
||||
}
|
||||
|
||||
|
@ -51,7 +42,7 @@ public abstract class AbstractJaxRsRestServer {
|
|||
* param and query methods
|
||||
*/
|
||||
protected HashMap<String, String[]> getQueryMap() {
|
||||
MultivaluedMap<String, String> queryParameters = info.getQueryParameters();
|
||||
MultivaluedMap<String, String> queryParameters = getInfo().getQueryParameters();
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
for (String key : queryParameters.keySet()) {
|
||||
params.put(key, queryParameters.get(key).toArray(new String[] {}));
|
||||
|
@ -59,63 +50,48 @@ public abstract class AbstractJaxRsRestServer {
|
|||
return params;
|
||||
}
|
||||
|
||||
private String getParam(String string) {
|
||||
for (Entry<String, List<String>> entry : info.getQueryParameters().entrySet()) {
|
||||
if (string.equalsIgnoreCase(entry.getKey())) {
|
||||
return entry.getValue().iterator().next();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected Integer getIntParam(String string) {
|
||||
String param = getParam(string);
|
||||
return param == null ? 0 : Integer.valueOf(param);
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
HardcodedServerAddressStrategy addressStrategy = new HardcodedServerAddressStrategy();
|
||||
addressStrategy.setValue(getBaseUri());
|
||||
return addressStrategy;
|
||||
}
|
||||
|
||||
protected String getBaseUri() {
|
||||
if(this.baseUri == null) {
|
||||
this.baseUri = info.getBaseUri().toASCIIString();
|
||||
}
|
||||
ourLog.debug("BaseUri is equal to %s", baseUri);
|
||||
return this.baseUri;
|
||||
return getInfo().getBaseUri().toASCIIString();
|
||||
}
|
||||
|
||||
/**
|
||||
* PARSING METHODS
|
||||
*/
|
||||
public IParser getParser() {
|
||||
IParser parser = MediaType.APPLICATION_XML.equals(getParserType()) ? xmlParser : jsonParser;
|
||||
return parser.setPrettyPrint(getPrettyPrint());
|
||||
public IParser getParser(JaxRsRequestDetails theRequestDetails) {
|
||||
return RestfulServerUtils.getNewParser(getFhirContext(), theRequestDetails);
|
||||
}
|
||||
|
||||
private boolean getPrettyPrint() {
|
||||
String printPretty = getParam("_pretty");
|
||||
return printPretty == null || printPretty.trim().length() == 0 ? true : Boolean.valueOf(printPretty);
|
||||
protected JaxRsRequestDetails createRequestDetails(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation) {
|
||||
JaxRsRequestDetails theRequest = new JaxRsRequestDetails(headers, resourceString);
|
||||
theRequest.setFhirServerBase(getBaseUri());
|
||||
theRequest.setRestOperationType(restOperation);
|
||||
theRequest.setServer(this);
|
||||
theRequest.setParameters(getQueryMap());
|
||||
theRequest.setRequestType(requestType);
|
||||
return theRequest;
|
||||
}
|
||||
|
||||
protected String getParserType() {
|
||||
if ((headers != null && headers.getMediaType() != null && headers.getMediaType().getSubtype() != null
|
||||
&& headers.getMediaType().getSubtype().contains("xml")) || getDefaultResponseEncoding() == EncodingEnum.XML
|
||||
|| "xml".equals(getParam("_format"))) {
|
||||
return MediaType.APPLICATION_XML;
|
||||
} else {
|
||||
return MediaType.APPLICATION_JSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the info
|
||||
* @return the info
|
||||
*/
|
||||
public UriInfo getInfo() {
|
||||
return info;
|
||||
}
|
||||
|
||||
Response createResponse(IBaseResource resource) {
|
||||
Bundle resultingBundle = new Bundle();
|
||||
resultingBundle.addEntry().setResource((IResource) resource);
|
||||
return ok(encodeResponse(resultingBundle));
|
||||
}
|
||||
|
||||
protected Response ok(String entity) {
|
||||
return Response.status(Constants.STATUS_HTTP_200_OK).header("Content-Type", getParserType()).entity(entity).build();
|
||||
}
|
||||
|
||||
private String encodeResponse(Bundle resource) {
|
||||
return resource == null ? "null" : getParser().encodeBundleToString(resource);
|
||||
/**
|
||||
* Set the info
|
||||
* @param info the info to set
|
||||
*/
|
||||
public void setInfo(UriInfo info) {
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -125,4 +101,23 @@ public abstract class AbstractJaxRsRestServer {
|
|||
return EncodingEnum.JSON;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultPrettyPrint() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ETagSupportEnum getETagSupport() {
|
||||
return ETagSupportEnum.DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddProfileTagEnum getAddProfileTag() {
|
||||
return AddProfileTagEnum.NEVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package ca.uhn.fhir.jaxrs.server;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.interceptor.Interceptors;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
|
@ -15,215 +15,165 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ca.uhn.fhir.jaxrs.server.interceptor.ExceptionInterceptor;
|
||||
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequestDetails;
|
||||
import ca.uhn.fhir.jaxrs.server.util.MethodBindings;
|
||||
import ca.uhn.fhir.jaxrs.server.util.RestfulServerDefaults;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.CreateMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.OperationMethodBinding;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||
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.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.ResourceBinding;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
/**
|
||||
* Fhir Physician Rest Service
|
||||
* @author axmpm
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings({ "unused"})
|
||||
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
|
||||
public abstract class AbstractResourceRestServer<R extends IResource> extends AbstractJaxRsRestServer implements IResourceProvider {
|
||||
@Produces({MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN})
|
||||
@Consumes({MediaType.APPLICATION_FORM_URLENCODED,MediaType.APPLICATION_JSON, "application/json+fhir", "application/xml+fhir"})
|
||||
public abstract class AbstractResourceRestServer<R extends IResource> extends AbstractJaxRsRestServer implements IResourceRestServer<R> {
|
||||
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(AbstractResourceRestServer.class);
|
||||
|
||||
private ResourceBinding myServerBinding = new ResourceBinding();
|
||||
private IRestfulServerDefaults serverDefaults = new RestfulServerDefaults();
|
||||
private MethodBindings bindings = new MethodBindings();
|
||||
private static MethodBindings bindings;
|
||||
|
||||
public AbstractResourceRestServer(Class<?> subclass) {
|
||||
bindings.findMethods(this, subclass, getFhirContext());
|
||||
initBindings(subclass);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
Response search() throws Exception {
|
||||
return execute();
|
||||
private void initBindings(Class<?> subclass) {
|
||||
if(bindings == null) {
|
||||
MethodBindings methodBindings = new MethodBindings();
|
||||
methodBindings.findMethods(this, subclass, getFhirContext());
|
||||
bindings = methodBindings;
|
||||
}
|
||||
}
|
||||
|
||||
protected Response customOperation(final IBaseResource resource, RequestTypeEnum requestType)
|
||||
throws Exception, IllegalAccessException, InvocationTargetException {
|
||||
OperationMethodBinding method = bindings.getBinding(OperationMethodBinding.class);
|
||||
final RequestDetails theRequest = createRequestDetails(resource, requestType);
|
||||
final Object[] paramsServer = bindings.createParams(resource, method, theRequest);
|
||||
Parameters result = (Parameters) method.getMethod().invoke(this, paramsServer);
|
||||
return ok(getParser().encodeResourceToString(result));
|
||||
@Override
|
||||
protected String getBaseUri() {
|
||||
try {
|
||||
return new URL(getInfo().getBaseUri().toURL(), getResourceType().getSimpleName()).toExternalForm();
|
||||
} catch(Exception e) {
|
||||
// cannot happen
|
||||
return null;
|
||||
}
|
||||
|
||||
Response create(final Map<String, String[]> params, R resource) throws Exception {
|
||||
CreateMethodBinding method = bindings.getBinding(CreateMethodBinding.class);
|
||||
final RequestDetails theRequest = createRequestDetails(resource, null);
|
||||
final Object[] paramsServer = bindings.createParams(resource, method, theRequest);
|
||||
MethodOutcome result = (MethodOutcome) method.getMethod().invoke(this, paramsServer);
|
||||
return createResponse(result.getResource());
|
||||
}
|
||||
|
||||
@POST
|
||||
@Override
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
public Response create(final String resourceString)
|
||||
throws Exception {
|
||||
return create(getQueryMap(), parseResource(resourceString));
|
||||
return executeMethod(resourceString, RequestTypeEnum.POST, RestOperationTypeEnum.CREATE, null);
|
||||
}
|
||||
|
||||
@POST
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
@Path("/_search")
|
||||
@Override
|
||||
public Response searchWithPost() throws Exception {
|
||||
return search();
|
||||
return executeMethod(null, RequestTypeEnum.POST, RestOperationTypeEnum.SEARCH_TYPE, null);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Override
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
public Response find(@PathParam("id") final String id) {
|
||||
final R resource = find(new IdDt(id));
|
||||
return createSingleResponse(resource);
|
||||
public Response search() throws Exception {
|
||||
return executeMethod(null, RequestTypeEnum.GET, RestOperationTypeEnum.SEARCH_TYPE, null);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Override
|
||||
@Path("/{id}")
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
public Response update(@PathParam("id") final String id, final String resourceString)
|
||||
throws Exception {
|
||||
final R resource = parseResource(resourceString);
|
||||
// final MethodOutcome update = update(new IdDt(resource.getId()), practitioner);
|
||||
// return createResponse(update.getResource());
|
||||
return createResponse(resource);
|
||||
return executeMethod(resourceString, RequestTypeEnum.PUT, RestOperationTypeEnum.UPDATE, id);
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Override
|
||||
@Path("/{id}")
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
public Response delete(@PathParam("id") final String id)
|
||||
public Response delete(@PathParam("id") final String id) throws Exception {
|
||||
return executeMethod(null, RequestTypeEnum.DELETE, RestOperationTypeEnum.DELETE, id);
|
||||
}
|
||||
|
||||
|
||||
@GET
|
||||
@Override
|
||||
@Path("/{id}")
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
public Response find(@PathParam("id") final String id) throws Exception {
|
||||
return executeMethod(null, RequestTypeEnum.GET, RestOperationTypeEnum.READ, id);
|
||||
}
|
||||
|
||||
protected Response customOperation(final String resource, RequestTypeEnum requestType, String id, String operationName, RestOperationTypeEnum operationType)
|
||||
throws Exception {
|
||||
// final MethodOutcome delete = delete(new IdDt(id));
|
||||
// return createResponse(delete.getResource());
|
||||
return null;
|
||||
return executeMethod(resource, requestType, operationType, id, bindings.getBinding(operationType, operationName));
|
||||
}
|
||||
|
||||
@GET
|
||||
@Override
|
||||
@Path("/{id}/_history/{version}")
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
public Response findHistory(@PathParam("id") final String id, @PathParam("version") final String version) {
|
||||
final IdDt dt = new IdDt(getBaseUri(), getResourceType().getSimpleName(), id, version);
|
||||
final R resource = findHistory(dt);
|
||||
return createSingleResponse(resource);
|
||||
public Response findHistory(@PathParam("id") final String id, @PathParam("version") final String versionString)
|
||||
throws BaseServerResponseException, IOException {
|
||||
BaseMethodBinding<?> method = bindings.getBinding(RestOperationTypeEnum.VREAD);
|
||||
final RequestDetails theRequest = createRequestDetails(null, RequestTypeEnum.GET, RestOperationTypeEnum.VREAD);
|
||||
if (id == null) {
|
||||
throw new InvalidRequestException("Don't know how to handle request path: " + getInfo().getRequestUri().toASCIIString());
|
||||
}
|
||||
theRequest.setId(new IdDt(getBaseUri(), id, UrlUtil.unescape(versionString)));
|
||||
return (Response) method.invokeServer(this, theRequest);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Override
|
||||
@Path("/{id}/{compartment}")
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
public Response findCompartment(@PathParam("id") final String id, @PathParam("compartment") final String compartment) {
|
||||
final IdDt dt = new IdDt(getBaseUri(), getResourceType().getSimpleName(), id);
|
||||
final R resource = find(new IdDt(id));
|
||||
return createResponse(resource);
|
||||
public Response findCompartment(@PathParam("id") final String id, @PathParam("compartment") final String compartment) throws BaseServerResponseException, IOException {
|
||||
BaseMethodBinding<?> method = bindings.getBinding(RestOperationTypeEnum.SEARCH_TYPE, compartment);
|
||||
final RequestDetails theRequest = createRequestDetails(null, RequestTypeEnum.GET, RestOperationTypeEnum.VREAD);
|
||||
if (id == null) {
|
||||
throw new InvalidRequestException("Don't know how to handle request path: " + getInfo().getRequestUri().toASCIIString());
|
||||
}
|
||||
theRequest.setCompartmentName(compartment);
|
||||
theRequest.setId(new IdDt(getBaseUri(), id));
|
||||
return (Response) method.invokeServer(this, theRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* PARSING METHODS
|
||||
*/
|
||||
private Response createSingleResponse(final R resource) {
|
||||
return ok(getParser().encodeResourceToString(resource));
|
||||
private <T extends BaseMethodBinding<?>> Response executeMethod(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation, String id)
|
||||
throws BaseServerResponseException, IOException {
|
||||
BaseMethodBinding<?> method = bindings.getBinding(restOperation);
|
||||
return executeMethod(resourceString, requestType, restOperation, id, method);
|
||||
}
|
||||
|
||||
Response createResponse(final IBaseResource resource) {
|
||||
final Bundle resultingBundle = new Bundle();
|
||||
resultingBundle.addEntry().setResource((IResource) resource);
|
||||
return ok(encodeToString(resultingBundle));
|
||||
}
|
||||
|
||||
Response createResponse(final List<R> resources) {
|
||||
final Bundle resultingBundle = new Bundle();
|
||||
for (final R resource : resources) {
|
||||
addBundleEntry(resultingBundle, resource);
|
||||
}
|
||||
return ok(encodeToString(resultingBundle));
|
||||
}
|
||||
|
||||
protected Response ok(String entity) {
|
||||
return Response.status(Constants.STATUS_HTTP_200_OK).header("Content-Type", getParserType()).entity(entity).build();
|
||||
}
|
||||
|
||||
protected String encodeToString(final Bundle resource) {
|
||||
return resource != null ? getParser().encodeBundleToString(resource) : "null";
|
||||
}
|
||||
|
||||
private R parseResource(final String resource) {
|
||||
return getParser().parseResource(getResourceType(), resource);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private void addBundleEntry(final Bundle resultingBundle, final R resource) {
|
||||
final BundleEntry entry = resultingBundle.addEntry();
|
||||
entry.setResource(resource);
|
||||
if (resource != null && resource.getId() != null) {
|
||||
entry.setId(resource.getId());
|
||||
}
|
||||
private Response executeMethod(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation, String id,
|
||||
BaseMethodBinding<?> method)
|
||||
throws IOException {
|
||||
final RequestDetails theRequest = createRequestDetails(resourceString, requestType, restOperation, id);
|
||||
return (Response) method.invokeServer(this, theRequest);
|
||||
}
|
||||
|
||||
|
||||
private RequestDetails createRequestDetails(final IBaseResource resource, RequestTypeEnum requestType) {
|
||||
final RequestDetails theRequest = new RequestDetails() {
|
||||
// @Override
|
||||
// public String getHeader(String headerIfNoneExist) {
|
||||
// List<String> requestHeader = headers.getRequestHeader(headerIfNoneExist);
|
||||
// return (requestHeader == null || requestHeader.size() == 0) ? null : requestHeader.get(0);
|
||||
// }
|
||||
};
|
||||
theRequest.setFhirServerBase(getBaseUri());
|
||||
// theRequest.setServer(this);
|
||||
theRequest.setParameters(getQueryMap());
|
||||
// theRequest.setRequestContent(resource);
|
||||
theRequest.setRequestType(requestType);
|
||||
protected JaxRsRequestDetails createRequestDetails(final String resourceString, RequestTypeEnum requestType, RestOperationTypeEnum restOperation, String id) {
|
||||
JaxRsRequestDetails theRequest = super.createRequestDetails(resourceString, requestType, restOperation);
|
||||
theRequest.setId(StringUtils.isBlank(id) ? null : new IdDt(getResourceType().getName(), UrlUtil.unescape(id)));
|
||||
if(restOperation == RestOperationTypeEnum.UPDATE) {
|
||||
String contentLocation = theRequest.getHeader(Constants.HEADER_CONTENT_LOCATION);
|
||||
if (contentLocation != null) {
|
||||
theRequest.setId(new IdDt(contentLocation));
|
||||
}
|
||||
}
|
||||
return theRequest;
|
||||
}
|
||||
|
||||
public Response execute() {
|
||||
SearchMethodBinding method = bindings.getBinding(SearchMethodBinding.class);
|
||||
final RequestDetails theRequest = createRequestDetails(null, null);
|
||||
final Object[] paramsServer = bindings.createParams(null, method, theRequest);
|
||||
Object result = null; //method.invokeServer(null, paramsServer);
|
||||
final IBundleProvider bundle = (IBundleProvider) result;
|
||||
IVersionSpecificBundleFactory bundleFactory = getFhirContext().newBundleFactory();
|
||||
// bundleFactory.initializeBundleFromBundleProvider(this, bundle, EncodingEnum.JSON, info.getAbsolutePath().toASCIIString(),
|
||||
// info.getAbsolutePath().toASCIIString(), getPrettyPrint(), getIntParam("_getpagesoffset"), getIntParam("_count"), null,
|
||||
// BundleTypeEnum.SEARCHSET, Collections.emptySet());
|
||||
IBaseResource resource = bundleFactory.getResourceBundle();
|
||||
return ok(getParser().encodeResourceToString(resource));
|
||||
}
|
||||
|
||||
public R find(final IdDt theId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public R findHistory(final IdDt theId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract Class<R> getResourceType();
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
package ca.uhn.fhir.jaxrs.server;
|
||||
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
|
||||
public interface IConformanceRestServer extends IResourceProvider {
|
||||
public interface IConformanceRestServer extends IResourceProvider, IRestfulServerDefaults {
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package ca.uhn.fhir.jaxrs.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
||||
public interface IResourceRestServer<T> extends IRestfulServer<JaxRsRequestDetails>, IResourceProvider {
|
||||
|
||||
Response search()
|
||||
throws Exception;
|
||||
|
||||
Response create(String resourceString)
|
||||
throws Exception;
|
||||
|
||||
Response searchWithPost()
|
||||
throws Exception;
|
||||
|
||||
Response find(String id) throws Exception;
|
||||
|
||||
Response update(String id, String resourceString)
|
||||
throws Exception;
|
||||
|
||||
Response delete(String id)
|
||||
throws Exception;
|
||||
|
||||
Response findHistory(String id, String version) throws BaseServerResponseException, IOException;
|
||||
|
||||
Response findCompartment(String id, String compartment) throws BaseServerResponseException, IOException;
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package ca.uhn.fhir.jaxrs.server;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
|
||||
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequestDetails;
|
||||
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;
|
||||
|
||||
public class JaxRsRestfulResponse extends RestfulResponse<JaxRsRequestDetails> {
|
||||
|
||||
public JaxRsRestfulResponse(String resourceString, JaxRsRequestDetails jaxRsRequestDetails) {
|
||||
super(jaxRsRequestDetails);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Writer getResponseWriter(int statusCode, String contentType, String charset, boolean respondGzip)
|
||||
throws UnsupportedEncodingException, IOException {
|
||||
if (respondGzip) {
|
||||
addHeader(Constants.HEADER_CONTENT_ENCODING, Constants.ENCODING_GZIP);
|
||||
return new OutputStreamWriter(new GZIPOutputStream(new ByteArrayOutputStream()), Constants.CHARSET_NAME_UTF8);
|
||||
} else {
|
||||
return new StringWriter();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response sendWriterResponse(int status, String contentType, String charset, Writer writer) {
|
||||
return Response.status(status)/*.header(HttpHeaders.CONTENT_TYPE, charset)*/.header(Constants.HEADER_CONTENT_TYPE, contentType).entity(writer.toString()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object sendAttachmentResponse(IBaseBinary bin, int statusCode, String contentType) throws IOException {
|
||||
ResponseBuilder response = Response.status(statusCode);
|
||||
for (Entry<String, String> header : getHeaders().entrySet()) {
|
||||
response.header(header.getKey(), header.getValue());
|
||||
}
|
||||
if (bin.getContent() != null && bin.getContent().length > 0) {
|
||||
response.header(Constants.HEADER_CONTENT_TYPE, contentType).entity(bin.getContent());
|
||||
}
|
||||
return response.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object returnResponse(ParseAction<?> outcome, int operationStatus, boolean allowPrefer, MethodOutcome response,
|
||||
String resourceName)
|
||||
throws IOException {
|
||||
Writer writer = new StringWriter();
|
||||
IParser parser = RestfulServerUtils.getNewParser(getRequestDetails().getServer().getFhirContext(), getRequestDetails());
|
||||
if(outcome != null) {
|
||||
outcome.execute(parser, writer);
|
||||
}
|
||||
return Response.status(operationStatus).header(Constants.HEADER_CONTENT_TYPE, getParserType()).entity(writer.toString()).build();
|
||||
}
|
||||
|
||||
protected String getParserType() {
|
||||
EncodingEnum encodingEnum = RestfulServerUtils.determineResponseEncodingWithDefault(getRequestDetails());
|
||||
return encodingEnum == EncodingEnum.JSON ? MediaType.APPLICATION_JSON : MediaType.APPLICATION_XML;
|
||||
}
|
||||
|
||||
}
|
|
@ -36,7 +36,7 @@ public class ExceptionInterceptor {
|
|||
@Context
|
||||
private HttpHeaders headers;
|
||||
|
||||
FhirContext fhirContext = AbstractJaxRsRestServer.getFhirContext();
|
||||
FhirContext fhirContext = AbstractJaxRsRestServer.CTX;
|
||||
|
||||
@AroundInvoke
|
||||
public Object intercept(final InvocationContext ctx) throws Exception {
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
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 ca.uhn.fhir.jaxrs.server.AbstractJaxRsRestServer;
|
||||
import ca.uhn.fhir.jaxrs.server.JaxRsRestfulResponse;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
|
||||
public class JaxRsRequestDetails extends RequestDetails {
|
||||
|
||||
private String theResourceString;
|
||||
private HttpHeaders headers;
|
||||
private AbstractJaxRsRestServer myServer;
|
||||
|
||||
public AbstractJaxRsRestServer getServer() {
|
||||
return myServer;
|
||||
}
|
||||
|
||||
public void setServer(AbstractJaxRsRestServer theServer) {
|
||||
this.myServer = theServer;
|
||||
}
|
||||
|
||||
public JaxRsRequestDetails(HttpHeaders headers, String resourceString) {
|
||||
this.headers = headers;
|
||||
this.theResourceString = resourceString;
|
||||
setResponse(new JaxRsRestfulResponse(resourceString, this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String headerIfNoneExist) {
|
||||
List<String> requestHeader = headers.getRequestHeader(headerIfNoneExist);
|
||||
return (requestHeader == null || requestHeader.size() == 0) ? 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 theResourceString.getBytes(ResourceParameter.determineRequestCharset(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader getReader()
|
||||
throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
|
@ -3,32 +3,21 @@ package ca.uhn.fhir.jaxrs.server.util;
|
|||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jaxrs.server.AbstractResourceRestServer;
|
||||
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.CreateMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.DeleteMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.IParameter;
|
||||
import ca.uhn.fhir.rest.method.OperationMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.UpdateMethodBinding;
|
||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class MethodBindings {
|
||||
|
||||
/** BaseOutcomeReturningMethodBinding */
|
||||
private ConcurrentHashMap<String, CreateMethodBinding> createMethods = new ConcurrentHashMap<String, CreateMethodBinding>();
|
||||
private ConcurrentHashMap<String, UpdateMethodBinding> updateMethods = new ConcurrentHashMap<String, UpdateMethodBinding>();
|
||||
private ConcurrentHashMap<String, DeleteMethodBinding> delete = new ConcurrentHashMap<String, DeleteMethodBinding>();
|
||||
|
||||
/** BaseResourceReturingMethodBinding */
|
||||
private ConcurrentHashMap<String, SearchMethodBinding> searchMethods = new ConcurrentHashMap<String, SearchMethodBinding>();
|
||||
private ConcurrentHashMap<String, OperationMethodBinding> operationMethods = new ConcurrentHashMap<String, OperationMethodBinding>();
|
||||
/** ALL METHOD BINDINGS */
|
||||
ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String, BaseMethodBinding<?>>> allBindings = new ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String,BaseMethodBinding<?>>>();
|
||||
|
||||
public <T extends AbstractResourceRestServer<?>> void findMethods(T theProvider, Class<?> subclass, FhirContext fhirContext) {
|
||||
for (final Method m : ReflectionUtil.getDeclaredMethods(subclass)) {
|
||||
|
@ -36,44 +25,44 @@ public class MethodBindings {
|
|||
if (foundMethodBinding == null) {
|
||||
continue;
|
||||
}
|
||||
ConcurrentHashMap map = getMap(foundMethodBinding.getClass());
|
||||
if (map.contains(theProvider.getResourceType().getName())) {
|
||||
throw new IllegalArgumentException("Multiple Search Method Bindings Found : " + foundMethodBinding.getMethod() + " -- "
|
||||
+ foundMethodBinding.getMethod());
|
||||
ConcurrentHashMap<String, BaseMethodBinding<?>> map = getAllBindingsMap(foundMethodBinding.getRestOperationType());
|
||||
if (foundMethodBinding instanceof OperationMethodBinding) {
|
||||
OperationMethodBinding binding = (OperationMethodBinding) foundMethodBinding;
|
||||
putIfAbsent(map, binding.getName(), binding);
|
||||
} else if (foundMethodBinding instanceof SearchMethodBinding) {
|
||||
Search search = m.getAnnotation(Search.class);
|
||||
String compartmentName = StringUtils.defaultIfBlank(search.compartmentName(), "");
|
||||
putIfAbsent(map, compartmentName, foundMethodBinding);
|
||||
} else {
|
||||
map.put(theProvider.getResourceType().getName(), foundMethodBinding);
|
||||
putIfAbsent(map, "", foundMethodBinding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <T> ConcurrentHashMap<String, T> getMap(Class<T> class1) {
|
||||
if(class1.isAssignableFrom(CreateMethodBinding.class)) return (ConcurrentHashMap<String, T>) createMethods;
|
||||
if(class1.isAssignableFrom(UpdateMethodBinding.class)) return (ConcurrentHashMap<String, T>) updateMethods;
|
||||
if(class1.isAssignableFrom(DeleteMethodBinding.class)) return (ConcurrentHashMap<String, T>) delete;
|
||||
if(class1.isAssignableFrom(SearchMethodBinding.class)) return (ConcurrentHashMap<String, T>) searchMethods;
|
||||
if(class1.isAssignableFrom(OperationMethodBinding.class)) return (ConcurrentHashMap<String, T>) operationMethods;
|
||||
return new ConcurrentHashMap();
|
||||
private void putIfAbsent(ConcurrentHashMap<String, BaseMethodBinding<?>> map, String key, BaseMethodBinding binding) {
|
||||
if (map.containsKey(key)) {
|
||||
throw new IllegalArgumentException("Multiple Search Method Bindings Found : " + map.get(key) + " -- " + binding.getMethod());
|
||||
}
|
||||
map.put(key, binding);
|
||||
}
|
||||
|
||||
public Object[] createParams(IBaseResource resource, final BaseMethodBinding<?> method, final RequestDetails theRequest) {
|
||||
final Object[] paramsServer = new Object[method.getParameters().size()];
|
||||
for (int i = 0; i < method.getParameters().size(); i++) {
|
||||
final IParameter param = method.getParameters().get(i);
|
||||
if(param instanceof ResourceParameter) {
|
||||
paramsServer[i] = resource;
|
||||
} else {
|
||||
paramsServer[i] = param.translateQueryParametersIntoServerArgument(theRequest, null, method);
|
||||
}
|
||||
}
|
||||
return paramsServer;
|
||||
private ConcurrentHashMap<String,BaseMethodBinding<?>> getAllBindingsMap(final RestOperationTypeEnum restOperationTypeEnum) {
|
||||
allBindings.putIfAbsent(restOperationTypeEnum, new ConcurrentHashMap<String, BaseMethodBinding<?>>());
|
||||
return allBindings.get(restOperationTypeEnum);
|
||||
}
|
||||
|
||||
public <T> T getBinding(Class<T> clazz) {
|
||||
ConcurrentHashMap map = getMap((Class<? extends BaseMethodBinding>) clazz);
|
||||
if(map.values().size() == 0) {
|
||||
public BaseMethodBinding<?> getBinding(RestOperationTypeEnum operationType, String qualifier) {
|
||||
String nonEmptyQualifier = StringUtils.defaultIfBlank(qualifier, "");
|
||||
ConcurrentHashMap<String, BaseMethodBinding<?>> map = getAllBindingsMap(operationType);
|
||||
if(!map.containsKey(nonEmptyQualifier)) {
|
||||
throw new UnsupportedOperationException();
|
||||
} else {
|
||||
return map.get(nonEmptyQualifier);
|
||||
}
|
||||
return (T) map.values().iterator().next();
|
||||
}
|
||||
|
||||
public BaseMethodBinding<?> getBinding(RestOperationTypeEnum operationType) {
|
||||
return getBinding(operationType, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<artifactId>hapi-fhir-jaxrsserver-example</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>HAPI FHIR JPA Server - Example</name>
|
||||
<name>HAPI FHIR JAX-RS Server - Example</name>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
|
|
|
@ -8,6 +8,8 @@ import javax.ws.rs.Path;
|
|||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
/**
|
||||
* Conformance Rest Service
|
||||
* @author Peter Van Houte
|
||||
|
@ -15,7 +17,7 @@ import javax.ws.rs.core.MediaType;
|
|||
@Local
|
||||
@Path(ConformanceRestServer.PATH)
|
||||
@Stateless
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Produces({MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML})
|
||||
public class ConformanceRestServer extends ca.uhn.fhir.jaxrs.server.AbstractConformanceRestServer {
|
||||
|
||||
private static final String SERVER_VERSION = "1.0.0";
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jaxrs.server.example;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -10,12 +12,15 @@ import javax.interceptor.Interceptors;
|
|||
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.AbstractResourceRestServer;
|
||||
import ca.uhn.fhir.jaxrs.server.interceptor.ExceptionInterceptor;
|
||||
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;
|
||||
|
@ -33,8 +38,16 @@ 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;
|
||||
|
||||
/**
|
||||
* Fhir Physician Rest Service
|
||||
|
@ -44,7 +57,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
|||
@Local(IFhirPatientRestServer.class)
|
||||
@Path(FhirPatientRestServer.PATH)
|
||||
@Stateless
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Produces({MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML})
|
||||
public class FhirPatientRestServer extends AbstractResourceRestServer<Patient> implements IFhirPatientRestServer {
|
||||
|
||||
static final String PATH = "/Patient";
|
||||
|
@ -57,8 +70,8 @@ public class FhirPatientRestServer extends AbstractResourceRestServer<Patient> i
|
|||
}
|
||||
|
||||
static {
|
||||
patients.put(""+counter, createPatient("Agfa"));
|
||||
patients.put(""+(counter), createPatient("Healthcare"));
|
||||
patients.put(""+counter, createPatient("Van Houte"));
|
||||
patients.put(""+(counter), createPatient("Agnew"));
|
||||
for(int i = 0 ; i<20 ; i++) {
|
||||
patients.put(""+(counter), createPatient("Random Patient " + counter));
|
||||
}
|
||||
|
@ -135,7 +148,7 @@ public class FhirPatientRestServer extends AbstractResourceRestServer<Patient> i
|
|||
}
|
||||
|
||||
@Override
|
||||
@Read(version = false)
|
||||
@Read(version = true)
|
||||
public Patient findHistory(@IdParam final IdDt theId) {
|
||||
if (patients.containsKey(theId.getIdPart())) {
|
||||
final List<Patient> list = patients.get(theId.getIdPart());
|
||||
|
@ -174,9 +187,19 @@ public class FhirPatientRestServer extends AbstractResourceRestServer<Patient> i
|
|||
@Path("/{id}/$last")
|
||||
@Interceptors(ExceptionInterceptor.class)
|
||||
@Override
|
||||
public Response operationLastGet(final String resource)
|
||||
public Response operationLastGet(@PathParam("id") String id)
|
||||
throws Exception {
|
||||
return customOperation(null, RequestTypeEnum.GET);
|
||||
return customOperation(null, RequestTypeEnum.GET, id, "$last", RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);
|
||||
}
|
||||
|
||||
@Search(compartmentName="Condition")
|
||||
@Override
|
||||
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;
|
||||
}
|
||||
|
||||
@POST
|
||||
|
@ -185,7 +208,7 @@ public class FhirPatientRestServer extends AbstractResourceRestServer<Patient> i
|
|||
@Override
|
||||
public Response operationLast(final String resource)
|
||||
throws Exception {
|
||||
return customOperation(getParser().parseResource(resource), RequestTypeEnum.POST);
|
||||
return customOperation(resource, RequestTypeEnum.POST, null, "$last", RestOperationTypeEnum.EXTENDED_OPERATION_TYPE);
|
||||
}
|
||||
|
||||
// @ca.uhn.fhir.rest.annotation.Validate
|
||||
|
@ -219,4 +242,40 @@ public class FhirPatientRestServer extends AbstractResourceRestServer<Patient> i
|
|||
return Patient.class;
|
||||
}
|
||||
|
||||
/** THE DEFAULTS */
|
||||
@Override
|
||||
public List<IServerInterceptor> getInterceptors() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ETagSupportEnum getETagSupport() {
|
||||
return ETagSupportEnum.DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultPrettyPrint() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddProfileTagEnum getAddProfileTag() {
|
||||
return AddProfileTagEnum.NEVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUseBrowserFriendlyContentTypes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPagingProvider getPagingProvider() {
|
||||
return new FifoMemoryPagingProvider(10);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BundleInclusionRule getBundleInclusionRule() {
|
||||
return BundleInclusionRule.BASED_ON_INCLUDES;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,15 +4,16 @@ import java.util.List;
|
|||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import ca.uhn.fhir.jaxrs.server.IResourceRestServer;
|
||||
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.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
||||
public interface IFhirPatientRestServer extends IResourceProvider {
|
||||
public interface IFhirPatientRestServer extends IResourceRestServer<Patient> {
|
||||
|
||||
List<Patient> search(StringParam name);
|
||||
|
||||
|
@ -36,4 +37,7 @@ public interface IFhirPatientRestServer extends IResourceProvider {
|
|||
|
||||
Parameters last(StringDt dummyInput);
|
||||
|
||||
|
||||
List<IResource> searchCompartment(IdDt thePatientId);
|
||||
|
||||
}
|
||||
|
|
|
@ -1,27 +1,5 @@
|
|||
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.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
@ -29,8 +7,8 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -82,11 +60,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
private boolean myCache = true;
|
||||
private volatile Conformance myConformance;
|
||||
private String myPublisher = "Not provided";
|
||||
private RestulfulServerConfiguration myRestfulServer;
|
||||
|
||||
public ServerConformanceProvider(RestfulServer theRestfulServer) {
|
||||
myRestfulServer = new RestulfulServerConfiguration(theRestfulServer);
|
||||
}
|
||||
private RestulfulServerConfiguration myServerConfiguration;
|
||||
|
||||
/*
|
||||
* Add a no-arg constructor and seetter so that the
|
||||
|
@ -99,7 +73,11 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
}
|
||||
|
||||
public void setRestfulServer (RestfulServer theRestfulServer) {
|
||||
myRestfulServer = new RestulfulServerConfiguration(theRestfulServer);
|
||||
myServerConfiguration = theRestfulServer.createConfiguration();
|
||||
}
|
||||
|
||||
public ServerConformanceProvider(RestfulServer theRestfulServer) {
|
||||
myServerConfiguration = theRestfulServer.createConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,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);
|
||||
|
||||
|
@ -141,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) {
|
||||
|
@ -155,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>();
|
||||
|
||||
|
@ -219,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);
|
||||
|
@ -230,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());
|
||||
|
||||
|
@ -352,7 +317,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
param.setDocumentation(nextParamDescription);
|
||||
param.getTypeElement().setValue(nextParameter.getParamType().getCode());
|
||||
for (Class<? extends IResource> nextTarget : nextParameter.getDeclaredTypes()) {
|
||||
RuntimeResourceDefinition targetDef = myRestfulServer.getFhirContext().getResourceDefinition(nextTarget);
|
||||
RuntimeResourceDefinition targetDef = myServerConfiguration.getFhirContext().getResourceDefinition(nextTarget);
|
||||
if (targetDef != null) {
|
||||
ResourceTypeEnum code = ResourceTypeEnum.VALUESET_BINDER.fromCodeString(targetDef.getName());
|
||||
if (code != null) {
|
||||
|
|
|
@ -62,10 +62,10 @@ public class InterceptorTest {
|
|||
public void testInterceptorFires() throws Exception {
|
||||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
|
@ -79,8 +79,8 @@ public class InterceptorTest {
|
|||
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||
order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
verifyNoMoreInteractions(myInterceptor1);
|
||||
verifyNoMoreInteractions(myInterceptor2);
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import ca.uhn.fhir.rest.method.IParameter;
|
|||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||
import ca.uhn.fhir.rest.method.SearchParameter;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
public class ResourceMethodTest {
|
||||
|
||||
|
@ -51,7 +52,7 @@ public class ResourceMethodTest {
|
|||
inputParams.add("lastName");
|
||||
inputParams.add("mrn");
|
||||
|
||||
RequestDetails params = RequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams);
|
||||
RequestDetails params = ServletRequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams);
|
||||
boolean actual = rm.incomingServerRequestMatchesMethod(params);
|
||||
assertTrue( actual); // True
|
||||
}
|
||||
|
@ -72,7 +73,7 @@ public class ResourceMethodTest {
|
|||
inputParams.add("mrn");
|
||||
inputParams.add("foo");
|
||||
|
||||
assertEquals(false, rm.incomingServerRequestMatchesMethod(RequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // False
|
||||
assertEquals(false, rm.incomingServerRequestMatchesMethod(ServletRequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // False
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -89,7 +90,7 @@ public class ResourceMethodTest {
|
|||
inputParams.add("firstName");
|
||||
inputParams.add("mrn");
|
||||
|
||||
assertEquals(true, rm.incomingServerRequestMatchesMethod(RequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // True
|
||||
assertEquals(true, rm.incomingServerRequestMatchesMethod(ServletRequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // True
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -106,7 +107,7 @@ public class ResourceMethodTest {
|
|||
inputParams.add("firstName");
|
||||
inputParams.add("lastName");
|
||||
|
||||
assertEquals(false, rm.incomingServerRequestMatchesMethod(RequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // False
|
||||
assertEquals(false, rm.incomingServerRequestMatchesMethod(ServletRequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // False
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -121,7 +122,7 @@ public class ResourceMethodTest {
|
|||
|
||||
Set<String> inputParams = new HashSet<String>();
|
||||
inputParams.add("mrn");
|
||||
assertEquals(true, rm.incomingServerRequestMatchesMethod(RequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // True
|
||||
assertEquals(true, rm.incomingServerRequestMatchesMethod(ServletRequestDetails.withResourceAndParams("Patient", RequestTypeEnum.GET, inputParams))); // True
|
||||
}
|
||||
|
||||
@Test(expected=IllegalStateException.class)
|
||||
|
|
|
@ -19,8 +19,7 @@ package ca.uhn.fhir.rest.server.provider.dstu2;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -59,8 +58,8 @@ 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.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
|
@ -324,7 +323,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl,
|
||||
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) {
|
||||
myBase = theServerBase;
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import java.util.Set;
|
|||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -97,7 +98,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
private RestulfulServerConfiguration myServerConfiguration;
|
||||
|
||||
public ServerConformanceProvider(RestfulServer theRestfulServer) {
|
||||
this.myServerConfiguration = new RestulfulServerConfiguration(theRestfulServer);
|
||||
this.myServerConfiguration = theRestfulServer.createConfiguration();
|
||||
}
|
||||
|
||||
public ServerConformanceProvider(RestulfulServerConfiguration theServerConfiguration) {
|
||||
|
@ -115,7 +116,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
}
|
||||
|
||||
public void setRestfulServer (RestfulServer theRestfulServer) {
|
||||
myServerConfiguration = new RestulfulServerConfiguration(theRestfulServer);
|
||||
myServerConfiguration = theRestfulServer.createConfiguration();
|
||||
}
|
||||
|
||||
private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) {
|
||||
|
@ -204,7 +205,9 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
String resourceName = nextEntry.getKey();
|
||||
RuntimeResourceDefinition def = myServerConfiguration.getFhirContext().getResourceDefinition(resourceName);
|
||||
resource.getTypeElement().setValue(def.getName());
|
||||
resource.getProfile().setReference(new IdDt(def.getResourceProfile(myServerConfiguration.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>();
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ 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.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
public class ResponseHighlightingInterceptorTest {
|
||||
|
@ -122,7 +123,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
RequestDetails reqDetails = new RequestDetails();
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setParameters(new HashMap<String, String[]>());
|
||||
reqDetails.setServer(new RestfulServer());
|
||||
|
@ -157,7 +158,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
RequestDetails reqDetails = new RequestDetails();
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
params.put(Constants.PARAM_PRETTY, new String[] { Constants.PARAM_PRETTY_VALUE_TRUE });
|
||||
|
@ -192,7 +193,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
RequestDetails reqDetails = new RequestDetails();
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
HashMap<String, String[]> params = new HashMap<String, String[]>();
|
||||
params.put(Constants.PARAM_PRETTY, new String[] { Constants.PARAM_PRETTY_VALUE_TRUE });
|
||||
|
@ -226,7 +227,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
RequestDetails reqDetails = new RequestDetails();
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setParameters(new HashMap<String, String[]>());
|
||||
reqDetails.setServer(new RestfulServer());
|
||||
|
@ -264,7 +265,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
RequestDetails reqDetails = new RequestDetails();
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setParameters(new HashMap<String, String[]>());
|
||||
RestfulServer server = new RestfulServer();
|
||||
|
@ -299,7 +300,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
Patient resource = new Patient();
|
||||
resource.addName().addFamily("FAMILY");
|
||||
|
||||
RequestDetails reqDetails = new RequestDetails();
|
||||
ServletRequestDetails reqDetails = new ServletRequestDetails();
|
||||
reqDetails.setRequestType(RequestTypeEnum.GET);
|
||||
reqDetails.setParameters(new HashMap<String, String[]>());
|
||||
RestfulServer server = new RestfulServer();
|
||||
|
|
|
@ -57,6 +57,7 @@ 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.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
|
@ -297,7 +298,7 @@ public class Dstu2Hl7OrgBundleFactory implements IVersionSpecificBundleFactory {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult,
|
||||
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) {
|
||||
myBase = theServerBase;
|
||||
|
|
Loading…
Reference in New Issue