Work on support for new bundle format in DSTU2

This commit is contained in:
James Agnew 2015-02-27 18:09:24 -05:00
parent 6580abe5f2
commit db65d5db59
38 changed files with 1183 additions and 267 deletions

View File

@ -28,7 +28,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.text.WordUtils;
import org.hl7.fhir.instance.model.IBase;

View File

@ -30,6 +30,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
public interface IFhirVersion {
@ -52,5 +53,6 @@ public interface IFhirVersion {
BaseCodingDt newCodingDt();
IVersionSpecificBundleFactory newBundleFactory();
}

View File

@ -1259,8 +1259,10 @@ class ParserState<T> {
push(new BundleLinkState(myInstance));
} else if ("entry".equals(theLocalPart)) {
push(new BundleEntryState(myInstance, myResourceType));
} else if ("text".equals(theLocalPart)) {
push(new SwallowChildrenWholeState(getPreResourceState()));
} else {
throw new DataFormatException("Unxpected element '" + theLocalPart + " in element 'Bundle'");
throw new DataFormatException("Unxpected element '" + theLocalPart + "' in element 'Bundle'");
}
// if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) {

View File

@ -85,6 +85,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
import ca.uhn.fhir.util.XmlUtil;
@ -244,12 +245,12 @@ public class XmlParser extends BaseParser implements IParser {
writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
writeAtomLink(eventWriter, Constants.LINK_SELF, theBundle.getLinkSelf());
writeAtomLink(eventWriter, Constants.LINK_FIRST, theBundle.getLinkFirst());
writeAtomLink(eventWriter, Constants.LINK_PREVIOUS, theBundle.getLinkPrevious());
writeAtomLink(eventWriter, Constants.LINK_NEXT, theBundle.getLinkNext());
writeAtomLink(eventWriter, Constants.LINK_LAST, theBundle.getLinkLast());
writeAtomLink(eventWriter, Constants.LINK_FHIR_BASE, theBundle.getLinkBase());
if (theBundle.getTotalResults().getValue() != null) {
eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS);

View File

@ -35,6 +35,7 @@ import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicNameValuePair;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
@ -47,7 +48,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.RestfulServerUtils;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
@ -168,7 +169,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myParams = theParams;
myBundleType = null;
}
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
StringBuilder url = new StringBuilder();
@ -235,9 +236,17 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
contents = parser.encodeBundleToString(myBundle);
contentType = encoding.getBundleContentType();
} else if (myResources != null) {
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(myContext, "", myResources, "", "", myResources.size(), myBundleType);
contents = parser.encodeBundleToString(bundle);
contentType = encoding.getBundleContentType();
IVersionSpecificBundleFactory bundleFactory = myContext.getVersion().newBundleFactory();
bundleFactory.initializeBundleFromResourceList(myContext, "", myResources, "", "", myResources.size(), myBundleType);
Bundle bundle = bundleFactory.getDstu1Bundle();
if (bundle != null) {
contents = parser.encodeBundleToString(bundle);
contentType = encoding.getBundleContentType();
} else {
IBaseResource bundleRes = bundleFactory.getResourceBundle();
contents = parser.encodeResourceToString(bundleRes);
contentType = encoding.getResourceContentType();
}
} else if (myContents != null) {
contents = myContents;
if (myContentsIsBundle) {

View File

@ -47,6 +47,7 @@ import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
@ -72,7 +73,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
set.add(Constants.PARAM_COUNT);
ALLOWED_PARAMS = Collections.unmodifiableSet(set);
}
private MethodReturnTypeEnum myMethodReturnType;
private Class<?> myResourceListCollectionType;
private String myResourceName;
@ -89,13 +90,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
Class<?> collectionType = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
if (collectionType != null) {
if (!Object.class.equals(collectionType) && !IResource.class.isAssignableFrom(collectionType)) {
throw new ConfigurationException("Method " + theMethod.getDeclaringClass().getSimpleName() + "#" + theMethod.getName() + " returns an invalid collection generic type: " + collectionType);
throw new ConfigurationException("Method " + theMethod.getDeclaringClass().getSimpleName() + "#" + theMethod.getName() + " returns an invalid collection generic type: "
+ collectionType);
}
}
myResourceListCollectionType = collectionType;
} else if (IBaseResource.class.isAssignableFrom(methodReturnType)) {
if (Modifier.isAbstract(methodReturnType.getModifiers())==false && theContext.getResourceDefinition((Class<? extends IBaseResource>) methodReturnType).isBundle()) {
if (Modifier.isAbstract(methodReturnType.getModifiers()) == false && theContext.getResourceDefinition((Class<? extends IBaseResource>) methodReturnType).isBundle()) {
myMethodReturnType = MethodReturnTypeEnum.BUNDLE_RESOURCE;
} else {
myMethodReturnType = MethodReturnTypeEnum.RESOURCE;
@ -105,7 +107,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
} else if (IBundleProvider.class.isAssignableFrom(methodReturnType)) {
myMethodReturnType = MethodReturnTypeEnum.BUNDLE_PROVIDER;
} else {
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: "
+ theMethod.getDeclaringClass().getCanonicalName());
}
if (theReturnResourceType != null) {
@ -113,8 +116,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
ResourceDef resourceDefAnnotation = theReturnResourceType.getAnnotation(ResourceDef.class);
if (resourceDefAnnotation == null) {
if (Modifier.isAbstract(theReturnResourceType.getModifiers())) {
// If we're returning an abstract type, that's ok
}else {
// If we're returning an abstract type, that's ok
} else {
throw new ConfigurationException(theReturnResourceType.getCanonicalName() + " has no @" + ResourceDef.class.getSimpleName() + " annotation");
}
} else {
@ -246,29 +249,53 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
HttpServletResponse response = theRequest.getServletResponse();
Object resultObj = invokeServer(theRequest, params);
switch (getReturnType()) {
case BUNDLE:{
case BUNDLE: {
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) {
IResource resource = (IResource) resultObj;
IResource resource;
if (resultObj instanceof IBundleProvider) {
IBundleProvider result = (IBundleProvider) resultObj;
resource = result.getResources(0, 1).get(0);
} else {
resource = (IResource) resultObj;
}
RestfulServerUtils.streamResponseAsResource(theServer, response, resource, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, respondGzip, theRequest.getFhirServerBase());
break;
} else {
IBundleProvider result = (IBundleProvider) resultObj;
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);
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
if (!continueProcessing) {
return;
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().getVersion().newBundleFactory();
bundleFactory.initializeBundleFromBundleProvider(theServer, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, 0, count, null,
getResponseBundleType());
Bundle bundle = bundleFactory.getDstu1Bundle();
if (bundle != null) {
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
if (!continueProcessing) {
ourLog.debug("Interceptor {} returned false, not continuing processing");
return;
}
}
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
} else {
IBaseResource resBundle = bundleFactory.getResourceBundle();
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = theServer.getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, resBundle, theRequest.getServletRequest(), theRequest.getServletResponse());
if (!continueProcessing) {
ourLog.debug("Interceptor {} returned false, not continuing processing");
return;
}
}
RestfulServerUtils.streamResponseAsResource(theServer, response, (IResource) resBundle, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode,
Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), theRequest.getFhirServerBase());
}
RestfulServerUtils.streamResponseAsBundle(theServer, response, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
break;
}
}
case RESOURCE:{
case RESOURCE: {
IBundleProvider result = (IBundleProvider) resultObj;
if (result.size() == 0) {
throw new ResourceNotFoundException(theRequest.getId());

View File

@ -113,6 +113,12 @@ public class Constants {
public static final String URL_TOKEN_METADATA = "metadata";
public static final String HEADER_IF_NONE_EXIST = "If-None-Exist";
public static final String HEADER_IF_NONE_EXIST_LC = HEADER_IF_NONE_EXIST.toLowerCase();
public static final String LINK_SELF = "self";
public static final String LINK_FIRST = "first";
public static final String LINK_PREVIOUS = "previous";
public static final String LINK_NEXT = "next";
public static final String LINK_LAST = "last";
public static final String LINK_FHIR_BASE = "fhir-base";
static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -0,0 +1,333 @@
package ca.uhn.fhir.rest.server;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.IBaseResource;
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.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class Dstu1BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu1BundleFactory.class);
private Bundle myBundle;
@Override
public void addResourcesToBundle(FhirContext theContext, List<IResource> theResult, BundleTypeEnum theBundleType, String theServerBase) {
if (myBundle == null) {
myBundle = new Bundle();
}
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);
myBundle.addResource(next, theContext, theServerBase);
}
/*
* Actually add the resources to the bundle
*/
for (IResource next : includedResources) {
BundleEntry entry = myBundle.addResource(next, theContext, theServerBase);
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
if (entry.getSearchMode().isEmpty()) {
entry.getSearchMode().setValueAsEnum(BundleEntrySearchModeEnum.INCLUDE);
}
}
}
}
@Override
public void initializeBundleFromBundleProvider(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);
}
}
}
addResourcesToBundle(theServer.getFhirContext(), resourceList, theBundleType, theServerBase);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType);
myBundle.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()) {
myBundle.getLinkNext().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
}
if (theOffset > 0) {
int start = Math.max(0, theOffset - limit);
myBundle.getLinkPrevious().setValue(RestfulServerUtils.createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
}
}
}
}
@Override
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
if (myBundle.getAuthorName().isEmpty()) {
myBundle.getAuthorName().setValue(theAuthor);
}
if (myBundle.getBundleId().isEmpty()) {
myBundle.getBundleId().setValue(UUID.randomUUID().toString());
}
if (myBundle.getPublished().isEmpty()) {
myBundle.getPublished().setToCurrentTimeInLocalTimeZone();
}
if (myBundle.getLinkBase().isEmpty()) {
myBundle.getLinkBase().setValue(theServerBase);
}
if (myBundle.getLinkSelf().isEmpty()) {
myBundle.getLinkSelf().setValue(theCompleteUrl);
}
if (theBundleType != null && myBundle.getType().isEmpty()) {
myBundle.getType().setValueAsString(theBundleType.getCode());
}
if (myBundle.getTotalResults().isEmpty()) {
myBundle.getTotalResults().setValue(theTotalResults);
}
}
@Override
public Bundle getDstu1Bundle() {
return myBundle;
}
@Override
public IBaseResource getResourceBundle() {
return null;
}
@Override
public void initializeBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
myBundle = new Bundle();
myBundle.getAuthorName().setValue(theAuthor);
myBundle.getBundleId().setValue(UUID.randomUUID().toString());
myBundle.getPublished().setToCurrentTimeInLocalTimeZone();
myBundle.getLinkBase().setValue(theServerBase);
myBundle.getLinkSelf().setValue(theCompleteUrl);
myBundle.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);
myBundle.addResource(next, theContext, theServerBase);
}
/*
* Actually add the resources to the bundle
*/
for (IResource next : includedResources) {
BundleEntry entry = myBundle.addResource(next, theContext, theServerBase);
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1)) {
if (entry.getSearchMode().isEmpty()) {
entry.getSearchMode().setValueAsEnum(BundleEntrySearchModeEnum.INCLUDE);
}
}
}
myBundle.getTotalResults().setValue(theTotalResults);
}
}

View File

@ -27,6 +27,11 @@ import ca.uhn.fhir.model.primitive.InstantDt;
public interface IBundleProvider {
/**
* @param theFromIndex The low index (inclusive) to return
* @param theToIndex The high index (exclusive) to return
* @return A list of resources. The size of this list must be at least <code>theToIndex - theFromIndex</code>.
*/
List<IResource> getResources(int theFromIndex, int theToIndex);
int size();

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.rest.server;
import java.util.List;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
public interface IVersionSpecificBundleFactory {
void addResourcesToBundle(FhirContext theContext, List<IResource> theResult, BundleTypeEnum theBundleType, String theServerBase);
void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType);
void initializeBundleFromBundleProvider(RestfulServer theServer, IBundleProvider theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint,
int theOffset, Integer theLimit, String theSearchId, BundleTypeEnum theBundleType);
Bundle getDstu1Bundle();
IBaseResource getResourceBundle();
void initializeBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType);
}

View File

@ -51,6 +51,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ProvidedResourceScanner;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
import ca.uhn.fhir.model.primitive.IdDt;
@ -79,7 +80,7 @@ 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;
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class);
private static final long serialVersionUID = 1L;
private AddProfileTagEnum myAddProfileTag;
@ -447,20 +448,34 @@ public class RestfulServer extends HttpServlet {
NarrativeModeEnum narrativeMode = RestfulServerUtils.determineNarrativeMode(theRequest);
boolean respondGzip = theRequest.isRespondGzip();
Bundle bundle = RestfulServerUtils.createBundleFromBundleProvider(this, resultList, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint,
start, count, thePagingAction, null);
IVersionSpecificBundleFactory bundleFactory = myFhirContext.getVersion().newBundleFactory();
bundleFactory.initializeBundleFromBundleProvider(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);
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
if (!continueProcessing) {
ourLog.debug("Interceptor {} returned false, not continuing processing");
return;
Bundle bundle = bundleFactory.getDstu1Bundle();
if (bundle != null) {
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, bundle, theRequest.getServletRequest(), theRequest.getServletResponse());
if (!continueProcessing) {
ourLog.debug("Interceptor {} returned false, not continuing processing");
return;
}
}
RestfulServerUtils.streamResponseAsBundle(this, theResponse, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
} else {
IBaseResource resBundle = bundleFactory.getResourceBundle();
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
IServerInterceptor next = getInterceptors().get(i);
boolean continueProcessing = next.outgoingResponse(theRequest, resBundle, theRequest.getServletRequest(), theRequest.getServletResponse());
if (!continueProcessing) {
ourLog.debug("Interceptor {} returned false, not continuing processing");
return;
}
}
RestfulServerUtils.streamResponseAsResource(this, theResponse, (IResource) resBundle, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, Constants.STATUS_HTTP_200_OK,
theRequest.isRespondGzip(), theRequest.getFhirServerBase());
}
RestfulServerUtils.streamResponseAsBundle(this, theResponse, bundle, responseEncoding, theRequest.getFhirServerBase(), prettyPrint, narrativeMode, respondGzip, requestIsBrowser);
}
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
@ -816,9 +831,9 @@ 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.
*
* @throws ServletException If the initialization failed. Note that you should consider throwing
* {@link UnavailableException} (which extends {@link ServletException}), as this is a flag
* to the servlet container that the servlet is not usable.
* @throws ServletException
* If the initialization failed. Note that you should consider throwing {@link UnavailableException} (which extends {@link ServletException}), as this is a flag to the servlet
* container that the servlet is not usable.
*/
protected void initialize() throws ServletException {
// nothing by default

View File

@ -7,13 +7,9 @@ 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;
@ -21,32 +17,26 @@ 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 {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerUtils.class);
static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
String countString = theRequest.getParameter(name);
Integer count = null;
@ -54,13 +44,13 @@ public class RestfulServerUtils {
try {
count = Integer.parseInt(countString);
} catch (NumberFormatException e) {
RestfulServer.ourLog.debug("Failed to parse _count value '{}': {}", countString, e);
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,
public 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);
@ -240,7 +230,7 @@ public class RestfulServerUtils {
return b.toString();
}
private static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
public static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
if (tl == null) {
@ -255,101 +245,6 @@ public class RestfulServerUtils {
}
}
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();
@ -427,8 +322,7 @@ public class RestfulServerUtils {
}
public static Integer extractCountParameter(HttpServletRequest theRequest) {
String name = Constants.PARAM_COUNT;
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, name);
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
}
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 {
@ -471,81 +365,12 @@ public class RestfulServerUtils {
RestfulServerUtils.streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
}
private static void validateResourceListNotNull(List<IResource> theResourceList) {
public 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;
}
}

View File

@ -26,8 +26,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.rest.annotation.Read;
@ -146,7 +147,7 @@ public interface IServerInterceptor {
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access attempt. If thrown, processing will stop and an HTTP 401 will be returned to the
* client.
*/
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws AuthenticationException;
/**

View File

@ -26,8 +26,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
@ -59,7 +60,7 @@ public class InterceptorAdapter implements IServerInterceptor {
}
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true;
}

View File

@ -24,9 +24,9 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
@ -62,7 +62,7 @@ public class ResponseValidatingInterceptor extends InterceptorAdapter {
}
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true;
}

View File

@ -35,6 +35,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
import ca.uhn.fhir.context.RuntimeChildDirectResource;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement;
@ -72,8 +73,8 @@ public class FhirTerser {
* well as any contained resources.
* </p>
* <p>
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not decend into linked resources (e.g.
* {@link BaseResourceReferenceDt#getResource()})
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
* {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
* </p>
*
* @param theResource
@ -191,6 +192,9 @@ public class FhirTerser {
// }
switch (theDefinition.getChildType()) {
// case RESOURCE:
// // Don't descend into embedded resources
// break;
case PRIMITIVE_XHTML:
case PRIMITIVE_DATATYPE:
// These are primitive types
@ -205,9 +209,14 @@ public class FhirTerser {
}
}
break;
case RESOURCE:
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE:
case RESOURCE: {
case COMPOSITE_DATATYPE: {
if (theChildDefinition instanceof RuntimeChildDirectResource) {
// Don't descend into embedded resources
return;
}
BaseRuntimeElementCompositeDefinition<?> childDef = (BaseRuntimeElementCompositeDefinition<?>) theDefinition;
for (BaseRuntimeChildDefinition nextChild : childDef.getChildrenAndExtension()) {
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
@ -264,6 +273,10 @@ public class FhirTerser {
/**
* Visit all elements in a given resource
*
* <p>
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
* {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
* </p>
* @param theResource
* The resource to visit
* @param theVisitor

View File

@ -27,7 +27,7 @@
<attribute name="org.eclipse.jst.component.nondependency" value=""/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<attributes>
<attribute name="owner.project.facets" value="java"/>
</attributes>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<faceted-project>
<installed facet="jst.utility" version="1.0"/>
<installed facet="java" version="1.6"/>
<installed facet="java" version="1.7"/>
</faceted-project>

View File

@ -514,7 +514,7 @@ public class ResourceProviderDstu2Test {
assertEquals(1, actual.getContained().getContainedResources().size());
assertThat(actual.getText().getDiv().getValueAsString(), containsString("<td>Identifier</td><td>testSaveAndRetrieveWithContained01</td>"));
Bundle b = ourClient.search().forResource("Patient").where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system:rpdstu2", "testSaveAndRetrieveWithContained01")).execute();
Bundle b = ourClient.search().forResource("Patient").where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system:rpdstu2", "testSaveAndRetrieveWithContained01")).prettyPrint().execute();
assertEquals(1, b.size());
}

View File

@ -13,9 +13,9 @@
<property name="servers">
<list>
<value>home , DSTU1 , UHN/HAPI Server (DSTU1 FHIR) , http://fhirtest.uhn.ca/baseDstu1</value>
<value>home_dev , DEV , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDstu2</value>
<value>home_dev , DSTU2 , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDstu2</value>
<value>hi , DSTU1 , Health Intersections (DSTU1 FHIR) , http://fhir.healthintersections.com.au/open</value>
<value>hidev , DEV , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open</value>
<value>hidev , DSTU2 , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open</value>
<value>furore , DSTU1 , Spark - Furore , http://spark.furore.com/fhir</value>
<value>blaze , DSTU1 , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
<value>oridashi , DSTU1 , Oridashi , http://demo.oridashi.com.au:8190</value>

View File

@ -37,7 +37,9 @@ import ca.uhn.fhir.model.dev.composite.ContainedDt;
import ca.uhn.fhir.model.dev.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dev.resource.Profile;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Dstu1BundleFactory;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.dev.ServerConformanceProvider;
import ca.uhn.fhir.rest.server.provider.dev.ServerProfileProvider;
@ -109,4 +111,9 @@ public class FhirDev implements IFhirVersion {
return new CodingDt();
}
@Override
public IVersionSpecificBundleFactory newBundleFactory() {
return new Dstu1BundleFactory();
}
}

View File

@ -75,7 +75,9 @@ import ca.uhn.fhir.model.dstu.resource.Profile.StructureElementDefinitionType;
import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.Dstu1BundleFactory;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
@ -384,5 +386,10 @@ public class FhirDstu1 implements IFhirVersion {
return new CodingDt();
}
@Override
public IVersionSpecificBundleFactory newBundleFactory() {
return new Dstu1BundleFactory();
}
}

View File

@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.NotImplementedException;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
@ -156,7 +157,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
* Intercept the outgoing response to perform auditing of the request data if the resource is auditable.
*/
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws AuthenticationException {
if(myClientParamsOptional && myDataStore == null){
//auditing is not required or configured, so do nothing here
@ -180,7 +181,7 @@ public class AuditingInterceptor extends InterceptorAdapter {
byte[] query = getQueryFromRequestDetails(theRequestDetails);
SecurityEventObjectLifecycleEnum lifecycle = mapResourceTypeToSecurityLifecycle(theRequestDetails.getResourceOperationType());
ObjectElement auditableObject = getObjectElement(theResponseObject, lifecycle , query);
ObjectElement auditableObject = getObjectElement((IResource) theResponseObject, lifecycle , query);
if(auditableObject == null){
log.debug("No auditable resources to audit");
return true;

View File

@ -15,7 +15,9 @@ import org.slf4j.LoggerFactory;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
@ -34,6 +36,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.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
/**
@ -197,7 +200,10 @@ public class ContainedResourceEncodingTest {
List<IResource> list = new ArrayList<IResource>();
list.add(dr);
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
IVersionSpecificBundleFactory factory = FhirVersionEnum.DSTU1.getVersionImplementation().newBundleFactory();
factory.initializeBundleFromResourceList(ctx, "", list, "http://foo", "http://foo", 2, null);
Bundle bundle = factory.getDstu1Bundle();
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
String xml = parser.encodeBundleToString(bundle);
@ -236,8 +242,11 @@ public class ContainedResourceEncodingTest {
List<IResource> list = new ArrayList<IResource>();
list.add(dr);
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(new FhirContext(), null, list, null, null, 0, null);
IVersionSpecificBundleFactory factory = FhirVersionEnum.DSTU1.getVersionImplementation().newBundleFactory();
factory.initializeBundleFromResourceList(ctx, "", list, "http://foo", "http://foo", 2, null);
Bundle bundle = factory.getDstu1Bundle();
IParser parser = this.ctx.newXmlParser().setPrettyPrint(true);
String xml = parser.encodeBundleToString(bundle);
Assert.assertTrue(xml.contains("Mueller"));

View File

@ -2,7 +2,10 @@ package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -16,7 +19,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import ca.uhn.fhir.model.dstu.resource.*;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
@ -40,6 +43,7 @@ import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
@ -49,6 +53,13 @@ import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.resource.AdverseReaction;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Profile;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -74,8 +85,6 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
import ca.uhn.fhir.util.PortUtil;
import javax.servlet.http.HttpServletRequest;
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -104,8 +113,9 @@ public class RestfulServerMethodTest {
p.getManagingOrganization().setResource(o);
Bundle bundle = RestfulServerUtils.createBundleFromResourceList(ourCtx, "", resources, "http://foo", "http://foo", 2, null);
assertEquals(2, bundle.getEntries().size());
IVersionSpecificBundleFactory factory = FhirVersionEnum.DSTU1.getVersionImplementation().newBundleFactory();
factory.initializeBundleFromResourceList(ourCtx, "", resources, "http://foo", "http://foo", 2, null);
assertEquals(2, factory.getDstu1Bundle().getEntries().size());
}
@Test

View File

@ -39,7 +39,9 @@ import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Profile;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.dstu2.Dstu2BundleFactory;
import ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider;
import ca.uhn.fhir.rest.server.provider.dstu2.ServerProfileProvider;
@ -109,5 +111,10 @@ public class FhirDstu2 implements IFhirVersion {
return new CodingDt();
}
@Override
public IVersionSpecificBundleFactory newBundleFactory() {
return new Dstu2BundleFactory();
}
}

View File

@ -0,0 +1,349 @@
package ca.uhn.fhir.rest.server.provider.dstu2;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.IBaseResource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Link;
import ca.uhn.fhir.model.dstu2.valueset.SearchEntryModeEnum;
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.rest.server.AddProfileTagEnum;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class Dstu2BundleFactory implements IVersionSpecificBundleFactory {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(Dstu2BundleFactory.class);
private Bundle myBundle;
@Override
public void addResourcesToBundle(FhirContext theContext, List<IResource> theResult, BundleTypeEnum theBundleType, String theServerBase) {
if (myBundle == null) {
myBundle = new Bundle();
}
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);
Entry entry = myBundle.addEntry().setResource(next);
BundleEntrySearchModeEnum searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(next);
if (searchMode != null) {
entry.getSearch().getModeElement().setValue(searchMode.getCode());
}
}
/*
* Actually add the resources to the bundle
*/
for (IResource next : includedResources) {
myBundle.addEntry().setResource(next).getSearch().setMode(SearchEntryModeEnum.INCLUDE);
}
}
@Override
public void addRootPropertiesToBundle(String theAuthor, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
if (myBundle.getId().isEmpty()) {
myBundle.setId(UUID.randomUUID().toString());
}
if (ResourceMetadataKeyEnum.PUBLISHED.get(myBundle) == null) {
InstantDt published = new InstantDt();
published.setToCurrentTimeInLocalTimeZone();
ResourceMetadataKeyEnum.PUBLISHED.put(myBundle, published);
}
if (!hasLink(Constants.LINK_SELF, myBundle) && isNotBlank(theCompleteUrl)) {
myBundle.addLink().setRelation("self").setUrl(theCompleteUrl);
}
if (!hasLink(Constants.LINK_FHIR_BASE, myBundle) && isNotBlank(theServerBase)) {
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
}
if (myBundle.getTypeElement().isEmpty() && theBundleType != null) {
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
}
if (myBundle.getTotalElement().isEmpty() && theTotalResults > 0) {
myBundle.getTotalElement().setValue(theTotalResults);
}
}
private boolean hasLink(String theLinkType, Bundle theBundle) {
for (Link next : theBundle.getLink()) {
if (theLinkType.equals(next.getRelation())) {
return true;
}
}
return false;
}
@Override
public void initializeBundleFromBundleProvider(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);
}
}
}
addResourcesToBundle(theServer.getFhirContext(), resourceList, theBundleType, theServerBase);
addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType);
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()) {
myBundle.addLink().setRelation(Constants.LINK_NEXT).setUrl(RestfulServerUtils.createPagingLink(theServerBase, searchId, theOffset + numToReturn, numToReturn, theResponseEncoding, thePrettyPrint));
}
if (theOffset > 0) {
int start = Math.max(0, theOffset - limit);
myBundle.addLink().setRelation(Constants.LINK_PREVIOUS).setUrl(RestfulServerUtils.createPagingLink(theServerBase, searchId, start, limit, theResponseEncoding, thePrettyPrint));
}
}
}
}
@Override
public ca.uhn.fhir.model.api.Bundle getDstu1Bundle() {
return null;
}
@Override
public IBaseResource getResourceBundle() {
return myBundle;
}
@Override
public void initializeBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) {
myBundle = new Bundle();
myBundle.setId(UUID.randomUUID().toString());
ResourceMetadataKeyEnum.PUBLISHED.put(myBundle, InstantDt.withCurrentTime());
myBundle.addLink().setRelation(Constants.LINK_FHIR_BASE).setUrl(theServerBase);
myBundle.addLink().setRelation(Constants.LINK_SELF).setUrl(theCompleteUrl);
myBundle.getTypeElement().setValueAsString(theBundleType.getCode());
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);
myBundle.addEntry().setResource(next);
}
/*
* Actually add the resources to the bundle
*/
for (IResource next : includedResources) {
myBundle.addEntry().setResource(next).getSearch().setMode(SearchEntryModeEnum.INCLUDE);
}
myBundle.getTotalElement().setValue(theTotalResults);
}
}

View File

@ -162,7 +162,7 @@ public class BinaryTest {
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());
assertEquals(Constants.CT_FHIR_XML + "; charset=UTF-8", status.getFirstHeader("content-type").getValue());
ourLog.info(responseContent);

View File

@ -48,6 +48,8 @@ public class BundleTypeInResponseTest {
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
ourLog.info(responseContent);
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.getEntries().size());
assertEquals(BundleTypeEnum.SEARCHSET, bundle.getType().getValueAsEnum());

View File

@ -35,13 +35,14 @@ public class SearchWithDstu2BundleTest {
@Test
public void testSearch() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=xml&_pretty=true");
// HttpResponse status = ourClient.execute(httpGet);
// String responseContent = IOUtils.toString(status.getEntity().getContent());
// IOUtils.closeQuietly(status.getEntity().getContent());
//
// assertEquals(200, status.getStatusLine().getStatusCode());
//
// ourLog.info(responseContent);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
ourLog.info(responseContent);
}
@ -68,7 +69,7 @@ public class SearchWithDstu2BundleTest {
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();

View File

@ -179,7 +179,7 @@ public class TransactionWithBundleResourceParamTest {
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(500000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();

View File

@ -0,0 +1,55 @@
package ca.uhn.fhir.util;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.util.List;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
public class FhirTerserTest {
private static FhirContext ourCtx = new FhirContext();
@Test
public void testGetAllPopulatedChildElementsOfTypeDoesntDescendIntoEmbedded() {
Patient p = new Patient();
p.addName().addFamily("PATIENT");
Bundle b = new Bundle();
b.addEntry().setResource(p);
b.addLink().setRelation("BUNDLE");
FhirTerser t = ourCtx.newTerser();
List<StringDt> strings = t.getAllPopulatedChildElementsOfType(b, StringDt.class);
assertEquals(1, strings.size());
assertThat(strings, containsInAnyOrder(new StringDt("BUNDLE")));
}
@Test
public void testGetAllPopulatedChildElementsOfTypeDescendsIntoContained() {
Patient p = new Patient();
p.addName().addFamily("PATIENT");
Organization o = new Organization();
o.getNameElement().setValue("ORGANIZATION");
p.getContained().getContainedResources().add(o);
FhirTerser t = ourCtx.newTerser();
List<StringDt> strings = t.getAllPopulatedChildElementsOfType(p, StringDt.class);
assertEquals(2, strings.size());
assertThat(strings, containsInAnyOrder(new StringDt("PATIENT"), new StringDt("ORGANIZATION")));
}
}

View File

@ -35,6 +35,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.IFhirVersion;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
public class FhirDstu2Hl7Org implements IFhirVersion {
@ -105,4 +106,9 @@ public class FhirDstu2Hl7Org implements IFhirVersion {
throw new UnsupportedOperationException();
}
@Override
public IVersionSpecificBundleFactory newBundleFactory() {
throw new UnsupportedOperationException();
}
}

View File

@ -41,6 +41,7 @@ import ca.uhn.fhir.model.dev.resource.Profile;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
public class FhirDev implements IFhirVersion {
@ -115,5 +116,9 @@ public class FhirDev implements IFhirVersion {
return new CodingDt();
}
@Override
public IVersionSpecificBundleFactory newBundleFactory() {
throw new UnsupportedOperationException();
}
}

View File

@ -75,6 +75,7 @@ import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
public class FhirDstu1 implements IFhirVersion {
@ -384,4 +385,10 @@ public class FhirDstu1 implements IFhirVersion {
return new ca.uhn.fhir.model.dstu.composite.CodingDt();
}
@Override
public IVersionSpecificBundleFactory newBundleFactory() {
throw new UnsupportedOperationException();
}
}

View File

@ -34,8 +34,10 @@ import ca.uhn.fhir.model.base.composite.BaseContainedDt;
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.composite.ContainedDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.rest.server.Dstu1BundleFactory;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
import ca.uhn.fhir.rest.server.RestfulServer;
public class FhirDstu2 implements IFhirVersion {
@ -100,4 +102,10 @@ public class FhirDstu2 implements IFhirVersion {
}
@Override
public IVersionSpecificBundleFactory newBundleFactory() {
throw new UnsupportedOperationException();
}
}

View File

@ -16,9 +16,9 @@
<property name="servers">
<list>
<!-- Each value is a reference to one FHIR server -->
<!-- Format is: [id],[name],[base URL] -->
<!-- Format is: [id],[FHIR version: DSTU1|DSTU2],[name],[base URL] -->
<!-- The URL should be customized to match the base URL for the FHIR endpoint -->
<value>example , Restful Server Example , http://localhost:8080/fhir</value>
<value>example , DSTU1 , Restful Server Example , http://localhost:8080/fhir</value>
</list>
</property>
</bean>

View File

@ -0,0 +1,176 @@
<?xml version="1.0" encoding="UTF-8"?>
<document xmlns="http://maven.apache.org/XDOC/2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
<properties>
<title>Server Tester - HAPI FHIR</title>
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
</properties>
<body>
<section name="FHIR Tester Web Application">
<macro name="toc">
</macro>
<p>
HAPI FHIR includes a web UI that can be used to test your server implementation.
This UI is the same UI used on the <a href="http://fhirtest.uhn.ca">http://fhirtest.uhn.ca</a>
public server.
</p>
<p>
The Tester is a
<a href="http://maven.apache.org/plugins/maven-war-plugin/overlays.html">Maven WAR Overlay</a>,
meaning that you create your own WAR project (which you would likely be doing anyway
to create your server) and the overlay drops a number of files into your project.
</p>
<subsection name="Adding the Overlay">
<p>
These instructions assume that you have an exsiting web project
which uses Maven to build. The POM.xml should have a "packaging"
type of "war".
</p>
<p>
Adding the overlay to your project is relatively simple. First,
add the "hapi-fhir-testpage-overlay" dependency to the dependencies
section of your POM.xml file.
<source><![CDATA[<dependencies>
<!-- ... other dependencies ... -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>${project.version}</version>
<type>war</type>
<scope>provided</scope>
</dependency>
</dependencies>]]></source>
</p>
<p>
Then, add the following WAR plugin to the plugins section
of your POM.xml
<source><![CDATA[<build>
<plugins>
<!-- ... other plugins ... -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<overlays>
<overlay>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId>
</overlay>
</overlays>
</configuration>
</plugin>
</build>
</plugins>]]></source>
</p>
<p>
Next, create the following directory in your project
if it doesn't already exist:<br/>
<code>src/main/webapp/WEB-INF</code>
</p>
<p>
Then, create a file in that directory
called <code>hapi-fhir-tester-config.xml</code>
and copy in the following contents:
</p>
<macro name="snippet">
<param name="file" value="restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml" />
</macro>
<p>
Note that the URL in the file above must be customized to point to
the FHIR endpoint your server will be deployed to. For example, if you
are naming your project "myfhir-1.0.war" and your endpoint in the WAR is
deployed to "/fhirbase/*" then you should put a URL similar to
<code>http://localhost:8080/myfhir-1.0/fhirbase</code>
</p>
<p>
Finally, in the same directory you should open you web.xml file. This file is
required in order to deploy to a servlet container and you should create it if
it does not already exist. Place the following contents in that file.
</p>
<source><![CDATA[<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/hapi-fhir-tester-application-context.xml
/WEB-INF/hapi-fhir-tester-config.xml
</param-value>
</context-param>
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/hapi-fhir-tester-application-context.xml
/WEB-INF/hapi-fhir-tester-config.xml
</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/tester/*</url-pattern>
</servlet-mapping>]]></source>
</subsection>
<subsection name="Customizing the Overlay">
<p>
The most important customization required is to
set the FHIR server base URL in the
<code>hapi-fhir-tester-config.xml</code>
configuration file created during the
previous step.
</p>
<p>
Beyond this, the entire tester application is built
from a number of
<a href="http://thymeleaf.org">Thymeleaf</a>
template files, any of which can be replaced to
create your own look and feel. All of the templates
can be found in your built war (after running the Maven
build), or in the target directory's staging area, in
<code>WEB-INF/templates</code>. By placing a file
with the same path/name in your <code>src/main/webapp/WEB-INF/templates</code>
directory you can replace the built in template
with your own file.
</p>
</subsection>
<subsection name="A Complete Example">
<p>
The "Restful Server Example" project contains a complete working
example of the FHIR Tester as a part of its configuration. You may
wish to browse its source to see how this works:<br/>
<a href="https://github.com/jamesagnew/hapi-fhir/tree/master/restful-server-example">https://github.com/jamesagnew/hapi-fhir/tree/master/restful-server-example</a>
</p>
</subsection>
</section>
</body>
</document>