Add configurable default response encoding to server, and serve Binary
resources as FHIR resources instead of blobs if the user has explicitly requested an encoding
This commit is contained in:
parent
2c4b37e5f0
commit
ecd3620e27
|
@ -45,7 +45,6 @@ import ca.uhn.fhir.rest.annotation.TagListParam;
|
|||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||
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.exceptions.BaseServerResponseException;
|
||||
|
@ -188,10 +187,8 @@ abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void>
|
|||
}
|
||||
}
|
||||
|
||||
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest.getServletRequest());
|
||||
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
response.setContentType(responseEncoding.getResourceContentType());
|
||||
response.setContentType(Constants.CT_TEXT);
|
||||
response.setStatus(Constants.STATUS_HTTP_200_OK);
|
||||
response.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ import ca.uhn.fhir.parser.IParser;
|
|||
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.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
/**
|
||||
|
@ -235,7 +235,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
contents = parser.encodeBundleToString(myBundle);
|
||||
contentType = encoding.getBundleContentType();
|
||||
} else if (myResources != null) {
|
||||
Bundle bundle = RestfulServer.createBundleFromResourceList(myContext, "", myResources, "", "", myResources.size(), myBundleType);
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(myContext, "", myResources, "", "", myResources.size(), myBundleType);
|
||||
contents = parser.encodeBundleToString(bundle);
|
||||
contentType = encoding.getBundleContentType();
|
||||
} else if (myContents != null) {
|
||||
|
|
|
@ -49,6 +49,7 @@ import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
|||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
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;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
|
@ -166,13 +167,13 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
response = (MethodOutcome) invokeServerMethod(params);
|
||||
} catch (InternalErrorException e) {
|
||||
ourLog.error("Internal error during method invocation", e);
|
||||
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest.getServletRequest());
|
||||
streamOperationOutcome(e, theServer, encoding, servletResponse, theRequest);
|
||||
EncodingEnum encodingNotNull = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
streamOperationOutcome(e, theServer, encodingNotNull, servletResponse, theRequest);
|
||||
return;
|
||||
} catch (BaseServerResponseException e) {
|
||||
ourLog.info("Exception during method invocation: " + e.getMessage());
|
||||
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest.getServletRequest());
|
||||
streamOperationOutcome(e, theServer, encoding, servletResponse, theRequest);
|
||||
EncodingEnum encodingNotNull = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
streamOperationOutcome(e, theServer, encodingNotNull, servletResponse, theRequest);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -234,11 +235,11 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
theServer.addHeadersToResponse(servletResponse);
|
||||
|
||||
if (outcome != null) {
|
||||
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest.getServletRequest());
|
||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
servletResponse.setContentType(encoding.getResourceContentType());
|
||||
Writer writer = servletResponse.getWriter();
|
||||
IParser parser = encoding.newParser(getContext());
|
||||
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
|
||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theRequest));
|
||||
try {
|
||||
parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
|
||||
} finally {
|
||||
|
@ -268,7 +269,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
* @throws IOException
|
||||
*/
|
||||
protected IResource parseIncomingServerResource(Request theRequest) throws IOException {
|
||||
EncodingEnum encoding = RestfulServer.determineRequestEncoding(theRequest);
|
||||
EncodingEnum encoding = RestfulServerUtils.determineRequestEncoding(theRequest);
|
||||
IParser parser = encoding.newParser(getContext());
|
||||
BufferedReader requestReader = theRequest.getServletRequest().getReader();
|
||||
|
||||
|
@ -302,15 +303,15 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
return null;
|
||||
}
|
||||
|
||||
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncoding, HttpServletResponse theResponse, Request theRequest) throws IOException {
|
||||
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, Request theRequest) throws IOException {
|
||||
theResponse.setStatus(theE.getStatusCode());
|
||||
|
||||
theServer.addHeadersToResponse(theResponse);
|
||||
|
||||
|
||||
if (theE.getOperationOutcome() != null) {
|
||||
theResponse.setContentType(theEncoding.getResourceContentType());
|
||||
IParser parser = theEncoding.newParser(theServer.getFhirContext());
|
||||
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
|
||||
theResponse.setContentType(theEncodingNotNull.getResourceContentType());
|
||||
IParser parser = theEncodingNotNull.newParser(theServer.getFhirContext());
|
||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theRequest));
|
||||
Writer writer = theResponse.getWriter();
|
||||
try {
|
||||
parser.encodeResourceToWriter(theE.getOperationOutcome(), writer);
|
||||
|
|
|
@ -49,6 +49,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
|
|||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||
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.rest.server.exceptions.InvalidRequestException;
|
||||
|
@ -212,13 +213,13 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
public void invokeServer(RestfulServer theServer, Request theRequest) throws BaseServerResponseException, IOException {
|
||||
|
||||
// Pretty print
|
||||
boolean prettyPrint = RestfulServer.prettyPrintResponse(theRequest);
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequest);
|
||||
|
||||
// Narrative mode
|
||||
NarrativeModeEnum narrativeMode = RestfulServer.determineNarrativeMode(theRequest);
|
||||
NarrativeModeEnum narrativeMode = RestfulServerUtils.determineNarrativeMode(theRequest);
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest.getServletRequest());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest());
|
||||
|
||||
// Is this request coming from a browser
|
||||
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
||||
|
@ -238,7 +239,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
}
|
||||
}
|
||||
|
||||
Integer count = RestfulServer.extractCountParameter(theRequest.getServletRequest());
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
|
||||
|
@ -249,11 +250,11 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
|
||||
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) {
|
||||
IResource resource = (IResource) resultObj;
|
||||
RestfulServer.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
|
||||
break;
|
||||
} else {
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
Bundle bundle = RestfulServer.createBundleFromBundleProvider(theServer, response, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, 0, count, null, getResponseBundleType());
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromBundleProvider(theServer, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, 0, count, null, getResponseBundleType());
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
|
@ -263,7 +264,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
}
|
||||
}
|
||||
|
||||
RestfulServer.streamResponseAsBundle(theServer, response, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip);
|
||||
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -285,7 +286,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
|
|||
}
|
||||
}
|
||||
|
||||
RestfulServer.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ 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.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
|
@ -167,7 +168,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||
}
|
||||
}
|
||||
|
||||
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest.getServletRequest());
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theServer, theRequest.getServletRequest());
|
||||
|
||||
HttpServletResponse response = theRequest.getServletResponse();
|
||||
response.setContentType(responseEncoding.getResourceContentType());
|
||||
|
@ -177,7 +178,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
|
|||
theServer.addHeadersToResponse(response);
|
||||
|
||||
IParser parser = responseEncoding.newParser(getContext());
|
||||
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
|
||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theRequest));
|
||||
PrintWriter writer = response.getWriter();
|
||||
try {
|
||||
parser.encodeTagListToWriter(resp, writer);
|
||||
|
|
|
@ -40,7 +40,7 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
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.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -98,7 +98,7 @@ class TransactionParamBinder implements IParameter {
|
|||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||
|
||||
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest.getServletRequest());
|
||||
EncodingEnum encoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequest.getServer(), theRequest.getServletRequest());
|
||||
IParser parser = encoding.newParser(myContext);
|
||||
|
||||
BufferedReader reader;
|
||||
|
|
|
@ -23,30 +23,20 @@ package ca.uhn.fhir.rest.server;
|
|||
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.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.UUID;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -54,28 +44,15 @@ import javax.servlet.http.HttpServletResponse;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.http.client.utils.DateUtils;
|
||||
import org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.ProvidedResourceScanner;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.base.resource.BaseBinary;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.Destroy;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
|
@ -101,10 +78,11 @@ public class RestfulServer extends HttpServlet {
|
|||
* Default setting for {@link #setETagSupport(ETagSupportEnum) ETag Support}: {@link ETagSupportEnum#ENABLED}
|
||||
*/
|
||||
public static final ETagSupportEnum DEFAULT_ETAG_SUPPORT = ETagSupportEnum.ENABLED;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
|
||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private AddProfileTagEnum myAddProfileTag;
|
||||
private EncodingEnum myDefaultResponseEncoding = EncodingEnum.XML;
|
||||
private ETagSupportEnum myETagSupport = DEFAULT_ETAG_SUPPORT;
|
||||
private FhirContext myFhirContext;
|
||||
private String myImplementationDescription;
|
||||
|
@ -120,6 +98,7 @@ public class RestfulServer extends HttpServlet {
|
|||
private String myServerName = "HAPI FHIR Server";
|
||||
/** This is configurable but by default we just use HAPI version */
|
||||
private String myServerVersion = VersionUtil.getVersion();
|
||||
|
||||
private boolean myStarted;
|
||||
|
||||
private boolean myUseBrowserFriendlyContentTypes;
|
||||
|
@ -138,8 +117,7 @@ public class RestfulServer extends HttpServlet {
|
|||
/**
|
||||
* This method is called prior to sending a response to incoming requests. It is used to add custom headers.
|
||||
* <p>
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid
|
||||
* inadvertantly disabling functionality.
|
||||
* Use caution if overriding this method: it is recommended to call <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling functionality.
|
||||
* </p>
|
||||
*/
|
||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||
|
@ -315,6 +293,14 @@ public class RestfulServer extends HttpServlet {
|
|||
return myAddProfileTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*/
|
||||
public EncodingEnum getDefaultResponseEncoding() {
|
||||
return myDefaultResponseEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the server support for ETags (will not be <code>null</code>). Default is {@link #DEFAULT_ETAG_SUPPORT}
|
||||
*/
|
||||
|
@ -323,8 +309,8 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
|
||||
* providers should generally use this context if one is needed, as opposed to creating their own.
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain providers should generally use this context if one is needed, as opposed to
|
||||
* creating their own.
|
||||
*/
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
|
@ -354,6 +340,21 @@ public class RestfulServer extends HttpServlet {
|
|||
return myPlainProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path implementation
|
||||
*
|
||||
* @param requestFullPath
|
||||
* the full request path
|
||||
* @param servletContextPath
|
||||
* the servelet context path
|
||||
* @param servletPath
|
||||
* the servelet path
|
||||
* @return created resource path
|
||||
*/
|
||||
protected String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) {
|
||||
return requestFullPath.substring(escapedLength(servletContextPath) + escapedLength(servletPath));
|
||||
}
|
||||
|
||||
public Collection<ResourceBinding> getResourceBindings() {
|
||||
return myResourceNameToProvider.values();
|
||||
}
|
||||
|
@ -366,8 +367,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public IServerAddressStrategy getServerAddressStrategy() {
|
||||
return myServerAddressStrategy;
|
||||
|
@ -384,11 +384,9 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement if one has been explicitly defined.
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement if one has been explicitly defined.
|
||||
* <p>
|
||||
* By default, the ServerConformanceProvider for the declared version of FHIR is used, but this can be changed, or
|
||||
* set to <code>null</code> to use the appropriate one for the given FHIR version.
|
||||
* By default, the ServerConformanceProvider for the declared version of FHIR is used, but this can be changed, or set to <code>null</code> to use the appropriate one for the given FHIR version.
|
||||
* </p>
|
||||
*/
|
||||
public Object getServerConformanceProvider() {
|
||||
|
@ -396,8 +394,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Gets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*
|
||||
* @see RestfulServer#setServerName(String)
|
||||
*/
|
||||
|
@ -410,8 +407,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||
* only, but can be helpful to set with something appropriate.
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public String getServerVersion() {
|
||||
return myServerVersion;
|
||||
|
@ -430,27 +426,28 @@ public class RestfulServer extends HttpServlet {
|
|||
return;
|
||||
}
|
||||
|
||||
Integer count = extractCountParameter(theRequest.getServletRequest());
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest.getServletRequest());
|
||||
if (count == null) {
|
||||
count = getPagingProvider().getDefaultPageSize();
|
||||
} else if (count > getPagingProvider().getMaximumPageSize()) {
|
||||
count = getPagingProvider().getMaximumPageSize();
|
||||
}
|
||||
|
||||
Integer offsetI = tryToExtractNamedParameter(theRequest.getServletRequest(), Constants.PARAM_PAGINGOFFSET);
|
||||
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest.getServletRequest(), Constants.PARAM_PAGINGOFFSET);
|
||||
if (offsetI == null || offsetI < 0) {
|
||||
offsetI = 0;
|
||||
}
|
||||
|
||||
int start = Math.min(offsetI, resultList.size() - 1);
|
||||
|
||||
EncodingEnum responseEncoding = determineResponseEncoding(theRequest.getServletRequest());
|
||||
boolean prettyPrint = prettyPrintResponse(theRequest);
|
||||
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest.getServletRequest());
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theRequest);
|
||||
boolean requestIsBrowser = requestIsBrowser(theRequest.getServletRequest());
|
||||
NarrativeModeEnum narrativeMode = determineNarrativeMode(theRequest);
|
||||
NarrativeModeEnum narrativeMode = RestfulServerUtils.determineNarrativeMode(theRequest);
|
||||
boolean respondGzip = theRequest.isRespondGzip();
|
||||
|
||||
Bundle bundle = createBundleFromBundleProvider(this, theResponse, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode, start, count, thePagingAction, null);
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint,
|
||||
start, count, thePagingAction, null);
|
||||
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = getInterceptors().get(i);
|
||||
|
@ -461,7 +458,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
streamResponseAsBundle(this, theResponse, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip);
|
||||
RestfulServerUtils.streamResponseAsBundle(this, theResponse, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
|
||||
|
||||
}
|
||||
|
||||
|
@ -509,7 +506,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
fhirServerBase = getServerBaseForRequest(theRequest);
|
||||
|
||||
|
||||
String completeUrl = StringUtils.isNotBlank(theRequest.getQueryString()) ? requestUrl + "?" + theRequest.getQueryString() : requestUrl.toString();
|
||||
|
||||
Map<String, String[]> params = new HashMap<String, String[]>(theRequest.getParameterMap());
|
||||
|
@ -558,7 +555,7 @@ public class RestfulServer extends HttpServlet {
|
|||
operation = Constants.PARAM_HISTORY;
|
||||
}
|
||||
} else if (nextString.startsWith("_")) {
|
||||
//FIXME: this would be untrue for _meta/_delete
|
||||
// FIXME: this would be untrue for _meta/_delete
|
||||
if (operation != null) {
|
||||
throw new InvalidRequestException("URL Path contains two operations (part beginning with _): " + requestPath);
|
||||
}
|
||||
|
@ -684,8 +681,7 @@ public class RestfulServer extends HttpServlet {
|
|||
} catch (Throwable e) {
|
||||
|
||||
/*
|
||||
* We have caught an exception while handling an incoming server request. Start by notifying the
|
||||
* interceptors..
|
||||
* We have caught an exception while handling an incoming server request. Start by notifying the interceptors..
|
||||
*/
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = getInterceptors().get(i);
|
||||
|
@ -740,7 +736,8 @@ public class RestfulServer extends HttpServlet {
|
|||
ourLog.error("Unknown error during processing", e);
|
||||
}
|
||||
|
||||
streamResponseAsResource(this, theResponse, oo, determineResponseEncoding(theRequest), true, requestIsBrowser, NarrativeModeEnum.NORMAL, statusCode, false, fhirServerBase);
|
||||
RestfulServerUtils.streamResponseAsResource(this, theResponse, oo, RestfulServerUtils.determineResponseEncodingNoDefault(theRequest), true, requestIsBrowser, NarrativeModeEnum.NORMAL,
|
||||
statusCode, false, fhirServerBase);
|
||||
|
||||
theResponse.setStatus(statusCode);
|
||||
addHeadersToResponse(theResponse);
|
||||
|
@ -753,9 +750,8 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
|
||||
* but subclasses may put initialization code in {@link #initialize()}, which is called immediately before beginning
|
||||
* initialization of the restful server's internal init.
|
||||
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations, but subclasses may put initialization code in {@link #initialize()}, which is
|
||||
* called immediately before beginning initialization of the restful server's internal init.
|
||||
*/
|
||||
@Override
|
||||
public final void init() throws ServletException {
|
||||
|
@ -778,7 +774,8 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
String resourceName = myFhirContext.getResourceDefinition(resourceType).getName();
|
||||
if (typeToProvider.containsKey(resourceName)) {
|
||||
throw new ServletException("Multiple resource providers return resource type[" + resourceName + "]: First[" + typeToProvider.get(resourceName).getClass().getCanonicalName() + "] and Second[" + nextProvider.getClass().getCanonicalName() + "]");
|
||||
throw new ServletException("Multiple resource providers return resource type[" + resourceName + "]: First[" + typeToProvider.get(resourceName).getClass().getCanonicalName()
|
||||
+ "] and Second[" + nextProvider.getClass().getCanonicalName() + "]");
|
||||
}
|
||||
typeToProvider.put(resourceName, nextProvider);
|
||||
providedResourceScanner.scanForProvidedResources(nextProvider);
|
||||
|
@ -816,8 +813,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the
|
||||
* server being used.
|
||||
* This method may be overridden by subclasses to do perform initialization that needs to be performed prior to the server being used.
|
||||
*/
|
||||
protected void initialize() throws ServletException {
|
||||
// nothing by default
|
||||
|
@ -864,9 +860,8 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the profile tagging behaviour for the server. When set to a value other than {@link AddProfileTagEnum#NEVER}
|
||||
* (which is the default), the server will automatically add a profile tag based on the class of the resource(s)
|
||||
* being returned.
|
||||
* Sets the profile tagging behaviour for the server. When set to a value other than {@link AddProfileTagEnum#NEVER} (which is the default), the server will automatically add a profile tag based
|
||||
* on the class of the resource(s) being returned.
|
||||
*
|
||||
* @param theAddProfileTag
|
||||
* The behaviour enum (must not be null)
|
||||
|
@ -877,8 +872,16 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is
|
||||
* {@link #DEFAULT_ETAG_SUPPORT}
|
||||
* Sets 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}.
|
||||
*/
|
||||
public void setDefaultResponseEncoding(EncodingEnum theDefaultResponseEncoding) {
|
||||
Validate.notNull(theDefaultResponseEncoding, "theDefaultResponseEncoding can not be null");
|
||||
myDefaultResponseEncoding = theDefaultResponseEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is {@link #DEFAULT_ETAG_SUPPORT}
|
||||
*
|
||||
* @param theETagSupport
|
||||
* The ETag support mode
|
||||
|
@ -974,8 +977,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this
|
||||
* server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
* Provide a server address strategy, which is used to determine what base URL to provide clients to refer to this server. Defaults to an instance of {@link IncomingRequestAddressStrategy}
|
||||
*/
|
||||
public void setServerAddressStrategy(IServerAddressStrategy theServerAddressStrategy) {
|
||||
Validate.notNull(theServerAddressStrategy, "Server address strategy can not be null");
|
||||
|
@ -983,17 +985,15 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance
|
||||
* (metadata) statement.
|
||||
* Returns the server conformance provider, which is the provider that is used to generate the server's conformance (metadata) statement.
|
||||
* <p>
|
||||
* By default, the ServerConformanceProvider implementation for the declared version of FHIR is used, but this can
|
||||
* be changed, or set to <code>null</code> if you do not wish to export a conformance statement.
|
||||
* By default, the ServerConformanceProvider implementation for the declared version of FHIR is used, but this can be changed, or set to <code>null</code> if you do not wish to export a
|
||||
* conformance statement.
|
||||
* </p>
|
||||
* Note that this method can only be called before the server is initialized.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an
|
||||
* {@link IllegalStateException} if called after that.
|
||||
* Note that this method can only be called prior to {@link #init() initialization} and will throw an {@link IllegalStateException} if called after that.
|
||||
*/
|
||||
public void setServerConformanceProvider(Object theServerConformanceProvider) {
|
||||
if (myStarted) {
|
||||
|
@ -1003,24 +1003,22 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only,
|
||||
* but can be helpful to set with something appropriate.
|
||||
* Sets the server's name, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerName(String theServerName) {
|
||||
myServerName = theServerName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational
|
||||
* only, but can be helpful to set with something appropriate.
|
||||
* Gets the server's version, as exported in conformance profiles exported by the server. This is informational only, but can be helpful to set with something appropriate.
|
||||
*/
|
||||
public void setServerVersion(String theServerVersion) {
|
||||
myServerVersion = theServerVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of
|
||||
* standard FHIR ones) when it detects that the request is coming from a browser instead of a FHIR
|
||||
* If set to <code>true</code> (default is false), the server will use browser friendly content-types (instead of standard FHIR ones) when it detects that the request is coming from a browser
|
||||
* instead of a FHIR
|
||||
*/
|
||||
public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) {
|
||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||
|
@ -1039,491 +1037,6 @@ public class RestfulServer extends HttpServlet {
|
|||
theResponse.getWriter().write(theException.getMessage());
|
||||
}
|
||||
|
||||
private static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
|
||||
|
||||
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
|
||||
if (tl == null) {
|
||||
tl = new TagList();
|
||||
ResourceMetadataKeyEnum.TAG_LIST.put(theResource, tl);
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
|
||||
String profile = nextDef.getResourceProfile(theServerBase);
|
||||
if (isNotBlank(profile)) {
|
||||
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
|
||||
}
|
||||
}
|
||||
|
||||
public static Bundle createBundleFromBundleProvider(RestfulServer theServer, HttpServletResponse theHttpResponse, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
|
||||
NarrativeModeEnum theNarrativeMode, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType) {
|
||||
theHttpResponse.setStatus(200);
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType());
|
||||
} else if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
||||
theHttpResponse.setContentType(Constants.CT_HTML);
|
||||
} else {
|
||||
theHttpResponse.setContentType(theResponseEncoding.getBundleContentType());
|
||||
}
|
||||
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
int numToReturn;
|
||||
String searchId = null;
|
||||
List<IResource> resourceList;
|
||||
if (theServer.getPagingProvider() == null) {
|
||||
numToReturn = theResult.size();
|
||||
resourceList = theResult.getResources(0, numToReturn);
|
||||
validateResourceListNotNull(resourceList);
|
||||
|
||||
} else {
|
||||
IPagingProvider pagingProvider = theServer.getPagingProvider();
|
||||
if (theLimit == null) {
|
||||
numToReturn = pagingProvider.getDefaultPageSize();
|
||||
} else {
|
||||
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
|
||||
}
|
||||
|
||||
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
|
||||
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
|
||||
validateResourceListNotNull(resourceList);
|
||||
|
||||
if (theSearchId != null) {
|
||||
searchId = theSearchId;
|
||||
} else {
|
||||
if (theResult.size() > numToReturn) {
|
||||
searchId = pagingProvider.storeResultList(theResult);
|
||||
Validate.notNull(searchId, "Paging provider returned null searchId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : resourceList) {
|
||||
if (next.getId() == null || next.getId().isEmpty()) {
|
||||
if (!(next instanceof BaseOperationOutcome)) {
|
||||
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
for (IResource nextRes : resourceList) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bundle bundle = createBundleFromResourceList(theServer.getFhirContext(), theServer.getServerName(), resourceList, theServerBase, theCompleteUrl, theResult.size(), theBundleType);
|
||||
|
||||
bundle.setPublished(theResult.getPublished());
|
||||
|
||||
if (theServer.getPagingProvider() != null) {
|
||||
int limit;
|
||||
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
|
||||
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
|
||||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
bundle.getLinkNext().setValue(createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
bundle.getLinkPrevious().setValue(createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.getAuthorName().setValue(theAuthor);
|
||||
bundle.getBundleId().setValue(UUID.randomUUID().toString());
|
||||
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
|
||||
bundle.getLinkBase().setValue(theServerBase);
|
||||
bundle.getLinkSelf().setValue(theCompleteUrl);
|
||||
bundle.getType().setValueAsEnum(theBundleType);
|
||||
|
||||
List<IResource> includedResources = new ArrayList<IResource>();
|
||||
Set<IdDt> addedResourceIds = new HashSet<IdDt>();
|
||||
|
||||
for (IResource next : theResult) {
|
||||
if (next.getId().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getId().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = theContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (BaseResourceReferenceDt nextRef : references) {
|
||||
IResource nextRes = nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getId().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getId().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt id = nextRes.getId();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = theContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<BaseResourceReferenceDt>();
|
||||
for (IResource iResource : addedResourcesThisPass) {
|
||||
List<BaseResourceReferenceDt> newReferences = theContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
bundle.addResource(next, theContext, theServerBase);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IResource next : includedResources) {
|
||||
BundleEntry entry = bundle.addResource(next, theContext, theServerBase);
|
||||
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
|
||||
if (entry.getSearchMode().isEmpty()) {
|
||||
entry.getSearchMode().setValueAsEnum(BundleEntrySearchModeEnum.INCLUDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bundle.getTotalResults().setValue(theTotalResults);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
b.append('?');
|
||||
b.append(Constants.PARAM_PAGINGACTION);
|
||||
b.append('=');
|
||||
try {
|
||||
b.append(URLEncoder.encode(theSearchId, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("UTF-8 not supported", e);// should not happen
|
||||
}
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PAGINGOFFSET);
|
||||
b.append('=');
|
||||
b.append(theOffset);
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_COUNT);
|
||||
b.append('=');
|
||||
b.append(theCount);
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_FORMAT);
|
||||
b.append('=');
|
||||
b.append(theResponseEncoding.getRequestContentType());
|
||||
if (thePrettyPrint) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PRETTY);
|
||||
b.append('=');
|
||||
b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static NarrativeModeEnum determineNarrativeMode(RequestDetails theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
||||
NarrativeModeEnum narrativeMode = null;
|
||||
if (narrative != null && narrative.length > 0) {
|
||||
narrativeMode = NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]);
|
||||
}
|
||||
if (narrativeMode == null) {
|
||||
narrativeMode = NarrativeModeEnum.NORMAL;
|
||||
}
|
||||
return narrativeMode;
|
||||
}
|
||||
|
||||
public static EncodingEnum determineRequestEncoding(Request theReq) {
|
||||
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
||||
int scIdx = nextPart.indexOf(';');
|
||||
if (scIdx == 0) {
|
||||
continue;
|
||||
}
|
||||
if (scIdx != -1) {
|
||||
nextPart = nextPart.substring(0, scIdx);
|
||||
}
|
||||
nextPart = nextPart.trim();
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EncodingEnum.XML;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 determineResponseEncoding(HttpServletRequest theReq) {
|
||||
String[] format = theReq.getParameterValues(Constants.PARAM_FORMAT);
|
||||
if (format != null) {
|
||||
for (String nextFormat : format) {
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Enumeration<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
||||
int scIdx = nextPart.indexOf(';');
|
||||
if (scIdx == 0) {
|
||||
continue;
|
||||
}
|
||||
if (scIdx != -1) {
|
||||
nextPart = nextPart.substring(0, scIdx);
|
||||
}
|
||||
nextPart = nextPart.trim();
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EncodingEnum.XML;
|
||||
}
|
||||
|
||||
public static Integer extractCountParameter(HttpServletRequest theRequest) {
|
||||
String name = Constants.PARAM_COUNT;
|
||||
return tryToExtractNamedParameter(theRequest, name);
|
||||
}
|
||||
|
||||
public static IParser getNewParser(FhirContext theContext, EncodingEnum theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
|
||||
IParser parser;
|
||||
switch (theResponseEncoding) {
|
||||
case JSON:
|
||||
parser = theContext.newJsonParser();
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
parser = theContext.newXmlParser();
|
||||
break;
|
||||
}
|
||||
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
||||
}
|
||||
|
||||
private 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 boolean prettyPrintResponse(Request theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
|
||||
boolean prettyPrint;
|
||||
if (pretty != null && pretty.length > 0) {
|
||||
if (Constants.PARAM_PRETTY_VALUE_TRUE.equals(pretty[0])) {
|
||||
prettyPrint = true;
|
||||
} else {
|
||||
prettyPrint = false;
|
||||
}
|
||||
} else {
|
||||
prettyPrint = false;
|
||||
Enumeration<String> acceptValues = theRequest.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
if (nextAcceptHeaderValue.contains("pretty=true")) {
|
||||
prettyPrint = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return prettyPrint;
|
||||
}
|
||||
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, Bundle bundle, EncodingEnum theResponseEncoding, String theServerBase, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip) throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
|
||||
Writer writer = getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
||||
for (IResource next : bundle.toListOfResources()) {
|
||||
writer.append(next.getText().getDiv().getValueAsString());
|
||||
writer.append("<hr/>");
|
||||
}
|
||||
} else {
|
||||
RestfulServer.getNewParser(theServer.getFhirContext(), theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeBundleToWriter(bundle, writer);
|
||||
}
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase)
|
||||
throws IOException {
|
||||
int stausCode = 200;
|
||||
streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
|
||||
}
|
||||
|
||||
private static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip,
|
||||
String theServerBase) throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
|
||||
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
||||
String resName = theServer.getFhirContext().getResourceDefinition(theResource).getName();
|
||||
IdDt fullId = theResource.getId().withServerBase(theServerBase, resName);
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId.getValue());
|
||||
}
|
||||
|
||||
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||
if (theResource.getId().hasVersionIdPart()) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_ETAG, "W/\"" + theResource.getId().getVersionIdPart() + '"');
|
||||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(theResource);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
addProfileToBundleEntry(theServer.getFhirContext(), theResource, theServerBase);
|
||||
}
|
||||
}
|
||||
|
||||
if (theResource instanceof BaseBinary) {
|
||||
BaseBinary bin = (BaseBinary) theResource;
|
||||
if (isNotBlank(bin.getContentType())) {
|
||||
theHttpResponse.setContentType(bin.getContentType());
|
||||
} else {
|
||||
theHttpResponse.setContentType(Constants.CT_OCTET_STREAM);
|
||||
}
|
||||
if (bin.getContent() == null || bin.getContent().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_DISPOSITION, "Attachment;");
|
||||
|
||||
theHttpResponse.setContentLength(bin.getContent().length);
|
||||
ServletOutputStream oos = theHttpResponse.getOutputStream();
|
||||
oos.write(bin.getContent());
|
||||
oos.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType());
|
||||
} else if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
||||
theHttpResponse.setContentType(Constants.CT_HTML);
|
||||
} else {
|
||||
theHttpResponse.setContentType(theResponseEncoding.getResourceContentType());
|
||||
}
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
InstantDt lastUpdated = ResourceMetadataKeyEnum.UPDATED.get(theResource);
|
||||
if (lastUpdated != null && lastUpdated.isEmpty() == false) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated.getValue()));
|
||||
}
|
||||
|
||||
TagList list = (TagList) 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Writer writer = getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
||||
writer.append(theResource.getText().getDiv().getValueAsString());
|
||||
} else {
|
||||
RestfulServer.getNewParser(theServer.getFhirContext(), theResponseEncoding, thePrettyPrint, theNarrativeMode).encodeResourceToWriter(theResource, writer);
|
||||
}
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
String countString = theRequest.getParameter(name);
|
||||
Integer count = null;
|
||||
if (isNotBlank(countString)) {
|
||||
try {
|
||||
count = Integer.parseInt(countString);
|
||||
} catch (NumberFormatException e) {
|
||||
ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static void validateResourceListNotNull(List<IResource> theResourceList) {
|
||||
if (theResourceList == null) {
|
||||
throw new InternalErrorException("IBundleProvider returned a null list of resources - This is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
public enum NarrativeModeEnum {
|
||||
NORMAL, ONLY, SUPPRESS;
|
||||
|
||||
|
@ -1531,20 +1044,4 @@ public class RestfulServer extends HttpServlet {
|
|||
return valueOf(NarrativeModeEnum.class, theCode.toUpperCase());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path
|
||||
* implementation
|
||||
*
|
||||
* @param requestFullPath
|
||||
* the full request path
|
||||
* @param servletContextPath
|
||||
* the servelet context path
|
||||
* @param servletPath
|
||||
* the servelet path
|
||||
* @return created resource path
|
||||
*/
|
||||
protected String getRequestPath(String requestFullPath, String servletContextPath, String servletPath) {
|
||||
return requestFullPath.substring(escapedLength(servletContextPath) + escapedLength(servletPath));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,551 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
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;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
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.commons.lang3.Validate;
|
||||
import org.apache.http.client.utils.DateUtils;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
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.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.base.resource.BaseBinary;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.method.Request;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class RestfulServerUtils {
|
||||
|
||||
static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
String countString = theRequest.getParameter(name);
|
||||
Integer count = null;
|
||||
if (isNotBlank(countString)) {
|
||||
try {
|
||||
count = Integer.parseInt(countString);
|
||||
} catch (NumberFormatException e) {
|
||||
RestfulServer.ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip,
|
||||
String theServerBase) throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
|
||||
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
||||
String resName = theServer.getFhirContext().getResourceDefinition(theResource).getName();
|
||||
IdDt fullId = theResource.getId().withServerBase(theServerBase, resName);
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId.getValue());
|
||||
}
|
||||
|
||||
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||
if (theResource.getId().hasVersionIdPart()) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_ETAG, "W/\"" + theResource.getId().getVersionIdPart() + '"');
|
||||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(theResource);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
addProfileToBundleEntry(theServer.getFhirContext(), theResource, theServerBase);
|
||||
}
|
||||
}
|
||||
|
||||
if (theResource instanceof BaseBinary && theResponseEncoding == null) {
|
||||
BaseBinary bin = (BaseBinary) theResource;
|
||||
if (isNotBlank(bin.getContentType())) {
|
||||
theHttpResponse.setContentType(bin.getContentType());
|
||||
} else {
|
||||
theHttpResponse.setContentType(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;");
|
||||
|
||||
theHttpResponse.setContentLength(bin.getContent().length);
|
||||
ServletOutputStream oos = theHttpResponse.getOutputStream();
|
||||
oos.write(bin.getContent());
|
||||
oos.close();
|
||||
return;
|
||||
}
|
||||
|
||||
EncodingEnum responseEncoding = theResponseEncoding != null ? theResponseEncoding : theServer.getDefaultResponseEncoding();
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
||||
} else if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
theHttpResponse.setContentType(Constants.CT_HTML);
|
||||
} else {
|
||||
theHttpResponse.setContentType(responseEncoding.getResourceContentType());
|
||||
}
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
InstantDt lastUpdated = ResourceMetadataKeyEnum.UPDATED.get(theResource);
|
||||
if (lastUpdated != null && lastUpdated.isEmpty() == false) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated.getValue()));
|
||||
}
|
||||
|
||||
TagList list = (TagList) 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Writer writer = getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
writer.append(theResource.getText().getDiv().getValueAsString());
|
||||
} else {
|
||||
getNewParser(theServer.getFhirContext(), responseEncoding, thePrettyPrint, theNarrativeMode).encodeResourceToWriter(theResource, writer);
|
||||
}
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean prettyPrintResponse(Request theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
|
||||
boolean prettyPrint;
|
||||
if (pretty != null && pretty.length > 0) {
|
||||
if (Constants.PARAM_PRETTY_VALUE_TRUE.equals(pretty[0])) {
|
||||
prettyPrint = true;
|
||||
} else {
|
||||
prettyPrint = false;
|
||||
}
|
||||
} else {
|
||||
prettyPrint = false;
|
||||
Enumeration<String> acceptValues = theRequest.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
if (nextAcceptHeaderValue.contains("pretty=true")) {
|
||||
prettyPrint = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return prettyPrint;
|
||||
}
|
||||
|
||||
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 EncodingEnum determineRequestEncoding(Request theReq) {
|
||||
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
||||
int scIdx = nextPart.indexOf(';');
|
||||
if (scIdx == 0) {
|
||||
continue;
|
||||
}
|
||||
if (scIdx != -1) {
|
||||
nextPart = nextPart.substring(0, scIdx);
|
||||
}
|
||||
nextPart = nextPart.trim();
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return EncodingEnum.XML;
|
||||
}
|
||||
|
||||
public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theServerBase);
|
||||
b.append('?');
|
||||
b.append(Constants.PARAM_PAGINGACTION);
|
||||
b.append('=');
|
||||
try {
|
||||
b.append(URLEncoder.encode(theSearchId, "UTF-8"));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new Error("UTF-8 not supported", e);// should not happen
|
||||
}
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PAGINGOFFSET);
|
||||
b.append('=');
|
||||
b.append(theOffset);
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_COUNT);
|
||||
b.append('=');
|
||||
b.append(theCount);
|
||||
if (theResponseEncoding != null) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_FORMAT);
|
||||
b.append('=');
|
||||
b.append(theResponseEncoding.getRequestContentType());
|
||||
}
|
||||
if (thePrettyPrint) {
|
||||
b.append('&');
|
||||
b.append(Constants.PARAM_PRETTY);
|
||||
b.append('=');
|
||||
b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
|
||||
}
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
private static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
|
||||
|
||||
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
|
||||
if (tl == null) {
|
||||
tl = new TagList();
|
||||
ResourceMetadataKeyEnum.TAG_LIST.put(theResource, tl);
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
|
||||
String profile = nextDef.getResourceProfile(theServerBase);
|
||||
if (isNotBlank(profile)) {
|
||||
tl.add(new Tag(Tag.HL7_ORG_PROFILE_TAG, profile, null));
|
||||
}
|
||||
}
|
||||
|
||||
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.getAuthorName().setValue(theAuthor);
|
||||
bundle.getBundleId().setValue(UUID.randomUUID().toString());
|
||||
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
|
||||
bundle.getLinkBase().setValue(theServerBase);
|
||||
bundle.getLinkSelf().setValue(theCompleteUrl);
|
||||
bundle.getType().setValueAsEnum(theBundleType);
|
||||
|
||||
List<IResource> includedResources = new ArrayList<IResource>();
|
||||
Set<IdDt> addedResourceIds = new HashSet<IdDt>();
|
||||
|
||||
for (IResource next : theResult) {
|
||||
if (next.getId().isEmpty() == false) {
|
||||
addedResourceIds.add(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : theResult) {
|
||||
|
||||
Set<String> containedIds = new HashSet<String>();
|
||||
for (IResource nextContained : next.getContained().getContainedResources()) {
|
||||
if (nextContained.getId().isEmpty() == false) {
|
||||
containedIds.add(nextContained.getId().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (theContext.getNarrativeGenerator() != null) {
|
||||
String title = theContext.getNarrativeGenerator().generateTitle(next);
|
||||
RestfulServer.ourLog.trace("Narrative generator created title: {}", title);
|
||||
if (StringUtils.isNotBlank(title)) {
|
||||
ResourceMetadataKeyEnum.TITLE.put(next, title);
|
||||
}
|
||||
} else {
|
||||
RestfulServer.ourLog.trace("No narrative generator specified");
|
||||
}
|
||||
|
||||
List<BaseResourceReferenceDt> references = theContext.newTerser().getAllPopulatedChildElementsOfType(next, BaseResourceReferenceDt.class);
|
||||
do {
|
||||
List<IResource> addedResourcesThisPass = new ArrayList<IResource>();
|
||||
|
||||
for (BaseResourceReferenceDt nextRef : references) {
|
||||
IResource nextRes = nextRef.getResource();
|
||||
if (nextRes != null) {
|
||||
if (nextRes.getId().hasIdPart()) {
|
||||
if (containedIds.contains(nextRes.getId().getValue())) {
|
||||
// Don't add contained IDs as top level resources
|
||||
continue;
|
||||
}
|
||||
|
||||
IdDt id = nextRes.getId();
|
||||
if (id.hasResourceType() == false) {
|
||||
String resName = theContext.getResourceDefinition(nextRes).getName();
|
||||
id = id.withResourceType(resName);
|
||||
}
|
||||
|
||||
if (!addedResourceIds.contains(id)) {
|
||||
addedResourceIds.add(id);
|
||||
addedResourcesThisPass.add(nextRes);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Linked resources may themselves have linked resources
|
||||
references = new ArrayList<BaseResourceReferenceDt>();
|
||||
for (IResource iResource : addedResourcesThisPass) {
|
||||
List<BaseResourceReferenceDt> newReferences = theContext.newTerser().getAllPopulatedChildElementsOfType(iResource, BaseResourceReferenceDt.class);
|
||||
references.addAll(newReferences);
|
||||
}
|
||||
|
||||
includedResources.addAll(addedResourcesThisPass);
|
||||
|
||||
} while (references.isEmpty() == false);
|
||||
|
||||
bundle.addResource(next, theContext, theServerBase);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Actually add the resources to the bundle
|
||||
*/
|
||||
for (IResource next : includedResources) {
|
||||
BundleEntry entry = bundle.addResource(next, theContext, theServerBase);
|
||||
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
|
||||
if (entry.getSearchMode().isEmpty()) {
|
||||
entry.getSearchMode().setValueAsEnum(BundleEntrySearchModeEnum.INCLUDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bundle.getTotalResults().setValue(theTotalResults);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
public static RestfulServer.NarrativeModeEnum determineNarrativeMode(RequestDetails theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
||||
RestfulServer.NarrativeModeEnum narrativeMode = null;
|
||||
if (narrative != null && narrative.length > 0) {
|
||||
narrativeMode = RestfulServer.NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]);
|
||||
}
|
||||
if (narrativeMode == null) {
|
||||
narrativeMode = RestfulServer.NarrativeModeEnum.NORMAL;
|
||||
}
|
||||
return narrativeMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
if (retVal == null) {
|
||||
retVal =theServer.getDefaultResponseEncoding();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static IParser getNewParser(FhirContext theContext, EncodingEnum theResponseEncoding, boolean thePrettyPrint, RestfulServer.NarrativeModeEnum theNarrativeMode) {
|
||||
IParser parser;
|
||||
switch (theResponseEncoding) {
|
||||
case JSON:
|
||||
parser = theContext.newJsonParser();
|
||||
break;
|
||||
case XML:
|
||||
default:
|
||||
parser = theContext.newXmlParser();
|
||||
break;
|
||||
}
|
||||
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == RestfulServer.NarrativeModeEnum.SUPPRESS);
|
||||
}
|
||||
|
||||
public static EncodingEnum determineResponseEncodingNoDefault(HttpServletRequest theReq) {
|
||||
String[] format = theReq.getParameterValues(Constants.PARAM_FORMAT);
|
||||
if (format != null) {
|
||||
for (String nextFormat : format) {
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Enumeration<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
String nextAcceptHeaderValue = acceptValues.nextElement();
|
||||
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
|
||||
for (String nextPart : nextAcceptHeaderValue.split(",")) {
|
||||
int scIdx = nextPart.indexOf(';');
|
||||
if (scIdx == 0) {
|
||||
continue;
|
||||
}
|
||||
if (scIdx != -1) {
|
||||
nextPart = nextPart.substring(0, scIdx);
|
||||
}
|
||||
nextPart = nextPart.trim();
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
|
||||
if (retVal != null) {
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Integer extractCountParameter(HttpServletRequest theRequest) {
|
||||
String name = Constants.PARAM_COUNT;
|
||||
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, name);
|
||||
}
|
||||
|
||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, Bundle bundle, EncodingEnum theResponseEncoding, String theServerBase, boolean thePrettyPrint, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, boolean theRequestIsBrowser) throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
|
||||
EncodingEnum responseEncoding = theResponseEncoding!= null? theResponseEncoding : theServer.getDefaultResponseEncoding();
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
||||
} else if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
theHttpResponse.setContentType(Constants.CT_HTML);
|
||||
} else {
|
||||
theHttpResponse.setContentType(responseEncoding.getBundleContentType());
|
||||
}
|
||||
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
Writer writer = RestfulServerUtils.getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
for (IResource next : bundle.toListOfResources()) {
|
||||
writer.append(next.getText().getDiv().getValueAsString());
|
||||
writer.append("<hr/>");
|
||||
}
|
||||
} else {
|
||||
RestfulServerUtils.getNewParser(theServer.getFhirContext(), responseEncoding, thePrettyPrint, theNarrativeMode).encodeBundleToWriter(bundle, writer);
|
||||
}
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase)
|
||||
throws IOException {
|
||||
int stausCode = 200;
|
||||
RestfulServerUtils.streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
|
||||
}
|
||||
|
||||
private static void validateResourceListNotNull(List<IResource> theResourceList) {
|
||||
if (theResourceList == null) {
|
||||
throw new InternalErrorException("IBundleProvider returned a null list of resources - This is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
public static Bundle createBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType) {
|
||||
|
||||
int numToReturn;
|
||||
String searchId = null;
|
||||
List<IResource> resourceList;
|
||||
if (theServer.getPagingProvider() == null) {
|
||||
numToReturn = theResult.size();
|
||||
resourceList = theResult.getResources(0, numToReturn);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
} else {
|
||||
IPagingProvider pagingProvider = theServer.getPagingProvider();
|
||||
if (theLimit == null) {
|
||||
numToReturn = pagingProvider.getDefaultPageSize();
|
||||
} else {
|
||||
numToReturn = Math.min(pagingProvider.getMaximumPageSize(), theLimit);
|
||||
}
|
||||
|
||||
numToReturn = Math.min(numToReturn, theResult.size() - theOffset);
|
||||
resourceList = theResult.getResources(theOffset, numToReturn + theOffset);
|
||||
RestfulServerUtils.validateResourceListNotNull(resourceList);
|
||||
|
||||
if (theSearchId != null) {
|
||||
searchId = theSearchId;
|
||||
} else {
|
||||
if (theResult.size() > numToReturn) {
|
||||
searchId = pagingProvider.storeResultList(theResult);
|
||||
Validate.notNull(searchId, "Paging provider returned null searchId");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (IResource next : resourceList) {
|
||||
if (next.getId() == null || next.getId().isEmpty()) {
|
||||
if (!(next instanceof BaseOperationOutcome)) {
|
||||
throw new InternalErrorException("Server method returned resource of type[" + next.getClass().getSimpleName() + "] with no ID specified (IResource#setId(IdDt) must be called)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
for (IResource nextRes : resourceList) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(nextRes);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
RestfulServerUtils.addProfileToBundleEntry(theServer.getFhirContext(), nextRes, theServerBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(theServer.getFhirContext(), theServer.getServerName(), resourceList, theServerBase, theCompleteUrl, theResult.size(), theBundleType);
|
||||
|
||||
bundle.setPublished(theResult.getPublished());
|
||||
|
||||
if (theServer.getPagingProvider() != null) {
|
||||
int limit;
|
||||
limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize();
|
||||
limit = Math.min(limit, theServer.getPagingProvider().getMaximumPageSize());
|
||||
|
||||
if (searchId != null) {
|
||||
if (theOffset + numToReturn < theResult.size()) {
|
||||
bundle.getLinkNext().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
if (theOffset > 0) {
|
||||
int start = Math.max(0, theOffset - limit);
|
||||
bundle.getLinkPrevious().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
|
||||
}
|
||||
}
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
}
|
|
@ -92,6 +92,11 @@ public class ResourceProviderDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
public void testTryToCreateResourceWithNumericId() {
|
||||
String resource = "<Patient xmlns=\"http://hl7.org/fhir\"><id value=\"1777\"/><meta><versionId value=\"1\"/><lastUpdated value=\"2015-02-25T15:47:48Z\"/></meta></Patient>";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for issue #60
|
||||
*/
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
|
||||
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
@ -33,7 +34,7 @@ import ca.uhn.fhir.model.dstu.valueset.ConditionStatusEnum;
|
|||
import ca.uhn.fhir.model.dstu.valueset.NameUseEnum;
|
||||
import ca.uhn.fhir.model.dstu.valueset.PractitionerRoleEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
|
||||
/**
|
||||
* Initially contributed by Alexander Kley for bug #29
|
||||
|
@ -196,7 +197,7 @@ public class ContainedResourceEncodingTest {
|
|||
|
||||
List<IResource> list = new ArrayList<IResource>();
|
||||
list.add(dr);
|
||||
Bundle bundle = RestfulServer.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
|
||||
|
||||
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
|
||||
String xml = parser.encodeBundleToString(bundle);
|
||||
|
@ -235,7 +236,7 @@ public class ContainedResourceEncodingTest {
|
|||
|
||||
List<IResource> list = new ArrayList<IResource>();
|
||||
list.add(dr);
|
||||
Bundle bundle = RestfulServer.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
|
||||
|
||||
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
|
||||
String xml = parser.encodeBundleToString(bundle);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -62,12 +63,47 @@ public class BinaryTest {
|
|||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("foo", status.getFirstHeader("content-type").getValue());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, responseContent);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithExplicitTypeXml() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=xml");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_XML + ";"));
|
||||
|
||||
Binary bin = ourCtx.newXmlParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithExplicitTypeJson() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=json");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_JSON + ";"));
|
||||
|
||||
Binary bin = ourCtx.newJsonParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
|
@ -104,6 +140,8 @@ public class BinaryTest {
|
|||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_ATOM_XML + "; charset=UTF-8", status.getFirstHeader("content-type").getValue());
|
||||
|
||||
|
|
|
@ -110,8 +110,8 @@ public class PagingTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchSmallPages() throws Exception {
|
||||
@Test()
|
||||
public void testExplicitEncoding() throws Exception {
|
||||
when(myPagingProvider.getDefaultPageSize()).thenReturn(5);
|
||||
when(myPagingProvider.getMaximumPageSize()).thenReturn(9);
|
||||
when(myPagingProvider.storeResultList(any(IBundleProvider.class))).thenReturn("ABCD");
|
||||
|
@ -120,7 +120,7 @@ public class PagingTest {
|
|||
String link;
|
||||
String base = "http://localhost:" + ourPort;
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(base + "/Patient?_count=2");
|
||||
HttpGet httpGet = new HttpGet(base + "/Patient?_count=2&_format=xml");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
@ -145,9 +145,51 @@ public class PagingTest {
|
|||
assertEquals(2, bundle.getEntries().size());
|
||||
assertEquals("2", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("3", bundle.getEntries().get(1).getId().getIdPart());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkNext().getValue());
|
||||
assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkSelf().getValue());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2&_format=xml", bundle.getLinkPrevious().getValue());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2", bundle.getLinkNext().getValue());
|
||||
assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2", bundle.getLinkSelf().getValue());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2", bundle.getLinkPrevious().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchSmallPages() throws Exception {
|
||||
when(myPagingProvider.getDefaultPageSize()).thenReturn(5);
|
||||
when(myPagingProvider.getMaximumPageSize()).thenReturn(9);
|
||||
when(myPagingProvider.storeResultList(any(IBundleProvider.class))).thenReturn("ABCD");
|
||||
when(myPagingProvider.retrieveResultList(eq("ABCD"))).thenReturn(ourBundleProvider);
|
||||
|
||||
String link;
|
||||
String base = "http://localhost:" + ourPort;
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(base + "/Patient?_count=2");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||
assertEquals(2, bundle.getEntries().size());
|
||||
assertEquals("0", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("1", bundle.getEntries().get(1).getId().getIdPart());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2", bundle.getLinkNext().getValue());
|
||||
assertNull(bundle.getLinkPrevious().getValue());
|
||||
link = bundle.getLinkNext().getValue();
|
||||
}
|
||||
{
|
||||
HttpGet httpGet = new HttpGet(link);
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
Bundle bundle = ourContext.newXmlParser().parseBundle(responseContent);
|
||||
assertEquals(2, bundle.getEntries().size());
|
||||
assertEquals("2", bundle.getEntries().get(0).getId().getIdPart());
|
||||
assertEquals("3", bundle.getEntries().get(1).getId().getIdPart());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=4&" + Constants.PARAM_COUNT + "=2", bundle.getLinkNext().getValue());
|
||||
assertEquals(base + '/' + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=2&" + Constants.PARAM_COUNT + "=2", bundle.getLinkSelf().getValue());
|
||||
assertEquals(base + '?' + Constants.PARAM_PAGINGACTION + "=ABCD&" + Constants.PARAM_PAGINGOFFSET + "=0&" + Constants.PARAM_COUNT + "=2", bundle.getLinkPrevious().getValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ public class RestfulServerMethodTest {
|
|||
|
||||
p.getManagingOrganization().setResource(o);
|
||||
|
||||
Bundle bundle = RestfulServer.createBundleFromResourceList(ourCtx, "", resources, "http://foo", "http://foo", 2, null);
|
||||
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(ourCtx, "", resources, "http://foo", "http://foo", 2, null);
|
||||
assertEquals(2, bundle.getEntries().size());
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
@ -58,6 +60,41 @@ public class BinaryTest {
|
|||
ourLast = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithExplicitTypeXml() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=xml");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_XML + ";"));
|
||||
|
||||
Binary bin = ourCtx.newXmlParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadWithExplicitTypeJson() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo?_format=json");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), "UTF-8");
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info(responseContent);
|
||||
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertThat(status.getFirstHeader("content-type").getValue(), startsWith(Constants.CT_FHIR_JSON + ";"));
|
||||
|
||||
Binary bin = ourCtx.newJsonParser().parseResource(Binary.class, responseContent);
|
||||
assertEquals("foo", bin.getContentType());
|
||||
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreate() throws Exception {
|
||||
HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary");
|
||||
|
|
|
@ -116,6 +116,17 @@
|
|||
Add support for DSTU2 style security labels in the parser and encoder. Thanks to
|
||||
Mohammad Jafari for the contribution!
|
||||
</action>
|
||||
<action type="fix">
|
||||
Server requests for Binary resources where the client has explicitly requested XML or JSON responses
|
||||
(either with a <![CDATA[<code>_format</code>]]> URL parameter, or an <![CDATA[<code>Accept</code>]]> request header)
|
||||
will be responded to using the Binary FHIR resource type instead of as Binary blobs. This is
|
||||
in accordance with the recommended behaviour in the FHIR specification.
|
||||
</action>
|
||||
<action type="add">
|
||||
Add a new property to RestfulServer called "DefaultResponseEncoding", which allows
|
||||
users to configure a default encoding (XML/JSON) to use if none is specified in the
|
||||
client request. Currently defaults to XML.
|
||||
</action>
|
||||
</release>
|
||||
<release version="0.8" date="2014-Dec-17">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue