Significant performance enhancements for context startup
This commit is contained in:
parent
99568a4b30
commit
7e57aed5d6
|
@ -6,6 +6,7 @@ import java.util.List;
|
|||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.PerformanceOptionsEnum;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
|
@ -25,12 +26,38 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
|||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
|
||||
public class GenericClientExample {
|
||||
public static void deferModelScanning() {
|
||||
// START SNIPPET: deferModelScanning
|
||||
// Create a context and configure it for deferred child scanning
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
ctx.setPerformanceOptions(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING);
|
||||
|
||||
// Now create a client and use it
|
||||
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
|
||||
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
|
||||
// END SNIPPET: deferModelScanning
|
||||
}
|
||||
|
||||
public static void performance() {
|
||||
// START SNIPPET: dontValidate
|
||||
// Create a context
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
||||
// Disable server validation (don't pull the server's metadata first)
|
||||
ctx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
|
||||
// Now create a client and use it
|
||||
String serverBase = "http://fhirtest.uhn.ca/baseDstu2";
|
||||
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
|
||||
// END SNIPPET: dontValidate
|
||||
}
|
||||
|
||||
public static void simpleExample() {
|
||||
// START SNIPPET: simple
|
||||
|
|
|
@ -97,6 +97,9 @@ public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeDecl
|
|||
@Override
|
||||
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
myElementDefinition = theClassToElementDefinitions.get(getDatatype());
|
||||
if (myElementDefinition == null) {
|
||||
myElementDefinition = theContext.getElementDefinition(getDatatype());
|
||||
}
|
||||
assert myElementDefinition != null : "Unknown type: " + getDatatype();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -23,23 +26,64 @@ package ca.uhn.fhir.context;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
|
||||
import org.hl7.fhir.instance.model.api.IBaseEnumeration;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseXhtml;
|
||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IBoundCodeableConcept;
|
||||
import ca.uhn.fhir.model.api.IDatatype;
|
||||
import ca.uhn.fhir.model.api.IElement;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.IResourceBlock;
|
||||
import ca.uhn.fhir.model.api.IValueSetEnumBinder;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.api.annotation.ChildOrder;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.api.annotation.Extension;
|
||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseNarrativeDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
||||
public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> extends BaseRuntimeElementDefinition<T> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeElementCompositeDefinition.class);
|
||||
private List<BaseRuntimeChildDefinition> myChildren = new ArrayList<BaseRuntimeChildDefinition>();
|
||||
private List<BaseRuntimeChildDefinition> myChildrenAndExtensions;
|
||||
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions;
|
||||
private FhirContext myContext;
|
||||
private Map<String, BaseRuntimeChildDefinition> myNameToChild = new HashMap<String, BaseRuntimeChildDefinition>();
|
||||
|
||||
public BaseRuntimeElementCompositeDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) {
|
||||
private volatile boolean mySealed;
|
||||
|
||||
public BaseRuntimeElementCompositeDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
super(theName, theImplementingClass, theStandardType);
|
||||
|
||||
myContext = theContext;
|
||||
myClassToElementDefinitions = theClassToElementDefinitions;
|
||||
}
|
||||
|
||||
void addChild(BaseRuntimeChildDefinition theNext) {
|
||||
|
@ -53,11 +97,13 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
|||
}
|
||||
|
||||
public BaseRuntimeChildDefinition getChildByName(String theName){
|
||||
validateSealed();
|
||||
BaseRuntimeChildDefinition retVal = myNameToChild.get(theName);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public BaseRuntimeChildDefinition getChildByNameOrThrowDataFormatException(String theName) throws DataFormatException {
|
||||
validateSealed();
|
||||
BaseRuntimeChildDefinition retVal = myNameToChild.get(theName);
|
||||
if (retVal == null) {
|
||||
throw new DataFormatException("Unknown child name '" + theName + "' in element " + getName() + " - Valid names are: " + new TreeSet<String>(myNameToChild.keySet()));
|
||||
|
@ -66,15 +112,354 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
|||
}
|
||||
|
||||
public List<BaseRuntimeChildDefinition> getChildren() {
|
||||
validateSealed();
|
||||
return myChildren;
|
||||
}
|
||||
|
||||
public List<BaseRuntimeChildDefinition> getChildrenAndExtension() {
|
||||
validateSealed();
|
||||
return myChildrenAndExtensions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Has this class been sealed
|
||||
*/
|
||||
public boolean isSealed() {
|
||||
return mySealed;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, BaseRuntimeElementCompositeDefinition<?> theDefinition) {
|
||||
Set<String> elementNames = new HashSet<String>();
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToElementDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
|
||||
|
||||
LinkedList<Class<? extends IBase>> classes = new LinkedList<Class<? extends IBase>>();
|
||||
|
||||
/*
|
||||
* We scan classes for annotated fields in the class but also all of its superclasses
|
||||
*/
|
||||
Class<? extends IBase> current = theClass;
|
||||
Map<String, Integer> forcedOrder = null;
|
||||
do {
|
||||
if (forcedOrder == null) {
|
||||
ChildOrder childOrder = current.getAnnotation(ChildOrder.class);
|
||||
if (childOrder != null) {
|
||||
forcedOrder = new HashMap<String, Integer>();
|
||||
for (int i = 0; i < childOrder.names().length; i++) {
|
||||
forcedOrder.put(childOrder.names()[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
classes.push(current);
|
||||
if (IBase.class.isAssignableFrom(current.getSuperclass())) {
|
||||
current = (Class<? extends IBase>) current.getSuperclass();
|
||||
} else {
|
||||
current = null;
|
||||
}
|
||||
} while (current != null);
|
||||
|
||||
for (Class<? extends IBase> next : classes) {
|
||||
scanCompositeElementForChildren(next, elementNames, orderToElementDef, orderToExtensionDef);
|
||||
}
|
||||
|
||||
if (forcedOrder != null) {
|
||||
/*
|
||||
* Find out how many elements don't match any entry in the list
|
||||
* for forced order. Those elements come first.
|
||||
*/
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> newOrderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
|
||||
int unknownCount = 0;
|
||||
for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
|
||||
if (!forcedOrder.containsKey(nextEntry.getElementName())) {
|
||||
newOrderToExtensionDef.put(unknownCount, nextEntry);
|
||||
unknownCount++;
|
||||
}
|
||||
}
|
||||
for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
|
||||
if (forcedOrder.containsKey(nextEntry.getElementName())) {
|
||||
Integer newOrder = forcedOrder.get(nextEntry.getElementName());
|
||||
newOrderToExtensionDef.put(newOrder + unknownCount, nextEntry);
|
||||
}
|
||||
}
|
||||
orderToElementDef = newOrderToExtensionDef;
|
||||
}
|
||||
|
||||
// while (orderToElementDef.size() > 0 && orderToElementDef.firstKey() <
|
||||
// 0) {
|
||||
// BaseRuntimeDeclaredChildDefinition elementDef =
|
||||
// orderToElementDef.remove(orderToElementDef.firstKey());
|
||||
// if (elementDef.getElementName().equals("identifier")) {
|
||||
// orderToElementDef.put(theIdentifierOrder, elementDef);
|
||||
// } else {
|
||||
// throw new ConfigurationException("Don't know how to handle element: "
|
||||
// + elementDef.getElementName());
|
||||
// }
|
||||
// }
|
||||
|
||||
TreeSet<Integer> orders = new TreeSet<Integer>();
|
||||
orders.addAll(orderToElementDef.keySet());
|
||||
orders.addAll(orderToExtensionDef.keySet());
|
||||
|
||||
for (Integer i : orders) {
|
||||
BaseRuntimeChildDefinition nextChild = orderToElementDef.get(i);
|
||||
if (nextChild != null) {
|
||||
theDefinition.addChild(nextChild);
|
||||
}
|
||||
BaseRuntimeDeclaredChildDefinition nextExt = orderToExtensionDef.get(i);
|
||||
if (nextExt != null) {
|
||||
theDefinition.addExtension((RuntimeChildDeclaredExtensionDefinition) nextExt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
||||
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
|
||||
|
||||
for (Field next : theClass.getDeclaredFields()) {
|
||||
|
||||
if (Modifier.isFinal(next.getModifiers())) {
|
||||
ourLog.trace("Ignoring constant {} on target type {}", next.getName(), theClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
Child childAnnotation = ModelScanner.pullAnnotation(next, Child.class);
|
||||
if (childAnnotation == null) {
|
||||
ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
Description descriptionAnnotation = ModelScanner.pullAnnotation(next, Description.class);
|
||||
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
|
||||
Extension extensionAttr = ModelScanner.pullAnnotation(next, Extension.class);
|
||||
if (extensionAttr != null) {
|
||||
orderMap = theOrderToExtensionDef;
|
||||
}
|
||||
|
||||
String elementName = childAnnotation.name();
|
||||
int order = childAnnotation.order();
|
||||
boolean childIsChoiceType = false;
|
||||
if (order == Child.REPLACE_PARENT) {
|
||||
|
||||
if (extensionAttr != null) {
|
||||
|
||||
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
|
||||
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
|
||||
if (nextDef instanceof RuntimeChildDeclaredExtensionDefinition) {
|
||||
if (nextDef.getExtensionUrl().equals(extensionAttr.url())) {
|
||||
order = nextEntry.getKey();
|
||||
orderMap.remove(nextEntry.getKey());
|
||||
elementNames.remove(elementName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (order == Child.REPLACE_PARENT) {
|
||||
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
|
||||
+ ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type " + next.getDeclaringClass().getSimpleName());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
|
||||
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
|
||||
if (elementName.equals(nextDef.getElementName())) {
|
||||
order = nextEntry.getKey();
|
||||
BaseRuntimeDeclaredChildDefinition existing = orderMap.remove(nextEntry.getKey());
|
||||
elementNames.remove(elementName);
|
||||
|
||||
/*
|
||||
* See #350 - If the original field (in the superclass) with the given name is a choice, then we need to make sure
|
||||
* that the field which replaces is a choice even if it's only a choice of one type - this is because the
|
||||
* element name when serialized still needs to reflect the datatype
|
||||
*/
|
||||
if (existing instanceof RuntimeChildChoiceDefinition) {
|
||||
childIsChoiceType = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (order == Child.REPLACE_PARENT) {
|
||||
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
|
||||
+ ") but no parent element with name " + elementName + " could be found on type " + next.getDeclaringClass().getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (order < 0 && order != Child.ORDER_UNKNOWN) {
|
||||
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + next.getName() + "' on target type: " + theClass);
|
||||
}
|
||||
if (order != Child.ORDER_UNKNOWN) {
|
||||
order = order + baseElementOrder;
|
||||
}
|
||||
// int min = childAnnotation.min();
|
||||
// int max = childAnnotation.max();
|
||||
|
||||
/*
|
||||
* Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict with any given IDs and can be figured out later
|
||||
*/
|
||||
if (order == Child.ORDER_UNKNOWN) {
|
||||
order = Integer.valueOf(0);
|
||||
while (orderMap.containsKey(order)) {
|
||||
order++;
|
||||
}
|
||||
}
|
||||
|
||||
List<Class<? extends IBase>> choiceTypes = new ArrayList<Class<? extends IBase>>();
|
||||
for (Class<? extends IBase> nextChoiceType : childAnnotation.type()) {
|
||||
choiceTypes.add(nextChoiceType);
|
||||
}
|
||||
|
||||
if (orderMap.containsKey(order)) {
|
||||
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + theClass.getCanonicalName() + "' - Already had: " + orderMap.get(order).getElementName());
|
||||
}
|
||||
|
||||
if (elementNames.contains(elementName)) {
|
||||
throw new ConfigurationException("Detected duplicate field name '" + elementName + "' in type '" + theClass.getCanonicalName() + "'");
|
||||
}
|
||||
|
||||
Class<?> nextElementType = ModelScanner.determineElementType(next);
|
||||
|
||||
if (childAnnotation.name().equals("extension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||
RuntimeChildExtension def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
} else if (childAnnotation.name().equals("modifierExtension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||
RuntimeChildExtension def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
} else if (BaseContainedDt.class.isAssignableFrom(nextElementType) || (childAnnotation.name().equals("contained") && IBaseResource.class.isAssignableFrom(nextElementType))) {
|
||||
/*
|
||||
* Child is contained resources
|
||||
*/
|
||||
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IAnyResource.class.isAssignableFrom(nextElementType) || IResource.class.equals(nextElementType)) {
|
||||
/*
|
||||
* Child is a resource as a direct child, as in Bundle.entry.resource
|
||||
*/
|
||||
RuntimeChildDirectResource def = new RuntimeChildDirectResource(next, childAnnotation, descriptionAnnotation, elementName);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else {
|
||||
childIsChoiceType |= choiceTypes.size() > 1;
|
||||
if (childIsChoiceType && !BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) && !IBaseReference.class.isAssignableFrom(nextElementType)) {
|
||||
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (next.getType().equals(ExtensionDt.class)) {
|
||||
|
||||
RuntimeChildExtensionDt def = new RuntimeChildExtensionDt(next, elementName, childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (extensionAttr != null) {
|
||||
/*
|
||||
* Child is an extension
|
||||
*/
|
||||
Class<? extends IBase> et = (Class<? extends IBase>) nextElementType;
|
||||
|
||||
Object binder = null;
|
||||
if (BoundCodeDt.class.isAssignableFrom(nextElementType) || IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
|
||||
binder = ModelScanner.getBoundCodeBinder(next);
|
||||
}
|
||||
|
||||
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et,
|
||||
binder);
|
||||
|
||||
if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
|
||||
def.setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next));
|
||||
}
|
||||
|
||||
orderMap.put(order, def);
|
||||
} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) || IBaseReference.class.isAssignableFrom(nextElementType)) {
|
||||
/*
|
||||
* Child is a resource reference
|
||||
*/
|
||||
List<Class<? extends IBaseResource>> refTypesList = new ArrayList<Class<? extends IBaseResource>>();
|
||||
for (Class<? extends IElement> nextType : childAnnotation.type()) {
|
||||
if (IBaseReference.class.isAssignableFrom(nextType)) {
|
||||
refTypesList.add(myContext.getVersion().getVersion().isRi() ? IAnyResource.class : IResource.class);
|
||||
continue;
|
||||
} else if (IBaseResource.class.isAssignableFrom(nextType) == false) {
|
||||
throw new ConfigurationException("Field '" + next.getName() + "' in class '" + next.getDeclaringClass().getCanonicalName() + "' is of type " + BaseResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
|
||||
}
|
||||
refTypesList.add((Class<? extends IBaseResource>) nextType);
|
||||
}
|
||||
RuntimeChildResourceDefinition def = new RuntimeChildResourceDefinition(next, elementName, childAnnotation, descriptionAnnotation, refTypesList);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.class.isAssignableFrom(nextElementType)
|
||||
|| IBaseDatatypeElement.class.isAssignableFrom(nextElementType)) {
|
||||
/*
|
||||
* Child is a resource block (i.e. a sub-tag within a resource) TODO: do these have a better name according to HL7?
|
||||
*/
|
||||
|
||||
Class<? extends IBase> blockDef = (Class<? extends IBase>) nextElementType;
|
||||
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(myContext, next, childAnnotation, descriptionAnnotation, elementName, blockDef);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "Type".equals(nextElementType.getSimpleName())
|
||||
|| IBaseDatatype.class.equals(nextElementType)) {
|
||||
|
||||
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType)
|
||||
|| IBaseDatatype.class.isAssignableFrom(nextElementType) || IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
|
||||
|
||||
BaseRuntimeChildDatatypeDefinition def;
|
||||
if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
|
||||
if (nextElementType.equals(BoundCodeDt.class)) {
|
||||
IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(next);
|
||||
Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(next);
|
||||
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
|
||||
} else if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
|
||||
Class<? extends Enum<?>> binderType = ModelScanner.determineEnumTypeForBoundField(next);
|
||||
def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
|
||||
} else {
|
||||
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
|
||||
}
|
||||
} else if (IBaseXhtml.class.isAssignableFrom(nextElementType)) {
|
||||
def = new RuntimeChildXhtmlDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
|
||||
} else {
|
||||
if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
|
||||
IValueSetEnumBinder<Enum<?>> binder = ModelScanner.getBoundCodeBinder(next);
|
||||
Class<? extends Enum<?>> enumType = ModelScanner.determineEnumTypeForBoundField(next);
|
||||
def = new RuntimeChildCompositeBoundDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
|
||||
} else if (BaseNarrativeDt.class.isAssignableFrom(nextElementType) || INarrative.class.isAssignableFrom(nextElementType)) {
|
||||
def = new RuntimeChildNarrativeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
|
||||
} else {
|
||||
def = new RuntimeChildCompositeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
|
||||
}
|
||||
}
|
||||
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else {
|
||||
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a valid child type: " + nextElementType);
|
||||
}
|
||||
}
|
||||
|
||||
elementNames.add(elementName);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
if (mySealed) {
|
||||
return;
|
||||
}
|
||||
mySealed = true;
|
||||
|
||||
scanCompositeElementForChildren(getImplementingClass(), this);
|
||||
|
||||
super.sealAndInitialize(theContext, theClassToElementDefinitions);
|
||||
|
||||
for (BaseRuntimeChildDefinition next : myChildren) {
|
||||
|
@ -141,6 +526,16 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
|||
myChildrenAndExtensions=Collections.unmodifiableList(children);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void validateSealed() {
|
||||
if (!mySealed) {
|
||||
synchronized(myContext) {
|
||||
sealAndInitialize(myContext, myClassToElementDefinitions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int findIndex(List<BaseRuntimeChildDefinition> theChildren, String theName, boolean theDefaultAtEnd) {
|
||||
int index = theDefaultAtEnd ? theChildren.size() : -1;
|
||||
for (ListIterator<BaseRuntimeChildDefinition> iter = theChildren.listIterator(); iter.hasNext(); ) {
|
||||
|
@ -152,4 +547,5 @@ public abstract class BaseRuntimeElementCompositeDefinition<T extends IBase> ext
|
|||
return index;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ package ca.uhn.fhir.context;
|
|||
*/
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -35,14 +34,14 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||
|
||||
private static final Class<Void> VOID_CLASS = Void.class;
|
||||
|
||||
private final String myName;
|
||||
private final Class<? extends T> myImplementingClass;
|
||||
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<Class<?>, Constructor<T>>());
|
||||
private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
||||
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
|
||||
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
||||
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
||||
private final Class<? extends T> myImplementingClass;
|
||||
private final String myName;
|
||||
private final boolean myStandardType;
|
||||
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<Class<?>, Constructor<T>>());
|
||||
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
|
||||
|
||||
public BaseRuntimeElementDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) {
|
||||
assert StringUtils.isNotBlank(theName);
|
||||
|
@ -60,15 +59,6 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||
myImplementingClass = theImplementingClass;
|
||||
}
|
||||
|
||||
public boolean isStandardType() {
|
||||
return myStandardType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+"[" + getName() + ", " + getImplementingClass().getSimpleName() + "]";
|
||||
}
|
||||
|
||||
public void addExtension(RuntimeChildDeclaredExtensionDefinition theExtension) {
|
||||
if (theExtension == null) {
|
||||
throw new NullPointerException();
|
||||
|
@ -76,56 +66,7 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||
myExtensions.add(theExtension);
|
||||
}
|
||||
|
||||
public List<RuntimeChildDeclaredExtensionDefinition> getExtensions() {
|
||||
return myExtensions;
|
||||
}
|
||||
|
||||
public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsModifier() {
|
||||
return myExtensionsModifier;
|
||||
}
|
||||
|
||||
public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsNonModifier() {
|
||||
return myExtensionsNonModifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns null if none
|
||||
*/
|
||||
public RuntimeChildDeclaredExtensionDefinition getDeclaredExtension(String theExtensionUrl) {
|
||||
return myUrlToExtension.get(theExtensionUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the runtime name for this resource (i.e. the name that
|
||||
* will be used in encoded messages)
|
||||
*/
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public T newInstance() {
|
||||
return newInstance(null);
|
||||
}
|
||||
|
||||
public T newInstance(Object theArgument) {
|
||||
try {
|
||||
if (theArgument == null) {
|
||||
return getConstructor(null).newInstance(null);
|
||||
} else {
|
||||
return getConstructor(theArgument).newInstance(theArgument);
|
||||
}
|
||||
} catch (InstantiationException e) {
|
||||
throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e);
|
||||
} catch (SecurityException e) {
|
||||
throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e);
|
||||
}
|
||||
}
|
||||
public abstract ChildTypeEnum getChildType();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Constructor<T> getConstructor(Object theArgument) {
|
||||
|
@ -160,10 +101,61 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns null if none
|
||||
*/
|
||||
public RuntimeChildDeclaredExtensionDefinition getDeclaredExtension(String theExtensionUrl) {
|
||||
validateSealed();
|
||||
return myUrlToExtension.get(theExtensionUrl);
|
||||
}
|
||||
|
||||
public List<RuntimeChildDeclaredExtensionDefinition> getExtensions() {
|
||||
validateSealed();
|
||||
return myExtensions;
|
||||
}
|
||||
|
||||
public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsModifier() {
|
||||
validateSealed();
|
||||
return myExtensionsModifier;
|
||||
}
|
||||
|
||||
public List<RuntimeChildDeclaredExtensionDefinition> getExtensionsNonModifier() {
|
||||
validateSealed();
|
||||
return myExtensionsNonModifier;
|
||||
}
|
||||
|
||||
public Class<? extends T> getImplementingClass() {
|
||||
return myImplementingClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the runtime name for this resource (i.e. the name that
|
||||
* will be used in encoded messages)
|
||||
*/
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public boolean isStandardType() {
|
||||
return myStandardType;
|
||||
}
|
||||
|
||||
public T newInstance() {
|
||||
return newInstance(null);
|
||||
}
|
||||
|
||||
public T newInstance(Object theArgument) {
|
||||
try {
|
||||
if (theArgument == null) {
|
||||
return getConstructor(null).newInstance(null);
|
||||
} else {
|
||||
return getConstructor(theArgument).newInstance(theArgument);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException("Failed to instantiate type:" + getImplementingClass().getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked prior to use to perform any initialization and make object
|
||||
* mutable.
|
||||
|
@ -192,29 +184,41 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||
myExtensions = Collections.unmodifiableList(myExtensions);
|
||||
}
|
||||
|
||||
public abstract ChildTypeEnum getChildType();
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName()+"[" + getName() + ", " + getImplementingClass().getSimpleName() + "]";
|
||||
}
|
||||
|
||||
protected void validateSealed() {
|
||||
/*
|
||||
* this does nothing, but BaseRuntimeElementCompositeDefinition
|
||||
* overrides this method to provide functionality because that class
|
||||
* defers the dealing process
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
public enum ChildTypeEnum {
|
||||
COMPOSITE_DATATYPE, PRIMITIVE_DATATYPE, RESOURCE, RESOURCE_BLOCK,
|
||||
/**
|
||||
COMPOSITE_DATATYPE, /**
|
||||
* HL7.org structure style.
|
||||
*/
|
||||
CONTAINED_RESOURCE_LIST, /**
|
||||
* HAPI structure style.
|
||||
*/
|
||||
CONTAINED_RESOURCES, EXTENSION_DECLARED,
|
||||
ID_DATATYPE,
|
||||
PRIMITIVE_DATATYPE, /**
|
||||
* HAPI style.
|
||||
*/
|
||||
PRIMITIVE_XHTML,
|
||||
UNDECL_EXT, EXTENSION_DECLARED,
|
||||
/**
|
||||
* HAPI structure style.
|
||||
*/
|
||||
CONTAINED_RESOURCES,
|
||||
ID_DATATYPE,
|
||||
/**
|
||||
* HL7.org structure style.
|
||||
*/
|
||||
CONTAINED_RESOURCE_LIST,
|
||||
|
||||
/**
|
||||
* HL7.org style.
|
||||
*/
|
||||
PRIMITIVE_XHTML_HL7ORG,
|
||||
RESOURCE,
|
||||
RESOURCE_BLOCK,
|
||||
|
||||
UNDECL_EXT,
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,14 @@ package ca.uhn.fhir.context;
|
|||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -86,16 +89,20 @@ public class FhirContext {
|
|||
private ArrayList<Class<? extends IBase>> myCustomTypes;
|
||||
private Map<String, Class<? extends IBaseResource>> myDefaultTypeForProfile = new HashMap<String, Class<? extends IBaseResource>>();
|
||||
private volatile Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = Collections.emptyMap();
|
||||
private boolean myInitialized;
|
||||
private HapiLocalizer myLocalizer = new HapiLocalizer();
|
||||
private volatile Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinition = Collections.emptyMap();
|
||||
private volatile Map<String, RuntimeResourceDefinition> myNameToResourceDefinition = Collections.emptyMap();
|
||||
private volatile Map<String, Class<? extends IBaseResource>> myNameToResourceType;
|
||||
private volatile INarrativeGenerator myNarrativeGenerator;
|
||||
private volatile IParserErrorHandler myParserErrorHandler = new LenientErrorHandler();
|
||||
private Set<PerformanceOptionsEnum> myPerformanceOptions = new HashSet<PerformanceOptionsEnum>();
|
||||
private Collection<Class<? extends IBaseResource>> myResourceTypesToScan;
|
||||
private volatile IRestfulClientFactory myRestfulClientFactory;
|
||||
private volatile RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
||||
private final IFhirVersion myVersion;
|
||||
private Map<FhirVersionEnum, Map<String, Class<? extends IBaseResource>>> myVersionToNameToResourceType = Collections.emptyMap();
|
||||
private boolean myInitializing;
|
||||
|
||||
/**
|
||||
* @deprecated It is recommended that you use one of the static initializer methods instead
|
||||
|
@ -166,7 +173,7 @@ public class FhirContext {
|
|||
ourLog.info("Creating new FHIR context for FHIR version [{}]", myVersion.getVersion().name());
|
||||
}
|
||||
|
||||
scanResourceTypes(toElementList(theResourceTypes));
|
||||
myResourceTypesToScan = theResourceTypes;
|
||||
}
|
||||
|
||||
private String createUnknownResourceNameError(String theResourceName, FhirVersionEnum theVersion) {
|
||||
|
@ -191,12 +198,18 @@ public class FhirContext {
|
|||
return myAddProfileTagWhenEncoding;
|
||||
}
|
||||
|
||||
Collection<RuntimeResourceDefinition> getAllResourceDefinitions() {
|
||||
validateInitialized();
|
||||
return myNameToResourceDefinition.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default resource type for the given profile
|
||||
*
|
||||
* @see #setDefaultTypeForProfile(String, Class)
|
||||
*/
|
||||
public Class<? extends IBaseResource> getDefaultTypeForProfile(String theProfile) {
|
||||
validateInitialized();
|
||||
return myDefaultTypeForProfile.get(theProfile);
|
||||
}
|
||||
|
||||
|
@ -206,6 +219,7 @@ public class FhirContext {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public BaseRuntimeElementDefinition<?> getElementDefinition(Class<? extends IBase> theElementType) {
|
||||
validateInitialized();
|
||||
BaseRuntimeElementDefinition<?> retVal = myClassToElementDefinition.get(theElementType);
|
||||
if (retVal == null) {
|
||||
retVal = scanDatatype((Class<? extends IElement>) theElementType);
|
||||
|
@ -221,11 +235,13 @@ public class FhirContext {
|
|||
* </p>
|
||||
*/
|
||||
public BaseRuntimeElementDefinition<?> getElementDefinition(String theElementName) {
|
||||
validateInitialized();
|
||||
return myNameToElementDefinition.get(theElementName.toLowerCase());
|
||||
}
|
||||
|
||||
/** For unit tests only */
|
||||
int getElementDefinitionCount() {
|
||||
validateInitialized();
|
||||
return myClassToElementDefinition.size();
|
||||
}
|
||||
|
||||
|
@ -233,6 +249,7 @@ public class FhirContext {
|
|||
* Returns all element definitions (resources, datatypes, etc.)
|
||||
*/
|
||||
public Collection<BaseRuntimeElementDefinition<?>> getElementDefinitions() {
|
||||
validateInitialized();
|
||||
return Collections.unmodifiableCollection(myClassToElementDefinition.values());
|
||||
}
|
||||
|
||||
|
@ -251,12 +268,20 @@ public class FhirContext {
|
|||
return myNarrativeGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configured performance options
|
||||
*/
|
||||
public Set<PerformanceOptionsEnum> getPerformanceOptions() {
|
||||
return myPerformanceOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scanned runtime model for the given type. This is an advanced feature which is generally only needed
|
||||
* for extending the core library.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public RuntimeResourceDefinition getResourceDefinition(Class<? extends IBaseResource> theResourceType) {
|
||||
validateInitialized();
|
||||
if (theResourceType == null) {
|
||||
throw new NullPointerException("theResourceType can not be null");
|
||||
}
|
||||
|
@ -273,6 +298,7 @@ public class FhirContext {
|
|||
|
||||
public RuntimeResourceDefinition getResourceDefinition(FhirVersionEnum theVersion, String theResourceName) {
|
||||
Validate.notNull(theVersion, "theVersion can not be null");
|
||||
validateInitialized();
|
||||
|
||||
if (theVersion.equals(myVersion.getVersion())) {
|
||||
return getResourceDefinition(theResourceName);
|
||||
|
@ -302,6 +328,7 @@ public class FhirContext {
|
|||
* for extending the core library.
|
||||
*/
|
||||
public RuntimeResourceDefinition getResourceDefinition(IBaseResource theResource) {
|
||||
validateInitialized();
|
||||
Validate.notNull(theResource, "theResource must not be null");
|
||||
return getResourceDefinition(theResource.getClass());
|
||||
}
|
||||
|
@ -315,6 +342,7 @@ public class FhirContext {
|
|||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public RuntimeResourceDefinition getResourceDefinition(String theResourceName) {
|
||||
validateInitialized();
|
||||
Validate.notBlank(theResourceName, "theResourceName must not be blank");
|
||||
|
||||
String resourceName = theResourceName.toLowerCase();
|
||||
|
@ -338,6 +366,7 @@ public class FhirContext {
|
|||
* for extending the core library.
|
||||
*/
|
||||
public RuntimeResourceDefinition getResourceDefinitionById(String theId) {
|
||||
validateInitialized();
|
||||
return myIdToResourceDefinition.get(theId);
|
||||
}
|
||||
|
||||
|
@ -345,7 +374,8 @@ public class FhirContext {
|
|||
* Returns the scanned runtime models. This is an advanced feature which is generally only needed for extending the
|
||||
* core library.
|
||||
*/
|
||||
public Collection<RuntimeResourceDefinition> getResourceDefinitions() {
|
||||
public Collection<RuntimeResourceDefinition> getResourceDefinitionsWithExplicitId() {
|
||||
validateInitialized();
|
||||
return myIdToResourceDefinition.values();
|
||||
}
|
||||
|
||||
|
@ -363,6 +393,7 @@ public class FhirContext {
|
|||
}
|
||||
|
||||
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
|
||||
validateInitialized();
|
||||
return myRuntimeChildUndeclaredExtensionDefinition;
|
||||
}
|
||||
|
||||
|
@ -378,6 +409,7 @@ public class FhirContext {
|
|||
* @see #getDefaultTypeForProfile(String)
|
||||
*/
|
||||
public boolean hasDefaultTypeForProfile() {
|
||||
validateInitialized();
|
||||
return !myDefaultTypeForProfile.isEmpty();
|
||||
}
|
||||
|
||||
|
@ -538,7 +570,8 @@ public class FhirContext {
|
|||
return (RuntimeResourceDefinition) defs.get(theResourceType);
|
||||
}
|
||||
|
||||
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(Collection<Class<? extends IElement>> theResourceTypes) {
|
||||
private synchronized Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> scanResourceTypes(Collection<Class<? extends IElement>> theResourceTypes) {
|
||||
myInitializing = true;
|
||||
|
||||
List<Class<? extends IBase>> typesToScan = new ArrayList<Class<? extends IBase>>();
|
||||
if (theResourceTypes != null) {
|
||||
|
@ -586,6 +619,7 @@ public class FhirContext {
|
|||
|
||||
myNameToResourceType = scanner.getNameToResourceType();
|
||||
|
||||
myInitialized = true;
|
||||
return classToElementDefinition;
|
||||
}
|
||||
|
||||
|
@ -659,6 +693,31 @@ public class FhirContext {
|
|||
myParserErrorHandler = theParserErrorHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configured performance options
|
||||
*
|
||||
* @see PerformanceOptionsEnum for a list of available options
|
||||
*/
|
||||
public void setPerformanceOptions(Collection<PerformanceOptionsEnum> theOptions) {
|
||||
myPerformanceOptions.clear();
|
||||
if (theOptions != null) {
|
||||
myPerformanceOptions.addAll(theOptions);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the configured performance options
|
||||
*
|
||||
* @see PerformanceOptionsEnum for a list of available options
|
||||
*/
|
||||
public void setPerformanceOptions(PerformanceOptionsEnum... thePerformanceOptions) {
|
||||
Collection<PerformanceOptionsEnum> asList = null;
|
||||
if (thePerformanceOptions != null) {
|
||||
asList = Arrays.asList(thePerformanceOptions);
|
||||
}
|
||||
setPerformanceOptions(asList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the restful client factory
|
||||
*
|
||||
|
@ -681,6 +740,12 @@ public class FhirContext {
|
|||
return resTypes;
|
||||
}
|
||||
|
||||
private void validateInitialized() {
|
||||
if (!myInitialized && !myInitializing) {
|
||||
scanResourceTypes(toElementList(myResourceTypesToScan));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU1 DSTU1}
|
||||
*/
|
||||
|
|
|
@ -118,7 +118,7 @@ class ModelScanner {
|
|||
myScanAlso.add(theType);
|
||||
}
|
||||
|
||||
private Class<?> determineElementType(Field next) {
|
||||
static Class<?> determineElementType(Field next) {
|
||||
Class<?> nextElementType = next.getType();
|
||||
if (List.class.equals(nextElementType)) {
|
||||
nextElementType = ReflectionUtil.getGenericCollectionTypeOfField(next);
|
||||
|
@ -129,7 +129,7 @@ class ModelScanner {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private IValueSetEnumBinder<Enum<?>> getBoundCodeBinder(Field theNext) {
|
||||
static IValueSetEnumBinder<Enum<?>> getBoundCodeBinder(Field theNext) {
|
||||
Class<?> bound = getGenericCollectionTypeOfCodedField(theNext);
|
||||
if (bound == null) {
|
||||
throw new ConfigurationException("Field '" + theNext + "' has no parameter for " + BoundCodeDt.class.getSimpleName() + " to determine enum type");
|
||||
|
@ -214,7 +214,16 @@ class ModelScanner {
|
|||
continue;
|
||||
}
|
||||
BaseRuntimeElementDefinition<?> next = nextEntry.getValue();
|
||||
next.sealAndInitialize(myContext, myClassToElementDefinitions);
|
||||
|
||||
boolean deferredSeal = false;
|
||||
if (myContext.getPerformanceOptions().contains(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING)) {
|
||||
if (next instanceof BaseRuntimeElementCompositeDefinition) {
|
||||
deferredSeal = true;
|
||||
}
|
||||
}
|
||||
if (!deferredSeal) {
|
||||
next.sealAndInitialize(myContext, myClassToElementDefinitions);
|
||||
}
|
||||
}
|
||||
|
||||
myRuntimeChildUndeclaredExtensionDefinition = new RuntimeChildUndeclaredExtensionDefinition();
|
||||
|
@ -235,7 +244,7 @@ class ModelScanner {
|
|||
* ones. Annotations can't extend each other or implement interfaces or anything like that, so rather than duplicate all of the annotation processing code this method just creates an interface
|
||||
* Proxy to simulate the HAPI annotations if the HL7.org ones are found instead.
|
||||
*/
|
||||
private <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) {
|
||||
static <T extends Annotation> T pullAnnotation(AnnotatedElement theTarget, Class<T> theAnnotationType) {
|
||||
T retVal = theTarget.getAnnotation(theAnnotationType);
|
||||
return retVal;
|
||||
}
|
||||
|
@ -298,366 +307,35 @@ class ModelScanner {
|
|||
throw new ConfigurationException("Block type @" + Block.class.getSimpleName() + " annotation contains no name: " + theClass.getCanonicalName());
|
||||
}
|
||||
|
||||
RuntimeResourceBlockDefinition resourceDef = new RuntimeResourceBlockDefinition(resourceName, theClass, isStandardType(theClass));
|
||||
myClassToElementDefinitions.put(theClass, resourceDef);
|
||||
RuntimeResourceBlockDefinition blockDef = new RuntimeResourceBlockDefinition(resourceName, theClass, isStandardType(theClass), myContext, myClassToElementDefinitions);
|
||||
myClassToElementDefinitions.put(theClass, blockDef);
|
||||
|
||||
scanCompositeElementForChildren(theClass, resourceDef);
|
||||
scanCompositeElementForChildren(theClass, blockDef);
|
||||
}
|
||||
|
||||
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, Object theBlockDef) {
|
||||
// TODO remove
|
||||
}
|
||||
|
||||
private void scanCompositeDatatype(Class<? extends ICompositeType> theClass, DatatypeDef theDatatypeDefinition) {
|
||||
ourLog.debug("Scanning datatype class: {}", theClass.getName());
|
||||
|
||||
RuntimeCompositeDatatypeDefinition resourceDef;
|
||||
RuntimeCompositeDatatypeDefinition elementDef;
|
||||
if (theClass.equals(ExtensionDt.class)) {
|
||||
resourceDef = new RuntimeExtensionDtDefinition(theDatatypeDefinition, theClass, true);
|
||||
elementDef = new RuntimeExtensionDtDefinition(theDatatypeDefinition, theClass, true, myContext, myClassToElementDefinitions);
|
||||
// } else if (IBaseMetaType.class.isAssignableFrom(theClass)) {
|
||||
// resourceDef = new RuntimeMetaDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
|
||||
} else {
|
||||
resourceDef = new RuntimeCompositeDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
|
||||
elementDef = new RuntimeCompositeDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass), myContext, myClassToElementDefinitions);
|
||||
}
|
||||
myClassToElementDefinitions.put(theClass, resourceDef);
|
||||
myNameToElementDefinitions.put(resourceDef.getName().toLowerCase(), resourceDef);
|
||||
scanCompositeElementForChildren(theClass, resourceDef);
|
||||
myClassToElementDefinitions.put(theClass, elementDef);
|
||||
myNameToElementDefinitions.put(elementDef.getName().toLowerCase(), elementDef);
|
||||
scanCompositeElementForChildren(theClass, elementDef);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, BaseRuntimeElementCompositeDefinition<?> theDefinition) {
|
||||
Set<String> elementNames = new HashSet<String>();
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToElementDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
|
||||
|
||||
LinkedList<Class<? extends IBase>> classes = new LinkedList<Class<? extends IBase>>();
|
||||
|
||||
/*
|
||||
* We scan classes for annotated fields in the class but also all of its superclasses
|
||||
*/
|
||||
Class<? extends IBase> current = theClass;
|
||||
Map<String, Integer> forcedOrder = null;
|
||||
do {
|
||||
if (forcedOrder == null) {
|
||||
ChildOrder childOrder = current.getAnnotation(ChildOrder.class);
|
||||
if (childOrder != null) {
|
||||
forcedOrder = new HashMap<String, Integer>();
|
||||
for (int i = 0; i < childOrder.names().length; i++) {
|
||||
forcedOrder.put(childOrder.names()[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
classes.push(current);
|
||||
if (IBase.class.isAssignableFrom(current.getSuperclass())) {
|
||||
current = (Class<? extends IBase>) current.getSuperclass();
|
||||
} else {
|
||||
current = null;
|
||||
}
|
||||
} while (current != null);
|
||||
|
||||
for (Class<? extends IBase> next : classes) {
|
||||
scanCompositeElementForChildren(next, elementNames, orderToElementDef, orderToExtensionDef);
|
||||
}
|
||||
|
||||
if (forcedOrder != null) {
|
||||
/*
|
||||
* Find out how many elements don't match any entry in the list
|
||||
* for forced order. Those elements come first.
|
||||
*/
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> newOrderToExtensionDef = new TreeMap<Integer, BaseRuntimeDeclaredChildDefinition>();
|
||||
int unknownCount = 0;
|
||||
for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
|
||||
if (!forcedOrder.containsKey(nextEntry.getElementName())) {
|
||||
newOrderToExtensionDef.put(unknownCount, nextEntry);
|
||||
unknownCount++;
|
||||
}
|
||||
}
|
||||
for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
|
||||
if (forcedOrder.containsKey(nextEntry.getElementName())) {
|
||||
Integer newOrder = forcedOrder.get(nextEntry.getElementName());
|
||||
newOrderToExtensionDef.put(newOrder + unknownCount, nextEntry);
|
||||
}
|
||||
}
|
||||
orderToElementDef = newOrderToExtensionDef;
|
||||
}
|
||||
|
||||
// while (orderToElementDef.size() > 0 && orderToElementDef.firstKey() <
|
||||
// 0) {
|
||||
// BaseRuntimeDeclaredChildDefinition elementDef =
|
||||
// orderToElementDef.remove(orderToElementDef.firstKey());
|
||||
// if (elementDef.getElementName().equals("identifier")) {
|
||||
// orderToElementDef.put(theIdentifierOrder, elementDef);
|
||||
// } else {
|
||||
// throw new ConfigurationException("Don't know how to handle element: "
|
||||
// + elementDef.getElementName());
|
||||
// }
|
||||
// }
|
||||
|
||||
TreeSet<Integer> orders = new TreeSet<Integer>();
|
||||
orders.addAll(orderToElementDef.keySet());
|
||||
orders.addAll(orderToExtensionDef.keySet());
|
||||
|
||||
for (Integer i : orders) {
|
||||
BaseRuntimeChildDefinition nextChild = orderToElementDef.get(i);
|
||||
if (nextChild != null) {
|
||||
theDefinition.addChild(nextChild);
|
||||
}
|
||||
BaseRuntimeDeclaredChildDefinition nextExt = orderToExtensionDef.get(i);
|
||||
if (nextExt != null) {
|
||||
theDefinition.addExtension((RuntimeChildDeclaredExtensionDefinition) nextExt);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void scanCompositeElementForChildren(Class<? extends IBase> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
||||
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
|
||||
|
||||
for (Field next : theClass.getDeclaredFields()) {
|
||||
|
||||
if (Modifier.isFinal(next.getModifiers())) {
|
||||
ourLog.trace("Ignoring constant {} on target type {}", next.getName(), theClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
Child childAnnotation = pullAnnotation(next, Child.class);
|
||||
if (childAnnotation == null) {
|
||||
ourLog.trace("Ignoring non @Child field {} on target type {}", next.getName(), theClass);
|
||||
continue;
|
||||
}
|
||||
|
||||
Description descriptionAnnotation = pullAnnotation(next, Description.class);
|
||||
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> orderMap = theOrderToElementDef;
|
||||
Extension extensionAttr = pullAnnotation(next, Extension.class);
|
||||
if (extensionAttr != null) {
|
||||
orderMap = theOrderToExtensionDef;
|
||||
}
|
||||
|
||||
String elementName = childAnnotation.name();
|
||||
int order = childAnnotation.order();
|
||||
boolean childIsChoiceType = false;
|
||||
if (order == Child.REPLACE_PARENT) {
|
||||
|
||||
if (extensionAttr != null) {
|
||||
|
||||
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
|
||||
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
|
||||
if (nextDef instanceof RuntimeChildDeclaredExtensionDefinition) {
|
||||
if (nextDef.getExtensionUrl().equals(extensionAttr.url())) {
|
||||
order = nextEntry.getKey();
|
||||
orderMap.remove(nextEntry.getKey());
|
||||
elementNames.remove(elementName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (order == Child.REPLACE_PARENT) {
|
||||
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
|
||||
+ ") but no parent element with extension URL " + extensionAttr.url() + " could be found on type " + next.getDeclaringClass().getSimpleName());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
for (Entry<Integer, BaseRuntimeDeclaredChildDefinition> nextEntry : orderMap.entrySet()) {
|
||||
BaseRuntimeDeclaredChildDefinition nextDef = nextEntry.getValue();
|
||||
if (elementName.equals(nextDef.getElementName())) {
|
||||
order = nextEntry.getKey();
|
||||
BaseRuntimeDeclaredChildDefinition existing = orderMap.remove(nextEntry.getKey());
|
||||
elementNames.remove(elementName);
|
||||
|
||||
/*
|
||||
* See #350 - If the original field (in the superclass) with the given name is a choice, then we need to make sure
|
||||
* that the field which replaces is a choice even if it's only a choice of one type - this is because the
|
||||
* element name when serialized still needs to reflect the datatype
|
||||
*/
|
||||
if (existing instanceof RuntimeChildChoiceDefinition) {
|
||||
childIsChoiceType = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (order == Child.REPLACE_PARENT) {
|
||||
throw new ConfigurationException("Field " + next.getName() + "' on target type " + theClass.getSimpleName() + " has order() of REPLACE_PARENT (" + Child.REPLACE_PARENT
|
||||
+ ") but no parent element with name " + elementName + " could be found on type " + next.getDeclaringClass().getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (order < 0 && order != Child.ORDER_UNKNOWN) {
|
||||
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + next.getName() + "' on target type: " + theClass);
|
||||
}
|
||||
if (order != Child.ORDER_UNKNOWN) {
|
||||
order = order + baseElementOrder;
|
||||
}
|
||||
// int min = childAnnotation.min();
|
||||
// int max = childAnnotation.max();
|
||||
|
||||
/*
|
||||
* Anything that's marked as unknown is given a new ID that is <0 so that it doesn't conflict with any given IDs and can be figured out later
|
||||
*/
|
||||
if (order == Child.ORDER_UNKNOWN) {
|
||||
order = Integer.valueOf(0);
|
||||
while (orderMap.containsKey(order)) {
|
||||
order++;
|
||||
}
|
||||
}
|
||||
|
||||
List<Class<? extends IBase>> choiceTypes = new ArrayList<Class<? extends IBase>>();
|
||||
for (Class<? extends IBase> nextChoiceType : childAnnotation.type()) {
|
||||
choiceTypes.add(nextChoiceType);
|
||||
}
|
||||
|
||||
if (orderMap.containsKey(order)) {
|
||||
throw new ConfigurationException("Detected duplicate field order '" + childAnnotation.order() + "' for element named '" + elementName + "' in type '" + theClass.getCanonicalName() + "' - Already had: " + orderMap.get(order).getElementName());
|
||||
}
|
||||
|
||||
if (elementNames.contains(elementName)) {
|
||||
throw new ConfigurationException("Detected duplicate field name '" + elementName + "' in type '" + theClass.getCanonicalName() + "'");
|
||||
}
|
||||
|
||||
Class<?> nextElementType = determineElementType(next);
|
||||
|
||||
if (childAnnotation.name().equals("extension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||
RuntimeChildExtension def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
} else if (childAnnotation.name().equals("modifierExtension") && IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||
RuntimeChildExtension def = new RuntimeChildExtension(next, childAnnotation.name(), childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
} else if (BaseContainedDt.class.isAssignableFrom(nextElementType) || (childAnnotation.name().equals("contained") && IBaseResource.class.isAssignableFrom(nextElementType))) {
|
||||
/*
|
||||
* Child is contained resources
|
||||
*/
|
||||
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IAnyResource.class.isAssignableFrom(nextElementType) || IResource.class.equals(nextElementType)) {
|
||||
/*
|
||||
* Child is a resource as a direct child, as in Bundle.entry.resource
|
||||
*/
|
||||
RuntimeChildDirectResource def = new RuntimeChildDirectResource(next, childAnnotation, descriptionAnnotation, elementName);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else {
|
||||
childIsChoiceType |= choiceTypes.size() > 1;
|
||||
if (childIsChoiceType && !BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) && !IBaseReference.class.isAssignableFrom(nextElementType)) {
|
||||
/*
|
||||
* Child is a choice element
|
||||
*/
|
||||
for (Class<? extends IBase> nextType : choiceTypes) {
|
||||
addScanAlso(nextType);
|
||||
}
|
||||
RuntimeChildChoiceDefinition def = new RuntimeChildChoiceDefinition(next, elementName, childAnnotation, descriptionAnnotation, choiceTypes);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (next.getType().equals(ExtensionDt.class)) {
|
||||
|
||||
RuntimeChildExtensionDt def = new RuntimeChildExtensionDt(next, elementName, childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
if (IElement.class.isAssignableFrom(nextElementType)) {
|
||||
addScanAlso((Class<? extends IElement>) nextElementType);
|
||||
}
|
||||
|
||||
} else if (extensionAttr != null) {
|
||||
/*
|
||||
* Child is an extension
|
||||
*/
|
||||
Class<? extends IBase> et = (Class<? extends IBase>) nextElementType;
|
||||
|
||||
Object binder = null;
|
||||
if (BoundCodeDt.class.isAssignableFrom(nextElementType) || IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
|
||||
binder = getBoundCodeBinder(next);
|
||||
}
|
||||
|
||||
RuntimeChildDeclaredExtensionDefinition def = new RuntimeChildDeclaredExtensionDefinition(next, childAnnotation, descriptionAnnotation, extensionAttr, elementName, extensionAttr.url(), et,
|
||||
binder);
|
||||
|
||||
if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
|
||||
def.setEnumerationType(ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next));
|
||||
}
|
||||
|
||||
orderMap.put(order, def);
|
||||
if (IBase.class.isAssignableFrom(nextElementType)) {
|
||||
addScanAlso((Class<? extends IBase>) nextElementType);
|
||||
}
|
||||
} else if (BaseResourceReferenceDt.class.isAssignableFrom(nextElementType) || IBaseReference.class.isAssignableFrom(nextElementType)) {
|
||||
/*
|
||||
* Child is a resource reference
|
||||
*/
|
||||
List<Class<? extends IBaseResource>> refTypesList = new ArrayList<Class<? extends IBaseResource>>();
|
||||
for (Class<? extends IElement> nextType : childAnnotation.type()) {
|
||||
if (IBaseReference.class.isAssignableFrom(nextType)) {
|
||||
refTypesList.add(myVersion.isRi() ? IAnyResource.class : IResource.class);
|
||||
continue;
|
||||
} else if (IBaseResource.class.isAssignableFrom(nextType) == false) {
|
||||
throw new ConfigurationException("Field '" + next.getName() + "' in class '" + next.getDeclaringClass().getCanonicalName() + "' is of type " + BaseResourceReferenceDt.class + " but contains a non-resource type: " + nextType.getCanonicalName());
|
||||
}
|
||||
refTypesList.add((Class<? extends IBaseResource>) nextType);
|
||||
addScanAlso(nextType);
|
||||
}
|
||||
RuntimeChildResourceDefinition def = new RuntimeChildResourceDefinition(next, elementName, childAnnotation, descriptionAnnotation, refTypesList);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IResourceBlock.class.isAssignableFrom(nextElementType) || IBaseBackboneElement.class.isAssignableFrom(nextElementType)
|
||||
|| IBaseDatatypeElement.class.isAssignableFrom(nextElementType)) {
|
||||
/*
|
||||
* Child is a resource block (i.e. a sub-tag within a resource) TODO: do these have a better name according to HL7?
|
||||
*/
|
||||
|
||||
Class<? extends IBase> blockDef = (Class<? extends IBase>) nextElementType;
|
||||
addScanAlso(blockDef);
|
||||
RuntimeChildResourceBlockDefinition def = new RuntimeChildResourceBlockDefinition(next, childAnnotation, descriptionAnnotation, elementName, blockDef);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IDatatype.class.equals(nextElementType) || IElement.class.equals(nextElementType) || "Type".equals(nextElementType.getSimpleName())
|
||||
|| IBaseDatatype.class.equals(nextElementType)) {
|
||||
|
||||
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else if (IDatatype.class.isAssignableFrom(nextElementType) || IPrimitiveType.class.isAssignableFrom(nextElementType) || ICompositeType.class.isAssignableFrom(nextElementType)
|
||||
|| IBaseDatatype.class.isAssignableFrom(nextElementType) || IBaseExtension.class.isAssignableFrom(nextElementType)) {
|
||||
Class<? extends IBase> nextDatatype = (Class<? extends IBase>) nextElementType;
|
||||
|
||||
addScanAlso(nextDatatype);
|
||||
BaseRuntimeChildDatatypeDefinition def;
|
||||
if (IPrimitiveType.class.isAssignableFrom(nextElementType)) {
|
||||
if (nextElementType.equals(BoundCodeDt.class)) {
|
||||
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
|
||||
Class<? extends Enum<?>> enumType = determineEnumTypeForBoundField(next);
|
||||
def = new RuntimeChildPrimitiveBoundCodeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
|
||||
} else if (IBaseEnumeration.class.isAssignableFrom(nextElementType)) {
|
||||
Class<? extends Enum<?>> binderType = determineEnumTypeForBoundField(next);
|
||||
def = new RuntimeChildPrimitiveEnumerationDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binderType);
|
||||
} else {
|
||||
def = new RuntimeChildPrimitiveDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
|
||||
}
|
||||
} else if (IBaseXhtml.class.isAssignableFrom(nextElementType)) {
|
||||
def = new RuntimeChildXhtmlDatatypeDefinition(next, elementName, descriptionAnnotation, childAnnotation, nextDatatype);
|
||||
} else {
|
||||
if (IBoundCodeableConcept.class.isAssignableFrom(nextElementType)) {
|
||||
IValueSetEnumBinder<Enum<?>> binder = getBoundCodeBinder(next);
|
||||
Class<? extends Enum<?>> enumType = determineEnumTypeForBoundField(next);
|
||||
def = new RuntimeChildCompositeBoundDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype, binder, enumType);
|
||||
} else if (BaseNarrativeDt.class.isAssignableFrom(nextElementType) || INarrative.class.isAssignableFrom(nextElementType)) {
|
||||
def = new RuntimeChildNarrativeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
|
||||
} else {
|
||||
def = new RuntimeChildCompositeDatatypeDefinition(next, elementName, childAnnotation, descriptionAnnotation, nextDatatype);
|
||||
}
|
||||
}
|
||||
|
||||
orderMap.put(order, def);
|
||||
|
||||
} else {
|
||||
throw new ConfigurationException("Field '" + elementName + "' in type '" + theClass.getCanonicalName() + "' is not a valid child type: " + nextElementType);
|
||||
}
|
||||
}
|
||||
|
||||
elementNames.add(elementName);
|
||||
}
|
||||
}
|
||||
|
||||
private Class<? extends Enum<?>> determineEnumTypeForBoundField(Field next) {
|
||||
static Class<? extends Enum<?>> determineEnumTypeForBoundField(Field next) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Enum<?>> enumType = (Class<? extends Enum<?>>) ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next);
|
||||
return enumType;
|
||||
|
@ -671,28 +349,28 @@ class ModelScanner {
|
|||
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name: " + theClass.getCanonicalName());
|
||||
}
|
||||
|
||||
BaseRuntimeElementDefinition<?> resourceDef;
|
||||
BaseRuntimeElementDefinition<?> elementDef;
|
||||
if (theClass.equals(XhtmlDt.class)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<XhtmlDt> clazz = (Class<XhtmlDt>) theClass;
|
||||
resourceDef = new RuntimePrimitiveDatatypeNarrativeDefinition(resourceName, clazz, isStandardType(clazz));
|
||||
elementDef = new RuntimePrimitiveDatatypeNarrativeDefinition(resourceName, clazz, isStandardType(clazz));
|
||||
} else if (IBaseXhtml.class.isAssignableFrom(theClass)) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends IBaseXhtml> clazz = (Class<? extends IBaseXhtml>) theClass;
|
||||
resourceDef = new RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition(resourceName, clazz, isStandardType(clazz));
|
||||
elementDef = new RuntimePrimitiveDatatypeXhtmlHl7OrgDefinition(resourceName, clazz, isStandardType(clazz));
|
||||
} else if (IIdType.class.isAssignableFrom(theClass)) {
|
||||
resourceDef = new RuntimeIdDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
|
||||
elementDef = new RuntimeIdDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
|
||||
} else {
|
||||
resourceDef = new RuntimePrimitiveDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
|
||||
elementDef = new RuntimePrimitiveDatatypeDefinition(theDatatypeDefinition, theClass, isStandardType(theClass));
|
||||
}
|
||||
myClassToElementDefinitions.put(theClass, resourceDef);
|
||||
myClassToElementDefinitions.put(theClass, elementDef);
|
||||
if (!theDatatypeDefinition.isSpecialization()) {
|
||||
if (myVersion.isRi() && IDatatype.class.isAssignableFrom(theClass)) {
|
||||
ourLog.debug("Not adding non RI type {} to RI context", theClass);
|
||||
} else if (!myVersion.isRi() && !IDatatype.class.isAssignableFrom(theClass)) {
|
||||
ourLog.debug("Not adding RI type {} to non RI context", theClass);
|
||||
} else {
|
||||
myNameToElementDefinitions.put(resourceName, resourceDef);
|
||||
myNameToElementDefinitions.put(resourceName, elementDef);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -736,7 +414,7 @@ class ModelScanner {
|
|||
}
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(myContext, resourceName, theClass, resourceDefinition, standardType);
|
||||
RuntimeResourceDefinition resourceDef = new RuntimeResourceDefinition(myContext, resourceName, theClass, resourceDefinition, standardType, myClassToElementDefinitions);
|
||||
myClassToElementDefinitions.put(theClass, resourceDef);
|
||||
if (primaryNameProvider) {
|
||||
if (resourceDef.getStructureVersion() == myVersion) {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
/**
|
||||
* This enum contains options to be used for {@link FhirContext#setPerformanceOptions(PerformanceOptionsEnum...)}
|
||||
*/
|
||||
public enum PerformanceOptionsEnum {
|
||||
|
||||
/**
|
||||
* When this option is set, model classes will not be scanned for children until the
|
||||
* child list for the given type is actually accessed.
|
||||
* <p>
|
||||
* The effect of this option is that reflection operations to scan children will be
|
||||
* deferred, and some may never happen if specific model types aren't actually used.
|
||||
* This option is useful on environments where reflection is particularly slow, e.g.
|
||||
* Android or low powered devices.
|
||||
* </p>
|
||||
*/
|
||||
DEFERRED_MODEL_SCANNING
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.context;
|
|||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.hamcrest.Matchers.emptyCollectionOf;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
|
@ -158,6 +159,15 @@ public class RuntimeChildDeclaredExtensionDefinition extends BaseRuntimeDeclared
|
|||
myUrlToChildExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
|
||||
|
||||
BaseRuntimeElementDefinition<?> elementDef = theClassToElementDefinitions.get(myChildType);
|
||||
|
||||
/*
|
||||
* This will happen for any type that isn't defined in the base set of
|
||||
* built-in types, e.g. custom structures or custom extensions
|
||||
*/
|
||||
if (elementDef == null) {
|
||||
elementDef = theContext.getElementDefinition(myChildType);
|
||||
}
|
||||
|
||||
if (elementDef instanceof RuntimePrimitiveDatatypeDefinition || elementDef instanceof RuntimeCompositeDatatypeDefinition) {
|
||||
myDatatypeChildName = "value" + elementDef.getName().substring(0, 1).toUpperCase() + elementDef.getName().substring(1);
|
||||
if ("valueResourceReference".equals(myDatatypeChildName)) {
|
||||
|
|
|
@ -34,21 +34,27 @@ public class RuntimeChildResourceBlockDefinition extends BaseRuntimeDeclaredChil
|
|||
|
||||
private RuntimeResourceBlockDefinition myElementDef;
|
||||
private Class<? extends IBase> myResourceBlockType;
|
||||
private FhirContext myContext;
|
||||
|
||||
public RuntimeChildResourceBlockDefinition(Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName, Class<? extends IBase> theResourceBlockType) throws ConfigurationException {
|
||||
public RuntimeChildResourceBlockDefinition(FhirContext theContext, Field theField, Child theChildAnnotation, Description theDescriptionAnnotation, String theElementName, Class<? extends IBase> theResourceBlockType) throws ConfigurationException {
|
||||
super(theField, theChildAnnotation, theDescriptionAnnotation, theElementName);
|
||||
myContext = theContext;
|
||||
myResourceBlockType = theResourceBlockType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RuntimeResourceBlockDefinition getChildByName(String theName) {
|
||||
if (getElementName().equals(theName)) {
|
||||
return myElementDef;
|
||||
return getDefinition();
|
||||
}else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private RuntimeResourceBlockDefinition getDefinition() {
|
||||
return (RuntimeResourceBlockDefinition) myContext.getElementDefinition(myResourceBlockType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getChildNameByDatatype(Class<? extends IBase> theDatatype) {
|
||||
if (myResourceBlockType.equals(theDatatype)) {
|
||||
|
@ -60,7 +66,7 @@ public class RuntimeChildResourceBlockDefinition extends BaseRuntimeDeclaredChil
|
|||
@Override
|
||||
public BaseRuntimeElementDefinition<?> getChildElementDefinitionByDatatype(Class<? extends IBase> theDatatype) {
|
||||
if (myResourceBlockType.equals(theDatatype)) {
|
||||
return myElementDef;
|
||||
return getDefinition();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -72,7 +78,7 @@ public class RuntimeChildResourceBlockDefinition extends BaseRuntimeDeclaredChil
|
|||
|
||||
@Override
|
||||
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
myElementDef = (RuntimeResourceBlockDefinition) theClassToElementDefinitions.get(myResourceBlockType);
|
||||
// myElementDef = (RuntimeResourceBlockDefinition) theClassToElementDefinitions.get(myResourceBlockType);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompos
|
|||
private Class<? extends IBaseDatatype> myProfileOfType;
|
||||
private BaseRuntimeElementDefinition<?> myProfileOf;
|
||||
|
||||
public RuntimeCompositeDatatypeDefinition(DatatypeDef theDef, Class<? extends ICompositeType> theImplementingClass, boolean theStandardType) {
|
||||
super(theDef.name(), theImplementingClass, theStandardType);
|
||||
public RuntimeCompositeDatatypeDefinition(DatatypeDef theDef, Class<? extends ICompositeType> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
super(theDef.name(), theImplementingClass, theStandardType, theContext, theClassToElementDefinitions);
|
||||
|
||||
String resourceName = theDef.name();
|
||||
if (isBlank(resourceName)) {
|
||||
|
@ -81,6 +81,7 @@ public class RuntimeCompositeDatatypeDefinition extends BaseRuntimeElementCompos
|
|||
|
||||
@Override
|
||||
public boolean isProfileOf(Class<? extends IBaseDatatype> theType) {
|
||||
validateSealed();
|
||||
if (myProfileOfType != null) {
|
||||
if (myProfileOfType.equals(theType)) {
|
||||
return true;
|
||||
|
|
|
@ -34,8 +34,8 @@ public class RuntimeExtensionDtDefinition extends RuntimeCompositeDatatypeDefini
|
|||
|
||||
private List<BaseRuntimeChildDefinition> myChildren;
|
||||
|
||||
public RuntimeExtensionDtDefinition(DatatypeDef theDef, Class<? extends ICompositeType> theImplementingClass, boolean theStandardType) {
|
||||
super(theDef, theImplementingClass, theStandardType);
|
||||
public RuntimeExtensionDtDefinition(DatatypeDef theDef, Class<? extends ICompositeType> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
super(theDef, theImplementingClass, theStandardType, theContext, theClassToElementDefinitions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -24,8 +26,8 @@ import org.hl7.fhir.instance.model.api.IBase;
|
|||
|
||||
public class RuntimeResourceBlockDefinition extends BaseRuntimeElementCompositeDefinition<IBase> {
|
||||
|
||||
public RuntimeResourceBlockDefinition(String theName, Class<? extends IBase> theImplementingClass, boolean theStandardType) {
|
||||
super(theName, theImplementingClass, theStandardType);
|
||||
public RuntimeResourceBlockDefinition(String theName, Class<? extends IBase> theImplementingClass, boolean theStandardType, FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
super(theName, theImplementingClass, theStandardType, theContext, theClassToElementDefinitions);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -36,7 +36,7 @@ import ca.uhn.fhir.util.UrlUtil;
|
|||
|
||||
public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefinition<IBaseResource> {
|
||||
|
||||
private RuntimeResourceDefinition myBaseDefinition;
|
||||
private Class<? extends IBaseResource> myBaseType;
|
||||
private Map<String, List<RuntimeSearchParam>> myCompartmentNameToSearchParams;
|
||||
private FhirContext myContext;
|
||||
private String myId;
|
||||
|
@ -45,9 +45,10 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
private String myResourceProfile;
|
||||
private List<RuntimeSearchParam> mySearchParams;
|
||||
private final FhirVersionEnum myStructureVersion;
|
||||
private volatile RuntimeResourceDefinition myBaseDefinition;
|
||||
|
||||
public RuntimeResourceDefinition(FhirContext theContext, String theResourceName, Class<? extends IBaseResource> theClass, ResourceDef theResourceAnnotation, boolean theStandardType) {
|
||||
super(theResourceName, theClass, theStandardType);
|
||||
public RuntimeResourceDefinition(FhirContext theContext, String theResourceName, Class<? extends IBaseResource> theClass, ResourceDef theResourceAnnotation, boolean theStandardType, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
super(theResourceName, theClass, theStandardType, theContext, theClassToElementDefinitions);
|
||||
myContext = theContext;
|
||||
myResourceProfile = theResourceAnnotation.profile();
|
||||
myId = theResourceAnnotation.id();
|
||||
|
@ -76,6 +77,10 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
* </p>
|
||||
*/
|
||||
public RuntimeResourceDefinition getBaseDefinition() {
|
||||
validateSealed();
|
||||
if (myBaseDefinition == null) {
|
||||
myBaseDefinition = myContext.getResourceDefinition(myBaseType);
|
||||
}
|
||||
return myBaseDefinition;
|
||||
}
|
||||
|
||||
|
@ -105,6 +110,7 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
}
|
||||
|
||||
public String getResourceProfile(String theServerBase) {
|
||||
validateSealed();
|
||||
String profile;
|
||||
if (!myResourceProfile.isEmpty()) {
|
||||
profile = myResourceProfile;
|
||||
|
@ -128,10 +134,12 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
}
|
||||
|
||||
public RuntimeSearchParam getSearchParam(String theName) {
|
||||
validateSealed();
|
||||
return myNameToSearchParam.get(theName);
|
||||
}
|
||||
|
||||
public List<RuntimeSearchParam> getSearchParams() {
|
||||
validateSealed();
|
||||
return mySearchParams;
|
||||
}
|
||||
|
||||
|
@ -139,6 +147,7 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
* Will not return null
|
||||
*/
|
||||
public List<RuntimeSearchParam> getSearchParamsForCompartmentName(String theCompartmentName) {
|
||||
validateSealed();
|
||||
List<RuntimeSearchParam> retVal = myCompartmentNameToSearchParams.get(theCompartmentName);
|
||||
if (retVal == null) {
|
||||
return Collections.emptyList();
|
||||
|
@ -154,6 +163,7 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
return "Bundle".equals(getName());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||
super.sealAndInitialize(theContext, theClassToElementDefinitions);
|
||||
|
@ -183,17 +193,18 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
myCompartmentNameToSearchParams = Collections.unmodifiableMap(compartmentNameToSearchParams);
|
||||
|
||||
Class<?> target = getImplementingClass();
|
||||
myBaseDefinition = this;
|
||||
myBaseType = (Class<? extends IBaseResource>) target;
|
||||
do {
|
||||
target = target.getSuperclass();
|
||||
if (IBaseResource.class.isAssignableFrom(target) && target.getAnnotation(ResourceDef.class) != null) {
|
||||
myBaseDefinition = (RuntimeResourceDefinition) theClassToElementDefinitions.get(target);
|
||||
myBaseType = (Class<? extends IBaseResource>) target;
|
||||
}
|
||||
} while (target.equals(Object.class) == false);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public synchronized IBaseResource toProfile() {
|
||||
validateSealed();
|
||||
if (myProfileDef != null) {
|
||||
return myProfileDef;
|
||||
}
|
||||
|
@ -205,6 +216,7 @@ public class RuntimeResourceDefinition extends BaseRuntimeElementCompositeDefini
|
|||
}
|
||||
|
||||
public synchronized IBaseResource toProfile(String theServerBase) {
|
||||
validateSealed();
|
||||
if (myProfileDef != null) {
|
||||
return myProfileDef;
|
||||
}
|
||||
|
|
|
@ -216,6 +216,10 @@ public interface IRestfulClientFactory {
|
|||
* validation involves the client requesting the server's conformance statement
|
||||
* to determine whether the server is appropriate for the given client.
|
||||
* <p>
|
||||
* This check is primarily to validate that the server supports an appropriate
|
||||
* version of FHIR
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value for this setting is defined by {@link #DEFAULT_SERVER_VALIDATION_MODE}
|
||||
* </p>
|
||||
*
|
||||
|
|
|
@ -37,6 +37,8 @@ import javax.persistence.criteria.Root;
|
|||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
|
@ -45,8 +47,10 @@ import org.springframework.transaction.support.TransactionCallback;
|
|||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
|
||||
import ca.uhn.fhir.jpa.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.util.ReindexFailureException;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
|
@ -68,6 +72,9 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
@Autowired
|
||||
private IForcedIdDao myForcedIdDao;
|
||||
|
||||
@Autowired
|
||||
private ITermConceptDao myTermConceptDao;
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
@Override
|
||||
public void deleteAllTagsOnServer(RequestDetails theRequestDetails) {
|
||||
|
@ -81,6 +88,12 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
private int doPerformReindexingPass(final Integer theCount, final RequestDetails theRequestDetails) {
|
||||
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
|
||||
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
|
||||
int retVal = doPerformReindexingPassForResources(theCount, theRequestDetails, txTemplate);
|
||||
retVal += doPerformReindexingPassForConcepts(txTemplate);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private int doPerformReindexingPassForResources(final Integer theCount, final RequestDetails theRequestDetails, TransactionTemplate txTemplate) {
|
||||
return txTemplate.execute(new TransactionCallback<Integer>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
|
@ -106,8 +119,7 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
for (ResourceTable resourceTable : resources) {
|
||||
try {
|
||||
/*
|
||||
* This part is because from HAPI 1.5 - 1.6 we changed the format of
|
||||
* forced ID to be "type/id" instead of just "id"
|
||||
* This part is because from HAPI 1.5 - 1.6 we changed the format of forced ID to be "type/id" instead of just "id"
|
||||
*/
|
||||
ForcedId forcedId = resourceTable.getForcedId();
|
||||
if (forcedId != null) {
|
||||
|
@ -140,6 +152,37 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
});
|
||||
}
|
||||
|
||||
private int doPerformReindexingPassForConcepts(TransactionTemplate txTemplate) {
|
||||
return txTemplate.execute(new TransactionCallback<Integer>() {
|
||||
@Override
|
||||
public Integer doInTransaction(TransactionStatus theStatus) {
|
||||
|
||||
int maxResult = 10000;
|
||||
Page<TermConcept> resources = myTermConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
|
||||
if (resources.hasContent() == false) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ourLog.info("Indexing {} / {} concepts", resources.getContent().size(), resources.getTotalElements());
|
||||
|
||||
int count = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
for (TermConcept resourceTable : resources) {
|
||||
resourceTable.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
|
||||
myTermConceptDao.save(resourceTable);
|
||||
count++;
|
||||
}
|
||||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
long avg = (delay / resources.getContent().size());
|
||||
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, resources.getContent().size(), delay, avg });
|
||||
|
||||
return resources.getContent().size();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagList getAllTags(RequestDetails theRequestDetails) {
|
||||
// Notify interceptors
|
||||
|
@ -194,7 +237,9 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
@Transactional()
|
||||
@Override
|
||||
public int markAllResourcesForReindexing() {
|
||||
return myEntityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " t SET t.myIndexStatus = null").executeUpdate();
|
||||
int retVal = myEntityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " t SET t.myIndexStatus = null").executeUpdate();
|
||||
retVal += myTermConceptDao.markAllForReindexing();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void markResourceAsIndexingFailed(final long theId) {
|
||||
|
|
|
@ -2,6 +2,9 @@ package ca.uhn.fhir.jpa.dao.data;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -42,4 +45,11 @@ public interface ITermConceptDao extends JpaRepository<TermConcept, Long> {
|
|||
@Modifying
|
||||
void deleteByCodeSystemVersion(@Param("cs_pid") Long thePid);
|
||||
|
||||
@Query("UPDATE TermConcept t SET t.myIndexStatus = null")
|
||||
@Modifying
|
||||
int markAllForReindexing();
|
||||
|
||||
@Query("SELECT t FROM TermConcept t WHERE t.myIndexStatus = null")
|
||||
Page<TermConcept> findResourcesRequiringReindexing(Pageable thePageRequest);
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
|
|||
import org.hl7.fhir.dstu3.model.CodeableConcept;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetFilterComponent;
|
||||
|
@ -71,8 +72,7 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
|
|||
@Override
|
||||
public ValueSet expand(IIdType theId, String theFilter) {
|
||||
ValueSet source = myValidationSupport.fetchResource(getContext(), ValueSet.class, theId.getValue());
|
||||
ValueSet retVal = doExpand(source);
|
||||
return retVal;
|
||||
return expand(source, theFilter);
|
||||
}
|
||||
|
||||
private ValueSet doExpand(ValueSet theSource) {
|
||||
|
@ -135,11 +135,35 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
|
|||
|
||||
@Override
|
||||
public ValueSet expand(ValueSet source, String theFilter) {
|
||||
ValueSet retVal = doExpand(source);
|
||||
ValueSet toExpand = new ValueSet();
|
||||
for (UriType next : source.getCompose().getImport()) {
|
||||
ConceptSetComponent include = toExpand.getCompose().addInclude();
|
||||
include.setSystem(next.getValue());
|
||||
addFilterIfPresent(theFilter, include);
|
||||
}
|
||||
|
||||
for (ConceptSetComponent next : source.getCompose().getInclude()) {
|
||||
toExpand.getCompose().addInclude(next);
|
||||
addFilterIfPresent(theFilter, next);
|
||||
}
|
||||
|
||||
if (toExpand.getCompose().isEmpty()) {
|
||||
throw new InvalidRequestException("ValueSet does not have any compose.include or compose.import values, can not expand");
|
||||
}
|
||||
|
||||
toExpand.getCompose().getExclude().addAll(source.getCompose().getExclude());
|
||||
|
||||
ValueSet retVal = doExpand(toExpand);
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
||||
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
|
||||
if (isNotBlank(theFilter)) {
|
||||
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
|
||||
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding, CodeableConcept theCodeableConcept) {
|
||||
|
|
|
@ -36,6 +36,7 @@ import javax.persistence.ForeignKey;
|
|||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Index;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
@ -63,6 +64,8 @@ import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
|||
@Indexed()
|
||||
@Table(name="TRM_CONCEPT", uniqueConstraints= {
|
||||
@UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"})
|
||||
}, indexes= {
|
||||
@Index(name = "IDX_CONCEPT_INDEXSTATUS", columnList="INDEX_STATUS")
|
||||
})
|
||||
//@formatter:on
|
||||
public class TermConcept implements Serializable {
|
||||
|
@ -83,6 +86,12 @@ public class TermConcept implements Serializable {
|
|||
@JoinColumn(name="CODESYSTEM_PID", referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_CONCEPT_PID_CS_PID"))
|
||||
private TermCodeSystemVersion myCodeSystem;
|
||||
|
||||
@Column(name="CODESYSTEM_PID", insertable=false, updatable=false)
|
||||
@Fields({
|
||||
@Field(name="myCodeSystemVersionPid")
|
||||
})
|
||||
private long myCodeSystemVersionPid;
|
||||
|
||||
//@formatter:off
|
||||
@Column(name="DISPLAY", length=MAX_DESC_LENGTH, nullable=true)
|
||||
@Fields({
|
||||
|
@ -100,6 +109,9 @@ public class TermConcept implements Serializable {
|
|||
@Column(name="PID")
|
||||
private Long myId;
|
||||
|
||||
@Column(name = "INDEX_STATUS", nullable = true)
|
||||
private Long myIndexStatus;
|
||||
|
||||
@Transient
|
||||
@Fields({
|
||||
@Field(name = "myParentPids", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "standardAnalyzer")),
|
||||
|
@ -109,12 +121,6 @@ public class TermConcept implements Serializable {
|
|||
@OneToMany(cascade= {}, fetch=FetchType.LAZY, mappedBy="myChild")
|
||||
private Collection<TermConceptParentChildLink> myParents;
|
||||
|
||||
@Column(name="CODESYSTEM_PID", insertable=false, updatable=false)
|
||||
@Fields({
|
||||
@Field(name="myCodeSystemVersionPid")
|
||||
})
|
||||
private long myCodeSystemVersionPid;
|
||||
|
||||
public TermConcept() {
|
||||
super();
|
||||
}
|
||||
|
@ -216,6 +222,10 @@ public class TermConcept implements Serializable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public void setIndexStatus(Long theIndexStatus) {
|
||||
myIndexStatus = theIndexStatus;
|
||||
}
|
||||
|
||||
public void setParentPids(Set<Long> theParentPids) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
for (Long next : theParentPids) {
|
||||
|
|
|
@ -33,17 +33,16 @@ import org.hl7.fhir.dstu3.model.DecimalType;
|
|||
import org.hl7.fhir.dstu3.model.IntegerType;
|
||||
import org.hl7.fhir.dstu3.model.Meta;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl.Suggestion;
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
|
||||
import ca.uhn.fhir.model.api.annotation.Description;
|
||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||
|
@ -63,99 +62,99 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProvider<Bundle, Meta>
|
|||
|
||||
//@formatter:off
|
||||
// This is generated by hand:
|
||||
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerDt.class, min=0, max=1),/"
|
||||
// ls hapi-fhir-structures-dstu2/target/generated-sources/tinder/ca/uhn/fhir/model/dstu2/resource/ | sort | sed "s/.java//" | sed "s/^/@OperationParam(name=\"/" | sed "s/$/\", type=IntegerType.class, min=0, max=1),/"
|
||||
@Operation(name="$get-resource-counts", idempotent=true, returnParameters= {
|
||||
@OperationParam(name="AllergyIntolerance", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Appointment", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="AppointmentResponse", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="AuditEvent", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Basic", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Binary", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="BodySite", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Bundle", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="CarePlan", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="CarePlan2", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Claim", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ClaimResponse", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ClinicalImpression", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Communication", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="CommunicationRequest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Composition", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ConceptMap", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Condition", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Conformance", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Contract", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Contraindication", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Coverage", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DataElement", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Device", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceComponent", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceMetric", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceUseRequest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceUseStatement", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DiagnosticOrder", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DiagnosticReport", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DocumentManifest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="DocumentReference", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="EligibilityRequest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="EligibilityResponse", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Encounter", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="EnrollmentRequest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="EnrollmentResponse", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="EpisodeOfCare", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ExplanationOfBenefit", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="FamilyMemberHistory", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Flag", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Goal", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Group", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="HealthcareService", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ImagingObjectSelection", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ImagingStudy", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Immunization", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ImmunizationRecommendation", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ListResource", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Location", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Media", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Medication", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationAdministration", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationDispense", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationPrescription", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationStatement", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="MessageHeader", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="NamingSystem", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="NutritionOrder", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Observation", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="OperationDefinition", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="OperationOutcome", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Order", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="OrderResponse", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Organization", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Parameters", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Patient", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="PaymentNotice", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="PaymentReconciliation", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Person", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Practitioner", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Procedure", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ProcedureRequest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ProcessRequest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ProcessResponse", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Provenance", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Questionnaire", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="QuestionnaireAnswers", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ReferralRequest", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="RelatedPerson", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="RiskAssessment", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Schedule", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="SearchParameter", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Slot", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Specimen", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="StructureDefinition", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Subscription", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Substance", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="Supply", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="ValueSet", type=IntegerDt.class, min=0, max=1),
|
||||
@OperationParam(name="VisionPrescription", type=IntegerDt.class, min=0, max=1)
|
||||
@OperationParam(name="AllergyIntolerance", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Appointment", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="AppointmentResponse", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="AuditEvent", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Basic", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Binary", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="BodySite", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Bundle", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="CarePlan", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="CarePlan2", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Claim", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ClaimResponse", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ClinicalImpression", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Communication", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="CommunicationRequest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Composition", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ConceptMap", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Condition", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Conformance", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Contract", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Contraindication", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Coverage", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DataElement", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Device", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceComponent", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceMetric", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceUseRequest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DeviceUseStatement", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DiagnosticOrder", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DiagnosticReport", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DocumentManifest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="DocumentReference", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="EligibilityRequest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="EligibilityResponse", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Encounter", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="EnrollmentRequest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="EnrollmentResponse", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="EpisodeOfCare", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ExplanationOfBenefit", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="FamilyMemberHistory", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Flag", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Goal", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Group", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="HealthcareService", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ImagingObjectSelection", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ImagingStudy", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Immunization", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ImmunizationRecommendation", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ListResource", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Location", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Media", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Medication", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationAdministration", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationDispense", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationPrescription", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="MedicationStatement", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="MessageHeader", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="NamingSystem", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="NutritionOrder", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Observation", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="OperationDefinition", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="OperationOutcome", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Order", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="OrderResponse", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Organization", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Parameters", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Patient", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="PaymentNotice", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="PaymentReconciliation", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Person", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Practitioner", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Procedure", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ProcedureRequest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ProcessRequest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ProcessResponse", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Provenance", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Questionnaire", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="QuestionnaireAnswers", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ReferralRequest", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="RelatedPerson", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="RiskAssessment", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Schedule", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="SearchParameter", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Slot", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Specimen", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="StructureDefinition", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Subscription", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Substance", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="Supply", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="ValueSet", type=IntegerType.class, min=0, max=1),
|
||||
@OperationParam(name="VisionPrescription", type=IntegerType.class, min=0, max=1)
|
||||
})
|
||||
@Description(shortDefinition="Provides the number of resources currently stored on the server, broken down by resource type")
|
||||
//@formatter:on
|
||||
|
@ -173,7 +172,7 @@ public class JpaSystemProviderDstu3 extends BaseJpaSystemProvider<Bundle, Meta>
|
|||
|
||||
//@formatter:off
|
||||
@Operation(name="$mark-all-resources-for-reindexing", idempotent=true, returnParameters= {
|
||||
@OperationParam(name="count", type=IntegerDt.class)
|
||||
@OperationParam(name="count", type=IntegerType.class)
|
||||
})
|
||||
//@formatter:on
|
||||
public Parameters markAllResourcesForReindexing() {
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import com.google.common.base.Stopwatch;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
|
||||
|
@ -209,10 +210,13 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
|
||||
if (theConceptsStack.size() % 1000 == 0) {
|
||||
float pct = (float) theConceptsStack.size() / (float) theTotalConcepts;
|
||||
ourLog.info("Have saved {}/{} concepts - {}%", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f));
|
||||
ourLog.info("Have saved {}/{} concepts ({}%), flushing", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f));
|
||||
myConceptDao.flush();
|
||||
myConceptParentChildLinkDao.flush();
|
||||
}
|
||||
|
||||
theConcept.setCodeSystem(theCodeSystem);
|
||||
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
|
||||
|
||||
myConceptDao.save(theConcept);
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
|
|
|
@ -98,7 +98,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
b.append("Removing circular reference code ");
|
||||
b.append(nextChild.getCode());
|
||||
b.append(" from parent ");
|
||||
b.append(nextChild.getCode());
|
||||
b.append(next.getParent().getCode());
|
||||
b.append(". Chain was: ");
|
||||
for (String nextInChain : theChain) {
|
||||
TermConcept nextCode = theCode2concept.get(nextInChain);
|
||||
|
@ -187,7 +187,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
|
||||
private void iterateOverZipFile(Map<String, File> theFilenameToFile, String fileNamePart, IRecordHandler handler, char theDelimiter, QuoteMode theQuoteMode) {
|
||||
boolean found = false;
|
||||
for (Entry<String, File> nextEntry : theFilenameToFile.entrySet()) {
|
||||
for (Entry<String, File> nextEntry : new HashMap<String, File>(theFilenameToFile).entrySet()) {
|
||||
|
||||
if (nextEntry.getKey().contains(fileNamePart)) {
|
||||
ourLog.info("Processing file {}", nextEntry.getKey());
|
||||
|
@ -217,6 +217,11 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
nextLoggedCount += logIncrement;
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Deleting temporary file: {}", nextEntry.getValue());
|
||||
nextEntry.getValue().delete();
|
||||
theFilenameToFile.remove(nextEntry.getKey());
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
} finally {
|
||||
|
@ -415,6 +420,7 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
private final class SctHandlerConcept implements IRecordHandler {
|
||||
|
||||
private Set<String> myValidConceptIds;
|
||||
private Map<String, String> myConceptIdToMostRecentDate = new HashMap<String, String>();
|
||||
|
||||
public SctHandlerConcept(Set<String> theValidConceptIds) {
|
||||
myValidConceptIds = theValidConceptIds;
|
||||
|
@ -423,11 +429,18 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
@Override
|
||||
public void accept(CSVRecord theRecord) {
|
||||
String id = theRecord.get("id");
|
||||
boolean active = "1".equals(theRecord.get("active"));
|
||||
if (!active) {
|
||||
return;
|
||||
String date = theRecord.get("effectiveTime");
|
||||
|
||||
if (!myConceptIdToMostRecentDate.containsKey(id) || myConceptIdToMostRecentDate.get(id).compareTo(date) < 0) {
|
||||
boolean active = "1".equals(theRecord.get("active"));
|
||||
if (active) {
|
||||
myValidConceptIds.add(id);
|
||||
} else {
|
||||
myValidConceptIds.remove(id);
|
||||
}
|
||||
myConceptIdToMostRecentDate.put(id, date);
|
||||
}
|
||||
myValidConceptIds.add(id);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -496,34 +509,29 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
if (!active) {
|
||||
return;
|
||||
}
|
||||
TermConcept typeConcept = findConcept(myCode2concept, typeId);
|
||||
TermConcept sourceConcept = findConcept(myCode2concept, sourceId);
|
||||
TermConcept targetConcept = findConcept(myCode2concept, destinationId);
|
||||
if (typeConcept.getDisplay().equals("Is a (attribute)")) {
|
||||
if (!sourceId.equals(destinationId)) {
|
||||
TermConceptParentChildLink link = new TermConceptParentChildLink();
|
||||
link.setChild(sourceConcept);
|
||||
link.setParent(targetConcept);
|
||||
link.setRelationshipType(TermConceptParentChildLink.RelationshipTypeEnum.ISA);
|
||||
link.setCodeSystem(myCodeSystemVersion);
|
||||
myRootConcepts.remove(link.getChild().getCode());
|
||||
TermConcept typeConcept = myCode2concept.get(typeId);
|
||||
TermConcept sourceConcept = myCode2concept.get(sourceId);
|
||||
TermConcept targetConcept = myCode2concept.get(destinationId);
|
||||
if (sourceConcept != null && targetConcept != null && typeConcept != null) {
|
||||
if (typeConcept.getDisplay().equals("Is a (attribute)")) {
|
||||
if (!sourceId.equals(destinationId)) {
|
||||
TermConceptParentChildLink link = new TermConceptParentChildLink();
|
||||
link.setChild(sourceConcept);
|
||||
link.setParent(targetConcept);
|
||||
link.setRelationshipType(TermConceptParentChildLink.RelationshipTypeEnum.ISA);
|
||||
link.setCodeSystem(myCodeSystemVersion);
|
||||
myRootConcepts.remove(link.getChild().getCode());
|
||||
|
||||
targetConcept.addChild(sourceConcept, RelationshipTypeEnum.ISA);
|
||||
targetConcept.addChild(sourceConcept, RelationshipTypeEnum.ISA);
|
||||
}
|
||||
} else if (ignoredTypes.contains(typeConcept.getDisplay())) {
|
||||
// ignore
|
||||
} else {
|
||||
// ourLog.warn("Unknown relationship type: {}/{}", typeId, typeConcept.getDisplay());
|
||||
}
|
||||
} else if (ignoredTypes.contains(typeConcept.getDisplay())) {
|
||||
// ignore
|
||||
} else {
|
||||
// ourLog.warn("Unknown relationship type: {}/{}", typeId, typeConcept.getDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
private TermConcept findConcept(final Map<String, TermConcept> code2concept, String typeId) {
|
||||
TermConcept typeConcept = code2concept.get(typeId);
|
||||
if (typeConcept == null) {
|
||||
throw new InternalErrorException("Unknown type ID: " + typeId);
|
||||
}
|
||||
return typeConcept;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SinkOutputStream extends OutputStream {
|
||||
|
|
|
@ -857,7 +857,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
@Test
|
||||
public void testDocumentManifestResources() throws Exception {
|
||||
myFhirCtx.getResourceDefinition(Practitioner.class);
|
||||
myFhirCtx.getResourceDefinition(ca.uhn.fhir.model.dstu.resource.DocumentManifest.class);
|
||||
myFhirCtx.getResourceDefinition(DocumentManifest.class);
|
||||
|
||||
IGenericClient client = ourClient;
|
||||
|
||||
|
|
|
@ -76,28 +76,25 @@ public class ResourceProviderDstu3ValueSetTest extends BaseResourceProviderDstu3
|
|||
assertThat(resp, containsString("</contains>"));
|
||||
assertThat(resp, containsString("</expansion>"));
|
||||
|
||||
/*
|
||||
* Filter with display name
|
||||
*/
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandByIdWithFilter() throws IOException {
|
||||
|
||||
//@formatter:off
|
||||
respParam = ourClient
|
||||
Parameters respParam = ourClient
|
||||
.operation()
|
||||
.onInstance(myExtensionalVsId)
|
||||
.named("expand")
|
||||
.withParameter(Parameters.class, "filter", new StringType("systolic"))
|
||||
.withParameter(Parameters.class, "filter", new StringType("first"))
|
||||
.execute();
|
||||
expanded = (ValueSet) respParam.getParameter().get(0).getResource();
|
||||
ValueSet expanded = (ValueSet) respParam.getParameter().get(0).getResource();
|
||||
//@formatter:on
|
||||
|
||||
expanded = myValueSetDao.expand(myExtensionalVsId, ("systolic"));
|
||||
resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded);
|
||||
ourLog.info(resp);
|
||||
//@formatter:off
|
||||
assertThat(resp, stringContainsInOrder(
|
||||
"<code value=\"11378-7\"/>",
|
||||
"<display value=\"Systolic blood pressure at First encounter\"/>"));
|
||||
//@formatter:on
|
||||
assertThat(resp, containsString("<display value=\"Systolic blood pressure at First encounter\"/>"));
|
||||
assertThat(resp, not(containsString("<display value=\"Systolic blood pressure--expiration\"/>")));
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
package ca.uhn.fhir.jpa.term;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInRelativeOrder;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.TreeSet;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
|
@ -14,22 +22,33 @@ import org.apache.commons.io.IOUtils;
|
|||
import org.apache.commons.lang3.Validate;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TerminologyLoaderSvcTest {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyLoaderSvcTest.class);
|
||||
private TerminologyLoaderSvc mySvc;
|
||||
|
||||
@Mock
|
||||
private IHapiTerminologySvc myTermSvc;
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<TermCodeSystemVersion> myCsvCaptor;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myTermSvc = mock(IHapiTerminologySvc.class);
|
||||
|
||||
mySvc = new TerminologyLoaderSvc();
|
||||
mySvc.setTermSvcForUnitTests(myTermSvc);
|
||||
}
|
||||
|
@ -74,6 +93,30 @@ public class TerminologyLoaderSvcTest {
|
|||
|
||||
RequestDetails details = mock(RequestDetails.class);
|
||||
mySvc.loadSnomedCt(Collections.singletonList(bos.toByteArray()), details);
|
||||
|
||||
verify(myTermSvc).storeNewCodeSystemVersion(any(String.class), myCsvCaptor.capture(), any(RequestDetails.class));
|
||||
|
||||
TermCodeSystemVersion csv = myCsvCaptor.getValue();
|
||||
TreeSet<String> allCodes = toCodes(csv);
|
||||
ourLog.info(allCodes.toString());
|
||||
|
||||
assertThat(allCodes, containsInRelativeOrder("116680003"));
|
||||
assertThat(allCodes, not(containsInRelativeOrder("207527008")));
|
||||
}
|
||||
|
||||
private TreeSet<String> toCodes(TermCodeSystemVersion theCsv) {
|
||||
TreeSet<String> retVal = new TreeSet<String>();
|
||||
for (TermConcept next : theCsv.getConcepts()) {
|
||||
toCodes(retVal, next);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void toCodes(TreeSet<String> theCodes, TermConcept theConcept) {
|
||||
theCodes.add(theConcept.getCode());
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
toCodes(theCodes, next.getChild());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.term;
|
|||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -102,6 +103,15 @@ public class TerminologySvcImplTest extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReindexTerminology() {
|
||||
IIdType id = createCodeSystem();
|
||||
|
||||
assertThat(mySystemDao.markAllResourcesForReindexing(), greaterThan(0));
|
||||
|
||||
assertThat(mySystemDao.performReindexingPass(100, mySrd), greaterThan(0));
|
||||
}
|
||||
|
||||
private IIdType createCodeSystem() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
|
|
|
@ -13,3 +13,6 @@ id effectiveTime active moduleId definitionStatusId
|
|||
126813005 20020131 1 900000000000207008 900000000000074008
|
||||
126813006 20020131 1 900000000000207008 900000000000074008
|
||||
126817006 20020131 1 900000000000207008 900000000000074008
|
||||
207527008 20020131 1 900000000000207008 900000000000074008
|
||||
207527008 20040731 1 900000000000207008 900000000000073002
|
||||
207527008 20090731 0 900000000000207008 900000000000074008
|
||||
|
|
|
@ -9,3 +9,12 @@ id effectiveTime active moduleId conceptId languageCode typeId term caseSignific
|
|||
108019 20020131 1 900000000000207008 126820003 en 900000000000013009 Neoplasm of abdominal esophagus 900000000000020002
|
||||
110017 20020131 1 900000000000207008 126822006 en 900000000000013009 Neoplasm of middle third of esophagus 900000000000020002
|
||||
181114011 20020131 1 900000000000207008 116680003 en 900000000000013009 Is a (attribute) 900000000000020002
|
||||
317927012 20020131 1 900000000000207008 207527008 en 900000000000013009 [D]Old age 900000000000017005
|
||||
317927012 20080731 1 900000000000207008 207527008 en 900000000000013009 [D]Old age 900000000000020002
|
||||
317927012 20090731 1 900000000000207008 207527008 en 900000000000013009 [D]Old age 900000000000017005
|
||||
593143017 20020131 1 900000000000207008 207527008 en 900000000000003001 [D]Old age (context-dependent category) 900000000000020002
|
||||
593143017 20030731 1 900000000000207008 207527008 en 900000000000003001 [D]Old age (context-dependent category) 900000000000017005
|
||||
593143017 20060731 0 900000000000207008 207527008 en 900000000000003001 [D]Old age (context-dependent category) 900000000000017005
|
||||
1222549013 20020731 1 900000000000207008 207527008 en 900000000000013009 [D]Senility 900000000000020002
|
||||
2608974012 20060731 1 900000000000207008 207527008 en 900000000000003001 [D]Old age (situation) 900000000000020002
|
||||
2608974012 20080731 1 900000000000207008 207527008 en 900000000000003001 [D]Old age (situation) 900000000000017005
|
||||
|
|
|
@ -67,7 +67,7 @@ public class ServerProfileProvider implements IResourceProvider {
|
|||
@Search()
|
||||
public List<Profile> getAllProfiles(HttpServletRequest theRequest) {
|
||||
final String serverBase = getServerBase(theRequest);
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitions());
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitionsWithExplicitId());
|
||||
Collections.sort(defs, new Comparator<RuntimeResourceDefinition>() {
|
||||
@Override
|
||||
public int compare(RuntimeResourceDefinition theO1, RuntimeResourceDefinition theO2) {
|
||||
|
|
|
@ -953,9 +953,15 @@ public class RestfulServerMethodTest {
|
|||
@Test
|
||||
public void testServerProfileProviderFindsProfiles() {
|
||||
ServerProfileProvider profileProvider = (ServerProfileProvider)ourRestfulServer.getServerProfilesProvider();
|
||||
|
||||
IdDt id = new IdDt("Profile", "observation");
|
||||
Profile profile = profileProvider.getProfileById(createHttpServletRequest(), id);
|
||||
assertNull(profile);
|
||||
|
||||
id = new IdDt("Profile", "patient");
|
||||
profile = profileProvider.getProfileById(createHttpServletRequest(), id);
|
||||
assertNotNull(profile);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -66,7 +66,7 @@ public class ServerProfileProvider implements IResourceProvider {
|
|||
@Search()
|
||||
public List<StructureDefinition> getAllProfiles(HttpServletRequest theRequest) {
|
||||
final String serverBase = getServerBase(theRequest);
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitions());
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitionsWithExplicitId());
|
||||
Collections.sort(defs, new Comparator<RuntimeResourceDefinition>() {
|
||||
@Override
|
||||
public int compare(RuntimeResourceDefinition theO1, RuntimeResourceDefinition theO2) {
|
||||
|
|
|
@ -67,7 +67,7 @@ public class ServerProfileProvider implements IResourceProvider {
|
|||
@Search()
|
||||
public List<StructureDefinition> getAllProfiles(HttpServletRequest theRequest) {
|
||||
final String serverBase = getServerBase(theRequest);
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitions());
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitionsWithExplicitId());
|
||||
Collections.sort(defs, new Comparator<RuntimeResourceDefinition>() {
|
||||
@Override
|
||||
public int compare(RuntimeResourceDefinition theO1, RuntimeResourceDefinition theO2) {
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
package ca.uhn.fhir.context;
|
||||
|
||||
import static org.hamcrest.Matchers.containsInRelativeOrder;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class ContextScanningDstu3Test {
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ContextScanningDstu3Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
|
||||
@Test
|
||||
public void testContextDoesntScanUnneccesaryTypes() {
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
|
||||
TreeSet<String> resDefs = scannedResourceNames(ctx);
|
||||
TreeSet<String> elementDefs = scannedElementNames(ctx);
|
||||
ourLog.info("Have {} resource definitions: {}", ctx.getAllResourceDefinitions().size(), resDefs);
|
||||
ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs);
|
||||
assertThat(resDefs, not(containsInRelativeOrder("Observation")));
|
||||
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
|
||||
resDefs = scannedResourceNames(ctx);
|
||||
elementDefs = scannedElementNames(ctx);
|
||||
ourLog.info("Have {} resource definitions: {}", ctx.getAllResourceDefinitions().size(), resDefs);
|
||||
ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs);
|
||||
assertThat(resDefs, not(containsInRelativeOrder("Observation")));
|
||||
|
||||
client.read().resource(Observation.class).withId("1").execute();
|
||||
resDefs = scannedResourceNames(ctx);
|
||||
elementDefs = scannedElementNames(ctx);
|
||||
ourLog.info("Have {} resource definitions: {}", ctx.getAllResourceDefinitions().size(), resDefs);
|
||||
ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs);
|
||||
assertThat(resDefs, containsInRelativeOrder("Observation"));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// 1.6 - no defer - Took 6700 ms - 6.7ms / pass
|
||||
// 1.6 - no defer - Took 6127 ms - 6.127ms / pass
|
||||
|
||||
// 1.6 - defer - Took 2523 ms - 2.523ms / pass
|
||||
// 1.6 - defer - Took 2328 ms - 2.328ms / pass
|
||||
|
||||
int passes = 1000;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
for (int i = 0; i < passes; i++) {
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
ctx.setPerformanceOptions(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING);
|
||||
ctx.getResourceDefinition("Observation");
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
long delay = end-start;
|
||||
float per = (float)delay / (float)passes;
|
||||
|
||||
ourLog.info("Took {} ms - {}ms / pass", delay, per);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeferredScanning() {
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
ctx.getRestfulClientFactory().setSocketTimeout(600000);
|
||||
|
||||
ctx.setPerformanceOptions(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING);
|
||||
|
||||
TreeSet<String> resDefs = scannedResourceNames(ctx);
|
||||
TreeSet<String> elementDefs = scannedElementNames(ctx);
|
||||
ourLog.info("Have {} resource definitions: {}", ctx.getAllResourceDefinitions().size(), resDefs);
|
||||
ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs);
|
||||
assertThat(resDefs, not(containsInRelativeOrder("Observation")));
|
||||
|
||||
BaseRuntimeElementCompositeDefinition<?> compositeDef = (BaseRuntimeElementCompositeDefinition<?>) ctx.getElementDefinition("identifier");
|
||||
assertFalse(compositeDef.isSealed());
|
||||
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
|
||||
resDefs = scannedResourceNames(ctx);
|
||||
elementDefs = scannedElementNames(ctx);
|
||||
ourLog.info("Have {} resource definitions: {}", ctx.getAllResourceDefinitions().size(), resDefs);
|
||||
ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs);
|
||||
assertThat(resDefs, not(containsInRelativeOrder("Observation")));
|
||||
compositeDef = (BaseRuntimeElementCompositeDefinition<?>) ctx.getElementDefinition("identifier");
|
||||
assertFalse(compositeDef.isSealed());
|
||||
|
||||
client.read().resource(Observation.class).withId("1").execute();
|
||||
resDefs = scannedResourceNames(ctx);
|
||||
elementDefs = scannedElementNames(ctx);
|
||||
ourLog.info("Have {} resource definitions: {}", ctx.getAllResourceDefinitions().size(), resDefs);
|
||||
ourLog.info("Have {} element definitions: {}", ctx.getElementDefinitions().size(), elementDefs);
|
||||
assertThat(resDefs, containsInRelativeOrder("Observation"));
|
||||
compositeDef = (BaseRuntimeElementCompositeDefinition<?>) ctx.getElementDefinition("identifier");
|
||||
assertTrue(compositeDef.isSealed());
|
||||
}
|
||||
|
||||
private TreeSet<String> scannedResourceNames(FhirContext ctx) {
|
||||
TreeSet<String> defs = new TreeSet<String>();
|
||||
for (RuntimeResourceDefinition next : ctx.getAllResourceDefinitions()) {
|
||||
defs.add(next.getName());
|
||||
}
|
||||
return defs;
|
||||
}
|
||||
|
||||
private TreeSet<String> scannedElementNames(FhirContext ctx) {
|
||||
TreeSet<String> defs = new TreeSet<String>();
|
||||
for (BaseRuntimeElementDefinition<?> next : ctx.getElementDefinitions()) {
|
||||
defs.add(next.getName());
|
||||
}
|
||||
return defs;
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setResourceProviders(new PatientProvider(), new ObservationProvider());
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
}
|
||||
|
||||
public static class PatientProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Read
|
||||
public Patient read(@IdParam IdType theId) {
|
||||
return (Patient) new Patient().setId(theId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class ObservationProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<Observation> getResourceType() {
|
||||
return Observation.class;
|
||||
}
|
||||
|
||||
@Read
|
||||
public Observation read(@IdParam IdType theId) {
|
||||
Observation retVal = new Observation();
|
||||
retVal.setId(theId);
|
||||
retVal.addIdentifier().setSystem("ISYS").setValue("IVAL");
|
||||
retVal.setStatus(ObservationStatus.FINAL);
|
||||
retVal.setValue(new StringType("VAL"));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -67,7 +67,7 @@ public class ServerProfileProvider implements IResourceProvider {
|
|||
@Search()
|
||||
public List<StructureDefinition> getAllProfiles(HttpServletRequest theRequest) {
|
||||
final String serverBase = getServerBase(theRequest);
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitions());
|
||||
List<RuntimeResourceDefinition> defs = new ArrayList<RuntimeResourceDefinition>(myContext.getResourceDefinitionsWithExplicitId());
|
||||
Collections.sort(defs, new Comparator<RuntimeResourceDefinition>() {
|
||||
@Override
|
||||
public int compare(RuntimeResourceDefinition theO1, RuntimeResourceDefinition theO2) {
|
||||
|
|
|
@ -126,6 +126,16 @@ datatype.unsignedInt=org.hl7.fhir.instance.model.UnsignedIntType
|
|||
datatype.uri=org.hl7.fhir.instance.model.UriType
|
||||
#datatype.xhtml=org.hl7.fhir.instance.model.XhtmlType
|
||||
|
||||
datatype.SimpleQuantity=org.hl7.fhir.instance.model.SimpleQuantity
|
||||
datatype.Age=org.hl7.fhir.instance.model.Age
|
||||
datatype.Count=org.hl7.fhir.instance.model.Count
|
||||
datatype.Distance=org.hl7.fhir.instance.model.Distance
|
||||
datatype.Duration=org.hl7.fhir.instance.model.Duration
|
||||
datatype.Money=org.hl7.fhir.instance.model.Money
|
||||
|
||||
datatype.xhtmlNode=org.hl7.fhir.instance.utilities.xhtml.XhtmlNode
|
||||
datatype.narrative=org.hl7.fhir.instance.model.Narrative
|
||||
datatype.markdown=org.hl7.fhir.instance.model.MarkdownType
|
||||
datatype.Extension=org.hl7.fhir.instance.model.Extension
|
||||
datatype.reference=org.hl7.fhir.instance.model.Reference
|
||||
datatype.enumeration=org.hl7.fhir.instance.model.Enumeration
|
|
@ -27,6 +27,7 @@ import org.apache.velocity.app.VelocityEngine;
|
|||
import org.apache.velocity.tools.generic.EscapeTool;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.tinder.parser.BaseStructureSpreadsheetParser;
|
||||
import ca.uhn.fhir.tinder.parser.ResourceGeneratorUsingSpreadsheet;
|
||||
|
||||
|
@ -104,6 +105,15 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
|
|||
baseResourceNames.add(next.substring("resource.".length()).toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No spreadsheet existed for Binary in DSTU1 so we don't generate it.. this
|
||||
* is something we could work around, but at this point why bother since it's
|
||||
* only an issue for DSTU1
|
||||
*/
|
||||
if (fhirContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
|
||||
baseResourceNames.remove("binary");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < baseResourceNames.size(); i++) {
|
||||
|
|
|
@ -38,6 +38,8 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Binary;
|
||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
import ca.uhn.fhir.model.primitive.BoundCodeableConceptDt;
|
||||
import ca.uhn.fhir.tinder.TinderStructuresMojo;
|
||||
import ca.uhn.fhir.tinder.ValueSetGenerator;
|
||||
import ca.uhn.fhir.tinder.model.BaseElement;
|
||||
|
@ -564,6 +566,14 @@ public abstract class BaseStructureParser {
|
|||
myNameToResourceClass.put("Binary", thePackageBase + ".resource.Binary");
|
||||
myNameToDatatypeClass.put("Extension", ExtensionDt.class.getName());
|
||||
|
||||
if (determineVersionEnum() == FhirVersionEnum.DSTU1) {
|
||||
myNameToDatatypeClass.put("boundCode", BoundCodeDt.class.getName());
|
||||
myNameToDatatypeClass.put("boundCodeableConcept", BoundCodeableConceptDt.class.getName());
|
||||
} else if (determineVersionEnum() == FhirVersionEnum.DSTU2) {
|
||||
myNameToDatatypeClass.put("boundCode", BoundCodeDt.class.getName());
|
||||
myNameToDatatypeClass.put("boundCodeableConcept", ca.uhn.fhir.model.dstu2.composite.BoundCodeableConceptDt.class.getName());
|
||||
}
|
||||
|
||||
try {
|
||||
File versionFile = new File(theResourceOutputDirectory, "fhirversion.properties");
|
||||
OutputStreamWriter w = new OutputStreamWriter(new FileOutputStream(versionFile, false), "UTF-8");
|
||||
|
|
|
@ -6,6 +6,7 @@ resource.AllergyIntolerance=ca.uhn.fhir.model.dstu.resource.AllergyIntolerance
|
|||
resource.Appointment=ca.uhn.fhir.model.dstu.resource.Appointment
|
||||
resource.AppointmentResponse=ca.uhn.fhir.model.dstu.resource.AppointmentResponse
|
||||
resource.Availability=ca.uhn.fhir.model.dstu.resource.Availability
|
||||
resource.Binary=ca.uhn.fhir.model.dstu.resource.Binary
|
||||
resource.CarePlan=ca.uhn.fhir.model.dstu.resource.CarePlan
|
||||
resource.Claim=ca.uhn.fhir.model.dstu.resource.Claim
|
||||
resource.Composition=ca.uhn.fhir.model.dstu.resource.Composition
|
||||
|
@ -114,3 +115,6 @@ datatype.time=ca.uhn.fhir.model.primitive.TimeDt
|
|||
datatype.unsignedInt=ca.uhn.fhir.model.primitive.UnsignedIntDt
|
||||
datatype.uri=ca.uhn.fhir.model.primitive.UriDt
|
||||
datatype.xhtml=ca.uhn.fhir.model.primitive.XhtmlDt
|
||||
|
||||
datatype.boundCode=ca.uhn.fhir.model.primitive.BoundCodeDt
|
||||
datatype.boundCodeableConcept=ca.uhn.fhir.model.primitive.BoundCodeableConceptDt
|
||||
|
|
|
@ -160,3 +160,6 @@ datatype.time=ca.uhn.fhir.model.primitive.TimeDt
|
|||
datatype.unsignedInt=ca.uhn.fhir.model.primitive.UnsignedIntDt
|
||||
datatype.uri=ca.uhn.fhir.model.primitive.UriDt
|
||||
datatype.xhtml=ca.uhn.fhir.model.primitive.XhtmlDt
|
||||
|
||||
datatype.boundCode=ca.uhn.fhir.model.primitive.BoundCodeDt
|
||||
datatype.boundCodeableConcept=ca.uhn.fhir.model.dstu2.composite.BoundCodeableConceptDt
|
||||
|
|
|
@ -7,6 +7,29 @@
|
|||
</properties>
|
||||
<body>
|
||||
<release version="1.6" date="TBD">
|
||||
<action type="fix">
|
||||
Performance has been improved for the initial FhirContext
|
||||
object creation by avoiding a lot of unnecessary reflection. HAPI FHIR
|
||||
1.5 had a regression compared to previous releases
|
||||
and this has been corrected, but other improvements have been
|
||||
made so that this release is faster than previous releases too.
|
||||
<![CDATA[<br/><br/>]]>
|
||||
In addition, a new "deferred scan" mode has been implemented for
|
||||
even faster initialization on slower environments (e.g. Android).
|
||||
See the <![CDATA[<a href="./doc_rest_client_http_config.html#performance">performance documentation</a>]]>
|
||||
for more information.
|
||||
<![CDATA[<br/><br/>]]>
|
||||
The following shows our benchmarks for context initialization across several
|
||||
versions of HAPI:
|
||||
<![CDATA[
|
||||
<ul>
|
||||
<li>Version 1.4: <b>560ms</b></li>
|
||||
<li>Version 1.5: <b>800ms</b></li>
|
||||
<li>Version 1.6: <b>340ms</b></li>
|
||||
<li>Version 1.6 (deferred mode): <b>240ms</b></li>
|
||||
</ul>
|
||||
]]>
|
||||
</action>
|
||||
<action type="add">
|
||||
Bump the version of a few dependencies to the
|
||||
latest versions (dependent HAPI modules listed in brackets):
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
<item name="Fluent/Generic Client" href="./doc_rest_client.html" />
|
||||
<item name="Annotation Client" href="./doc_rest_client_annotation.html" />
|
||||
<item name="Interceptors (client)" href="./doc_rest_client_interceptor.html"/>
|
||||
<item name="Client HTTP Configuration" href="./doc_rest_client_http_config.html"/>
|
||||
<item name="Client Configuration" href="./doc_rest_client_http_config.html"/>
|
||||
<item name="Client Examples" href="./doc_rest_client_examples.html"/>
|
||||
<item name="JAX-RS Client & Alternate HTTP Providers" href="./doc_rest_client_alternate_provider.html"/>
|
||||
</item>
|
||||
|
|
|
@ -59,6 +59,16 @@
|
|||
</subsection>
|
||||
</section>
|
||||
|
||||
<section name="Performance">
|
||||
<p>
|
||||
On mobile devices, performance problems are particularly noticeable. This
|
||||
is made worse by the fact that some Android devices have much slower performance
|
||||
than modern desktop computers. See the
|
||||
<a href="./doc_rest_client_http_config.html#performance">Client Configuration Performance</a>
|
||||
page for some tips on how to improve client performance.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
|
||||
</document>
|
||||
|
|
|
@ -3,11 +3,75 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">
|
||||
|
||||
<properties>
|
||||
<title>Client HTTP Configuration</title>
|
||||
<title>Client Configuration</title>
|
||||
<author email="jamesagnew@users.sourceforge.net">James Agnew</author>
|
||||
</properties>
|
||||
|
||||
<body>
|
||||
<section name="Configuring Client Behaviour">
|
||||
|
||||
<p>
|
||||
This page outlines ways that the client can be configured
|
||||
for specific behaviour.
|
||||
</p>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<a name="performance"/>
|
||||
<section name="Performance">
|
||||
|
||||
<subsection name="Server Conformance Check">
|
||||
<p>
|
||||
By default, the client will query the server before the very first
|
||||
operation to download the server's conformance/metadata statement
|
||||
and verify that the server is appropriate for the given client.
|
||||
This check is only done once per server endpoint for a given
|
||||
FhirContext.
|
||||
</p>
|
||||
<p>
|
||||
This check is useful to prevent bugs or unexpected behaviour
|
||||
when talking to servers. It may introduce unneccesary overhead
|
||||
however in circumstances where the client and server are known
|
||||
to be compatible. The following example shows how to disable this
|
||||
check.
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="dontValidate" />
|
||||
<param name="file" value="examples/src/main/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
</subsection>
|
||||
|
||||
<subsection name="Deferred Model Scanning">
|
||||
|
||||
<p>
|
||||
By default, HAPI will scan each model type it encounters
|
||||
as soon as it encounters it. This scan includes a check for
|
||||
all fields within the type, and makes use of reflection to do this.
|
||||
</p>
|
||||
<p>
|
||||
While this process is not particularly significant on reasonably
|
||||
performant machines (current benchmarks show that this takes
|
||||
roughly 6 seconds on one developer workstation), on some devices
|
||||
(e.g. Android phones where every millisecond counts)
|
||||
it may be desirable to defer this scan.
|
||||
</p>
|
||||
<p>
|
||||
When the scan is deferred, objects will only be scanned when they
|
||||
are actually accessed, meaning that only types that are
|
||||
actually used in an application get scanned.
|
||||
</p>
|
||||
<p>
|
||||
The following example shows how to defer model scanning:
|
||||
</p>
|
||||
<macro name="snippet">
|
||||
<param name="id" value="deferModelScanning" />
|
||||
<param name="file" value="examples/src/main/java/example/GenericClientExample.java" />
|
||||
</macro>
|
||||
|
||||
</subsection>
|
||||
|
||||
</section>
|
||||
|
||||
<section name="Configuring the HTTP Client">
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
<li><a href="./doc_rest_client.html">Fluent/Generic Client</a></li>
|
||||
<li><a href="./doc_rest_client_annotation.html">Annotation Client</a></li>
|
||||
<li><a href="./doc_rest_client_interceptor.html">Interceptors (client)</a></li>
|
||||
<li><a href="./doc_rest_client_http_config.html">Client HTTP Configuration</a></li>
|
||||
<li><a href="./doc_rest_client_http_config.html">Client Configuration</a></li>
|
||||
<li><a href="./doc_rest_client_examples.html">Client Examples</a></li>
|
||||
<li><a href="./doc_rest_client_alternate_provider.html">JAX-RS Client & Alternate HTTP Providers</a></li>
|
||||
</ul>
|
||||
|
|
Loading…
Reference in New Issue