diff --git a/.travis.yml b/.travis.yml
index 52908fa3d78..333caa4ff7f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,7 @@ jdk:
- oraclejdk9
env:
global:
- - MAVEN_OPTS="-Xmx1024m"
+ - MAVEN_OPTS="-Xmx10244M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC"
cache:
directories:
diff --git a/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml b/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml
index a4fd75c4e79..ae3a99a8ce8 100644
--- a/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml
+++ b/example-projects/hapi-fhir-base-example-embedded-ws/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhirhapi-fhir
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../../pom.xml
diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml b/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml
index 7c78473fadb..2f0027d243e 100644
--- a/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml
+++ b/example-projects/hapi-fhir-jpaserver-cds-example/pom.xml
@@ -10,7 +10,7 @@
ca.uhn.hapi.fhirhapi-fhir
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../../pom.xml
diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/java/ca/uhn/fhir/jpa/cds/example/CdsExampleTests.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/java/ca/uhn/fhir/jpa/cds/example/CdsExampleTests.java
index 2fc033c6b76..478accecb5b 100644
--- a/example-projects/hapi-fhir-jpaserver-cds-example/src/test/java/ca/uhn/fhir/jpa/cds/example/CdsExampleTests.java
+++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/test/java/ca/uhn/fhir/jpa/cds/example/CdsExampleTests.java
@@ -26,6 +26,8 @@ import java.util.Collection;
import java.util.List;
import java.util.Scanner;
+// FIXME KHS
+@Ignore
public class CdsExampleTests {
private static IGenericClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu3();
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml b/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml
index 94937eae01b..c49e749a221 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/pom.xml
@@ -10,7 +10,7 @@
ca.uhn.hapi.fhirhapi-fhir
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../../pom.xml
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index 35dcc65acec..67e43eb2910 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -54,7 +54,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
/**
* Configure FHIR properties around the the JPA server via this bean
*/
- @Bean()
+ @Bean
public DaoConfig daoConfig() {
return FhirServerConfigCommon.getDaoConfig();
}
@@ -71,7 +71,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
}
@Override
- @Bean()
+ @Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu3());
}
@@ -99,7 +99,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
return interceptor;
}
- @Bean()
+ @Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return FhirServerConfigCommon.getTransactionManager(entityManagerFactory);
}
diff --git a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
index c70b393e839..b35fdf54f99 100644
--- a/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
+++ b/example-projects/hapi-fhir-jpaserver-dynamic/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfigDstu2.java
@@ -57,7 +57,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
* Configure FHIR properties around the the JPA server via this bean
*/
@SuppressWarnings("deprecation")
- @Bean()
+ @Bean
public DaoConfig daoConfig() {
return FhirServerConfigCommon.getDaoConfig();
}
@@ -74,7 +74,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
}
@Override
- @Bean()
+ @Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
return FhirServerConfigCommon.getEntityManagerFactory(env, dataSource(), fhirContextDstu2());
}
@@ -103,7 +103,7 @@ public class FhirServerConfigDstu2 extends BaseJavaConfigDstu2 {
return interceptor;
}
- @Bean()
+ @Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
return FhirServerConfigCommon.getTransactionManager(entityManagerFactory);
}
diff --git a/example-projects/hapi-fhir-jpaserver-example-postgres/pom.xml b/example-projects/hapi-fhir-jpaserver-example-postgres/pom.xml
index 56bee813797..5ff0d7b9ef0 100644
--- a/example-projects/hapi-fhir-jpaserver-example-postgres/pom.xml
+++ b/example-projects/hapi-fhir-jpaserver-example-postgres/pom.xml
@@ -10,7 +10,7 @@
ca.uhn.hapi.fhirhapi-fhir
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../../pom.xml
diff --git a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
index c2f14354b5c..c5150eac986 100644
--- a/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
+++ b/example-projects/hapi-fhir-jpaserver-example-postgres/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java
@@ -37,7 +37,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
/**
* Configure FHIR properties around the the JPA server via this bean
*/
- @Bean()
+ @Bean
public DaoConfig daoConfig() {
DaoConfig retVal = new DaoConfig();
retVal.setSubscriptionEnabled(true);
@@ -64,13 +64,11 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
}
@Override
- @Bean()
+ @Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
- LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
+ LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
retVal.setPersistenceUnitName("HAPI_PU");
retVal.setDataSource(dataSource());
- retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
- retVal.setPersistenceProvider(new HibernatePersistenceProvider());
retVal.setJpaProperties(jpaProperties());
return retVal;
}
@@ -122,7 +120,7 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 {
return retVal;
}
- @Bean()
+ @Bean
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager retVal = new JpaTransactionManager();
retVal.setEntityManagerFactory(entityManagerFactory);
diff --git a/example-projects/hapi-fhir-standalone-overlay-example/pom.xml b/example-projects/hapi-fhir-standalone-overlay-example/pom.xml
index 47279a95647..ce0a7a13ce9 100644
--- a/example-projects/hapi-fhir-standalone-overlay-example/pom.xml
+++ b/example-projects/hapi-fhir-standalone-overlay-example/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhirhapi-fhir
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../../pom.xmlhapi-fhir-standalone-overlay-example
diff --git a/examples/pom.xml b/examples/pom.xml
index 3fa77b1ee6a..218c17630a5 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhirhapi-fhir
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../pom.xml
diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index cdb316bec57..054dde91604 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -4,7 +4,7 @@
ca.uhn.hapi.fhirhapi-fhir
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../pom.xml
diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml
index 16718b71b5c..6f00dafb504 100644
--- a/hapi-fhir-android/pom.xml
+++ b/hapi-fhir-android/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhirhapi-deployable-pom
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml
index a9654e28d99..bd368198eff 100644
--- a/hapi-fhir-base/pom.xml
+++ b/hapi-fhir-base/pom.xml
@@ -5,7 +5,7 @@
ca.uhn.hapi.fhirhapi-deployable-pom
- 3.6.0-SNAPSHOT
+ 3.7.0-SNAPSHOT../hapi-deployable-pom/pom.xml
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java
index 7465ee0ddbe..41fddc384e2 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementCompositeDefinition.java
@@ -73,12 +73,12 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseRuntimeElementCompositeDefinition.class);
private Map forcedOrder = null;
- private List myChildren = new ArrayList();
+ private List myChildren = new ArrayList<>();
private List myChildrenAndExtensions;
private Map, BaseRuntimeElementDefinition>> myClassToElementDefinitions;
- private FhirContext myContext;
- private Map myNameToChild = new HashMap();
- private List myScannedFields = new ArrayList();
+ private final FhirContext myContext;
+ private Map myNameToChild = new HashMap<>();
+ private List myScannedFields = new ArrayList<>();
private volatile boolean mySealed;
@SuppressWarnings("unchecked")
@@ -92,12 +92,12 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
* We scan classes for annotated fields in the class but also all of its superclasses
*/
Class extends IBase> current = theImplementingClass;
- LinkedList> classes = new LinkedList>();
+ LinkedList> classes = new LinkedList<>();
do {
if (forcedOrder == null) {
ChildOrder childOrder = current.getAnnotation(ChildOrder.class);
if (childOrder != null) {
- forcedOrder = new HashMap();
+ forcedOrder = new HashMap<>();
for (int i = 0; i < childOrder.names().length; i++) {
String nextName = childOrder.names()[i];
if (nextName.endsWith("[x]")) {
@@ -115,7 +115,7 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
}
} while (current != null);
- Set fields = new HashSet();
+ Set fields = new HashSet<>();
for (Class extends IBase> nextClass : classes) {
int fieldIndexInClass = 0;
for (Field next : nextClass.getDeclaredFields()) {
@@ -192,9 +192,9 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
}
private void scanCompositeElementForChildren() {
- Set elementNames = new HashSet();
- TreeMap orderToElementDef = new TreeMap();
- TreeMap orderToExtensionDef = new TreeMap();
+ Set elementNames = new HashSet<>();
+ TreeMap orderToElementDef = new TreeMap<>();
+ TreeMap orderToExtensionDef = new TreeMap<>();
scanCompositeElementForChildren(elementNames, orderToElementDef, orderToExtensionDef);
@@ -203,7 +203,7 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
* Find out how many elements don't match any entry in the list
* for forced order. Those elements come first.
*/
- TreeMap newOrderToExtensionDef = new TreeMap();
+ TreeMap newOrderToExtensionDef = new TreeMap<>();
int unknownCount = 0;
for (BaseRuntimeDeclaredChildDefinition nextEntry : orderToElementDef.values()) {
if (!forcedOrder.containsKey(nextEntry.getElementName())) {
@@ -220,7 +220,7 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
orderToElementDef = newOrderToExtensionDef;
}
- TreeSet orders = new TreeSet();
+ TreeSet orders = new TreeSet<>();
orders.addAll(orderToElementDef.keySet());
orders.addAll(orderToExtensionDef.keySet());
@@ -329,7 +329,7 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
* 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);
+ order = 0;
while (orderMap.containsKey(order)) {
order++;
}
@@ -386,7 +386,7 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
/*
* Child is a resource reference
*/
- List> refTypesList = new ArrayList>();
+ List> refTypesList = new ArrayList<>();
for (Class extends IElement> nextType : childAnnotation.type()) {
if (IBaseReference.class.isAssignableFrom(nextType)) {
refTypesList.add(myContext.getVersion().getVersion().isRi() ? IAnyResource.class : IResource.class);
@@ -469,10 +469,10 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
next.sealAndInitialize(theContext, theClassToElementDefinitions);
}
- myNameToChild = new HashMap();
+ myNameToChild = new HashMap<>();
for (BaseRuntimeChildDefinition next : myChildren) {
if (next instanceof RuntimeChildChoiceDefinition) {
- String key = ((RuntimeChildChoiceDefinition) next).getElementName()+"[x]";
+ String key = next.getElementName()+"[x]";
myNameToChild.put(key, next);
}
for (String nextName : next.getValidChildNames()) {
@@ -486,7 +486,7 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
myChildren = Collections.unmodifiableList(myChildren);
myNameToChild = Collections.unmodifiableMap(myNameToChild);
- List children = new ArrayList();
+ List children = new ArrayList<>();
children.addAll(myChildren);
/*
@@ -554,11 +554,11 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
private static class ScannedField {
private Child myChildAnnotation;
- private List> myChoiceTypes = new ArrayList>();
+ private List> myChoiceTypes = new ArrayList<>();
private Class> myElementType;
private Field myField;
private boolean myFirstFieldInNewClass;
- public ScannedField(Field theField, Class> theClass, boolean theFirstFieldInNewClass) {
+ ScannedField(Field theField, Class> theClass, boolean theFirstFieldInNewClass) {
myField = theField;
myFirstFieldInNewClass = theFirstFieldInNewClass;
@@ -574,10 +574,8 @@ public abstract class BaseRuntimeElementCompositeDefinition ext
myChildAnnotation = childAnnotation;
myElementType = ModelScanner.determineElementType(theField);
-
- for (Class extends IBase> nextChoiceType : childAnnotation.type()) {
- myChoiceTypes.add(nextChoiceType);
- }
+
+ Collections.addAll(myChoiceTypes, childAnnotation.type());
}
public Child getChildAnnotation() {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java
index 79b26314f3f..24b05464213 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java
@@ -19,17 +19,6 @@ package ca.uhn.fhir.context;
* limitations under the License.
* #L%
*/
-import static org.apache.commons.lang3.StringUtils.isBlank;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.annotation.Annotation;
-import java.lang.reflect.*;
-import java.util.*;
-import java.util.Map.Entry;
-
-import org.apache.commons.io.IOUtils;
-import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import ca.uhn.fhir.model.api.*;
@@ -38,6 +27,19 @@ import ca.uhn.fhir.model.primitive.BoundCodeDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.util.ReflectionUtil;
+import org.hl7.fhir.instance.model.api.*;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.util.Map.Entry;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
class ModelScanner {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
@@ -55,7 +57,7 @@ class ModelScanner {
private Set> myVersionTypes;
ModelScanner(FhirContext theContext, FhirVersionEnum theVersion, Map, BaseRuntimeElementDefinition>> theExistingDefinitions,
- Collection> theResourceTypes) throws ConfigurationException {
+ Collection> theResourceTypes) throws ConfigurationException {
myContext = theContext;
myVersion = theVersion;
Set> toScan;
@@ -67,32 +69,6 @@ class ModelScanner {
init(theExistingDefinitions, toScan);
}
- static Class> determineElementType(Field next) {
- Class> nextElementType = next.getType();
- if (List.class.equals(nextElementType)) {
- nextElementType = ReflectionUtil.getGenericCollectionTypeOfField(next);
- } else if (Collection.class.isAssignableFrom(nextElementType)) {
- throw new ConfigurationException("Field '" + next.getName() + "' in type '" + next.getClass().getCanonicalName() + "' is a Collection - Only java.util.List curently supported");
- }
- return nextElementType;
- }
-
- @SuppressWarnings("unchecked")
- static IValueSetEnumBinder> 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");
- }
-
- String fieldName = "VALUESET_BINDER";
- try {
- Field bindingField = bound.getField(fieldName);
- return (IValueSetEnumBinder>) bindingField.get(null);
- } catch (Exception e) {
- throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field (must have a field called " + fieldName + ")", e);
- }
- }
-
public Map, BaseRuntimeElementDefinition>> getClassToElementDefinitions() {
return myClassToElementDefinitions;
}
@@ -137,11 +113,7 @@ class ModelScanner {
for (Class extends IBase> nextClass : typesToScan) {
scan(nextClass);
}
- for (Iterator> iter = myScanAlso.iterator(); iter.hasNext();) {
- if (myClassToElementDefinitions.containsKey(iter.next())) {
- iter.remove();
- }
- }
+ myScanAlso.removeIf(theClass -> myClassToElementDefinitions.containsKey(theClass));
typesToScan.clear();
typesToScan.addAll(myScanAlso);
myScanAlso.clear();
@@ -152,7 +124,7 @@ class ModelScanner {
continue;
}
BaseRuntimeElementDefinition> next = nextEntry.getValue();
-
+
boolean deferredSeal = false;
if (myContext.getPerformanceOptions().contains(PerformanceOptionsEnum.DEFERRED_MODEL_SCANNING)) {
if (next instanceof BaseRuntimeElementCompositeDefinition) {
@@ -177,16 +149,6 @@ class ModelScanner {
return retVal;
}
- /**
- * There are two implementations of all of the annotations (e.g. {@link Child} since the HL7.org ones will eventually replace the HAPI
- * 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.
- */
- static T pullAnnotation(AnnotatedElement theTarget, Class theAnnotationType) {
- T retVal = theTarget.getAnnotation(theAnnotationType);
- return retVal;
- }
-
private void scan(Class extends IBase> theClass) throws ConfigurationException {
BaseRuntimeElementDefinition> existingDef = myClassToElementDefinitions.get(theClass);
if (existingDef != null) {
@@ -197,7 +159,7 @@ class ModelScanner {
if (resourceDefinition != null) {
if (!IBaseResource.class.isAssignableFrom(theClass)) {
throw new ConfigurationException(
- "Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName());
+ "Resource type contains a @" + ResourceDef.class.getSimpleName() + " annotation but does not implement " + IResource.class.getCanonicalName() + ": " + theClass.getCanonicalName());
}
@SuppressWarnings("unchecked")
Class extends IBaseResource> resClass = (Class extends IBaseResource>) theClass;
@@ -212,11 +174,11 @@ class ModelScanner {
Class extends ICompositeType> resClass = (Class extends ICompositeType>) theClass;
scanCompositeDatatype(resClass, datatypeDefinition);
} else if (IPrimitiveType.class.isAssignableFrom(theClass)) {
- @SuppressWarnings({ "unchecked" })
+ @SuppressWarnings({"unchecked"})
Class extends IPrimitiveType>> resClass = (Class extends IPrimitiveType>>) theClass;
scanPrimitiveDatatype(resClass, datatypeDefinition);
- }
-
+ }
+
return;
}
@@ -227,13 +189,13 @@ class ModelScanner {
scanBlock(theClass);
} else {
throw new ConfigurationException(
- "Type contains a @" + Block.class.getSimpleName() + " annotation but does not implement " + IResourceBlock.class.getCanonicalName() + ": " + theClass.getCanonicalName());
+ "Type contains a @" + Block.class.getSimpleName() + " annotation but does not implement " + IResourceBlock.class.getCanonicalName() + ": " + theClass.getCanonicalName());
}
}
if (blockDefinition == null
//Redundant checking && datatypeDefinition == null && resourceDefinition == null
- ) {
+ ) {
throw new ConfigurationException("Resource class[" + theClass.getName() + "] does not contain any valid HAPI-FHIR annotations");
}
}
@@ -246,7 +208,16 @@ class ModelScanner {
throw new ConfigurationException("Block type @" + Block.class.getSimpleName() + " annotation contains no name: " + theClass.getCanonicalName());
}
+ // Just in case someone messes up when upgrading from DSTU2
+ if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
+ if (BaseIdentifiableElement.class.isAssignableFrom(theClass)) {
+ throw new ConfigurationException("@Block class for version " + myContext.getVersion().getVersion().name() + " should not extend " + BaseIdentifiableElement.class.getSimpleName() + ": " + theClass.getName());
+ }
+ }
+
RuntimeResourceBlockDefinition blockDef = new RuntimeResourceBlockDefinition(resourceName, theClass, isStandardType(theClass), myContext, myClassToElementDefinitions);
+ blockDef.populateScanAlso(myScanAlso);
+
myClassToElementDefinitions.put(theClass, blockDef);
}
@@ -272,14 +243,6 @@ class ModelScanner {
elementDef.populateScanAlso(myScanAlso);
}
-
-
- static Class extends Enum>> determineEnumTypeForBoundField(Field next) {
- @SuppressWarnings("unchecked")
- Class extends Enum>> enumType = (Class extends Enum>>) ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next);
- return enumType;
- }
-
private String scanPrimitiveDatatype(Class extends IPrimitiveType>> theClass, DatatypeDef theDatatypeDefinition) {
ourLog.debug("Scanning resource class: {}", theClass.getName());
@@ -333,7 +296,7 @@ class ModelScanner {
}
if (isBlank(resourceName)) {
throw new ConfigurationException("Resource type @" + ResourceDef.class.getSimpleName() + " annotation contains no resource name(): " + theClass.getCanonicalName()
- + " - This is only allowed for types that extend other resource types ");
+ + " - This is only allowed for types that extend other resource types ");
}
}
@@ -345,12 +308,12 @@ class ModelScanner {
primaryNameProvider = false;
}
}
-
+
String resourceId = resourceDefinition.id();
if (!isBlank(resourceId)) {
if (myIdToResourceDefinition.containsKey(resourceId)) {
throw new ConfigurationException("The following resource types have the same ID of '" + resourceId + "' - " + theClass.getCanonicalName() + " and "
- + myIdToResourceDefinition.get(resourceId).getImplementingClass().getCanonicalName());
+ + myIdToResourceDefinition.get(resourceId).getImplementingClass().getCanonicalName());
}
}
@@ -372,7 +335,7 @@ class ModelScanner {
* sure that this type gets scanned as well
*/
resourceDef.populateScanAlso(myScanAlso);
-
+
return resourceName;
}
@@ -393,7 +356,7 @@ class ModelScanner {
}
nextClass = nextClass.getSuperclass();
} while (nextClass.equals(Object.class) == false);
-
+
/*
* Now scan the fields for search params
*/
@@ -404,8 +367,8 @@ class ModelScanner {
if (paramType == null) {
throw new ConfigurationException("Search param " + searchParam.name() + " has an invalid type: " + searchParam.type());
}
- Set providesMembershipInCompartments = null;
- providesMembershipInCompartments = new HashSet();
+ Set providesMembershipInCompartments;
+ providesMembershipInCompartments = new HashSet<>();
for (Compartment next : searchParam.providesMembershipIn()) {
if (paramType != RestSearchParameterTypeEnum.REFERENCE) {
StringBuilder b = new StringBuilder();
@@ -420,14 +383,15 @@ class ModelScanner {
}
providesMembershipInCompartments.add(next.name());
}
-
+
if (paramType == RestSearchParameterTypeEnum.COMPOSITE) {
compositeFields.put(nextField, searchParam);
continue;
}
- RuntimeSearchParam param = new RuntimeSearchParam(searchParam.name(), searchParam.description(), searchParam.path(), paramType, providesMembershipInCompartments, toTargetList(searchParam.target()), RuntimeSearchParamStatusEnum.ACTIVE);
+ Collection base = Collections.singletonList(theResourceDef.getName());
+ RuntimeSearchParam param = new RuntimeSearchParam(null, null, searchParam.name(), searchParam.description(), searchParam.path(), paramType, null, providesMembershipInCompartments, toTargetList(searchParam.target()), RuntimeSearchParamStatusEnum.ACTIVE, base);
theResourceDef.addSearchParam(param);
nameToParam.put(param.getName(), param);
}
@@ -441,7 +405,7 @@ class ModelScanner {
RuntimeSearchParam param = nameToParam.get(nextName);
if (param == null) {
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parametr name itself. Valid values are: {}",
- new Object[] { theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet() });
+ new Object[]{theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet()});
continue;
}
compositeOf.add(param);
@@ -454,17 +418,59 @@ class ModelScanner {
private Set toTargetList(Class extends IBaseResource>[] theTarget) {
HashSet retVal = new HashSet();
-
+
for (Class extends IBaseResource> nextType : theTarget) {
ResourceDef resourceDef = nextType.getAnnotation(ResourceDef.class);
if (resourceDef != null) {
retVal.add(resourceDef.name());
}
}
-
+
return retVal;
}
+ static Class> determineElementType(Field next) {
+ Class> nextElementType = next.getType();
+ if (List.class.equals(nextElementType)) {
+ nextElementType = ReflectionUtil.getGenericCollectionTypeOfField(next);
+ } else if (Collection.class.isAssignableFrom(nextElementType)) {
+ throw new ConfigurationException("Field '" + next.getName() + "' in type '" + next.getClass().getCanonicalName() + "' is a Collection - Only java.util.List curently supported");
+ }
+ return nextElementType;
+ }
+
+ @SuppressWarnings("unchecked")
+ static IValueSetEnumBinder> 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");
+ }
+
+ String fieldName = "VALUESET_BINDER";
+ try {
+ Field bindingField = bound.getField(fieldName);
+ return (IValueSetEnumBinder>) bindingField.get(null);
+ } catch (Exception e) {
+ throw new ConfigurationException("Field '" + theNext + "' has type parameter " + bound.getCanonicalName() + " but this class has no valueset binding field (must have a field called " + fieldName + ")", e);
+ }
+ }
+
+ /**
+ * There are two implementations of all of the annotations (e.g. {@link Child} since the HL7.org ones will eventually replace the HAPI
+ * 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.
+ */
+ static T pullAnnotation(AnnotatedElement theTarget, Class theAnnotationType) {
+ T retVal = theTarget.getAnnotation(theAnnotationType);
+ return retVal;
+ }
+
+ static Class extends Enum>> determineEnumTypeForBoundField(Field next) {
+ @SuppressWarnings("unchecked")
+ Class extends Enum>> enumType = (Class extends Enum>>) ReflectionUtil.getGenericCollectionTypeOfFieldWithSecondOrderForList(next);
+ return enumType;
+ }
+
private static Class> getGenericCollectionTypeOfCodedField(Field next) {
Class> type;
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java
index b25dcbb8cdb..7db7c93400c 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java
@@ -49,9 +49,8 @@ public class RuntimeChildResourceDefinition extends BaseRuntimeDeclaredChildDefi
myResourceTypes = theResourceTypes;
if (theResourceTypes == null || theResourceTypes.isEmpty()) {
- myResourceTypes = new ArrayList>();
+ myResourceTypes = new ArrayList<>();
myResourceTypes.add(IBaseResource.class);
-// throw new ConfigurationException("Field '" + theField.getName() + "' on type '" + theField.getDeclaringClass().getCanonicalName() + "' has no resource types noted");
}
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java
index a96f1283ce8..01d81d69855 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildUndeclaredExtensionDefinition.java
@@ -149,9 +149,9 @@ public class RuntimeChildUndeclaredExtensionDefinition extends BaseRuntimeChildD
@Override
void sealAndInitialize(FhirContext theContext, Map, BaseRuntimeElementDefinition>> theClassToElementDefinitions) {
- Map> datatypeAttributeNameToDefinition = new HashMap>();
- myDatatypeToAttributeName = new HashMap, String>();
- myDatatypeToDefinition = new HashMap, BaseRuntimeElementDefinition>>();
+ Map> datatypeAttributeNameToDefinition = new HashMap<>();
+ myDatatypeToAttributeName = new HashMap<>();
+ myDatatypeToDefinition = new HashMap<>();
for (BaseRuntimeElementDefinition> next : theClassToElementDefinitions.values()) {
if (next instanceof IRuntimeDatatypeDefinition) {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/IFluentPath.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/IFluentPath.java
index b370ea014e1..8f2b8158781 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/IFluentPath.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/fluentpath/IFluentPath.java
@@ -21,6 +21,7 @@ package ca.uhn.fhir.fluentpath;
*/
import java.util.List;
+import java.util.Optional;
import org.hl7.fhir.instance.model.api.IBase;
@@ -36,6 +37,15 @@ public interface IFluentPath {
*/
List evaluate(IBase theInput, String thePath, Class theReturnType);
-
+ /**
+ * Apply the given FluentPath expression against the given input and return
+ * the first match (if any)
+ *
+ * @param theInput The input object (generally a resource or datatype)
+ * @param thePath The fluent path expression
+ * @param theReturnType The type to return (in order to avoid casting)
+ */
+ Optional evaluateFirst(IBase theInput, String thePath, Class theReturnType);
+
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IQueryParameterType.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IQueryParameterType.java
index eb57e00164c..b494e020aac 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IQueryParameterType.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/IQueryParameterType.java
@@ -60,7 +60,7 @@ public interface IQueryParameterType extends Serializable {
public String getValueAsQueryToken(FhirContext theContext);
/**
- * This method will return any qualifier that should be appended to the parameter name (e.g ":exact")
+ * This method will return any qualifier that should be appended to the parameter name (e.g ":exact"). Returns null if none are present.
*/
public String getQueryParameterQualifier();
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java
index b9769cb99f0..9ab0c192c8e 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative/BaseThymeleafNarrativeGenerator.java
@@ -34,6 +34,7 @@ import org.thymeleaf.cache.ICacheEntryValidity;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.ITemplateContext;
import org.thymeleaf.engine.AttributeName;
+import org.thymeleaf.messageresolver.IMessageResolver;
import org.thymeleaf.model.IProcessableElementTag;
import org.thymeleaf.processor.IProcessor;
import org.thymeleaf.processor.element.AbstractAttributeTagProcessor;
@@ -65,6 +66,8 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
private HashMap myNameToNarrativeTemplate;
private TemplateEngine myProfileTemplateEngine;
+ private IMessageResolver resolver;
+
/**
* Constructor
*/
@@ -166,11 +169,21 @@ public abstract class BaseThymeleafNarrativeGenerator implements INarrativeGener
};
myProfileTemplateEngine.setDialect(dialect);
+ if (this.resolver != null) {
+ myProfileTemplateEngine.setMessageResolver(this.resolver);
+ }
}
myInitialized = true;
}
+ public void setMessageResolver(IMessageResolver resolver) {
+ this.resolver = resolver;
+ if (myProfileTemplateEngine != null && resolver != null) {
+ myProfileTemplateEngine.setMessageResolver(resolver);
+ }
+ }
+
/**
* If set to true (which is the default), most whitespace will be trimmed from the generated narrative
* before it is returned.
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java
index 28c840a0940..16160728112 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/BaseParser.java
@@ -27,6 +27,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.UrlUtil;
+import com.google.common.base.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*;
@@ -623,6 +624,16 @@ public abstract class BaseParser implements IParser {
return mySuppressNarratives;
}
+ @Override
+ public IBaseResource parseResource(InputStream theInputStream) throws DataFormatException {
+ return parseResource(new InputStreamReader(theInputStream, Charsets.UTF_8));
+ }
+
+ @Override
+ public T parseResource(Class theResourceType, InputStream theInputStream) throws DataFormatException {
+ return parseResource(theResourceType, new InputStreamReader(theInputStream, Charsets.UTF_8));
+ }
+
@Override
public T parseResource(Class theResourceType, Reader theReader) throws DataFormatException {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java
index b10b6e23ea5..0bde9217995 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/IParser.java
@@ -19,14 +19,23 @@ package ca.uhn.fhir.parser;
* limitations under the License.
* #L%
*/
-import java.io.*;
-import java.util.*;
-import org.hl7.fhir.instance.model.api.*;
-
-import ca.uhn.fhir.context.*;
+import ca.uhn.fhir.context.ConfigurationException;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.ParserOptions;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.api.EncodingEnum;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
/**
* A parser, which can be used to convert between HAPI FHIR model/structure objects, and their respective String wire
@@ -127,6 +136,20 @@ public interface IParser {
*/
T parseResource(Class theResourceType, Reader theReader) throws DataFormatException;
+ /**
+ * Parses a resource
+ *
+ * @param theResourceType
+ * The resource type to use. This can be used to explicitly specify a class which extends a built-in type
+ * (e.g. a custom type extending the default Patient class)
+ * @param theInputStream
+ * The InputStream to parse input from, with an implied charset of UTF-8. Note that the InputStream will not be closed by the parser upon completion.
+ * @return A parsed resource
+ * @throws DataFormatException
+ * If the resource can not be parsed because the data is not recognized or invalid for any reason
+ */
+ T parseResource(Class theResourceType, InputStream theInputStream) throws DataFormatException;
+
/**
* Parses a resource
*
@@ -153,6 +176,19 @@ public interface IParser {
*/
IBaseResource parseResource(Reader theReader) throws ConfigurationException, DataFormatException;
+ /**
+ * Parses a resource
+ *
+ * @param theInputStream
+ * The InputStream to parse input from (charset is assumed to be UTF-8).
+ * Note that the stream will not be closed by the parser upon completion.
+ * @return A parsed resource. Note that the returned object will be an instance of {@link IResource} or
+ * {@link IAnyResource} depending on the specific FhirContext which created this parser.
+ * @throws DataFormatException
+ * If the resource can not be parsed because the data is not recognized or invalid for any reason
+ */
+ IBaseResource parseResource(InputStream theInputStream) throws ConfigurationException, DataFormatException;
+
/**
* Parses a resource
*
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java
index 7fdbdefa417..d17b376b08a 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Operation.java
@@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.annotation;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java
index 4fc0ab5e5a4..deaed08c063 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java
@@ -42,8 +42,14 @@ public class Constants {
*/
public static final Set CORS_ALLWED_METHODS;
public static final String CT_FHIR_JSON = "application/json+fhir";
+ /**
+ * The FHIR MimeType for JSON encoding in FHIR DSTU3+
+ */
public static final String CT_FHIR_JSON_NEW = "application/fhir+json";
public static final String CT_FHIR_XML = "application/xml+fhir";
+ /**
+ * The FHIR MimeType for XML encoding in FHIR DSTU3+
+ */
public static final String CT_FHIR_XML_NEW = "application/fhir+xml";
public static final String CT_HTML = "text/html";
public static final String CT_HTML_WITH_UTF8 = "text/html" + CHARSET_UTF8_CTSUFFIX;
@@ -86,6 +92,7 @@ public class Constants {
public static final String HEADER_CONTENT_LOCATION = "Content-Location";
public static final String HEADER_CONTENT_LOCATION_LC = HEADER_CONTENT_LOCATION.toLowerCase();
public static final String HEADER_CONTENT_TYPE = "Content-Type";
+ public static final String HEADER_CONTENT_TYPE_LC = HEADER_CONTENT_TYPE.toLowerCase();
public static final String HEADER_COOKIE = "Cookie";
public static final String HEADER_CORS_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String HEADER_CORS_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java
index 68ffee11480..fbe11a89491 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java
@@ -20,11 +20,13 @@ package ca.uhn.fhir.rest.api;
* #L%
*/
+import ca.uhn.fhir.util.CoverageIgnore;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
-import ca.uhn.fhir.util.CoverageIgnore;
+import java.util.List;
+import java.util.Map;
public class MethodOutcome {
@@ -32,6 +34,7 @@ public class MethodOutcome {
private IIdType myId;
private IBaseOperationOutcome myOperationOutcome;
private IBaseResource myResource;
+ private Map> myResponseHeaders;
/**
* Constructor
@@ -42,13 +45,10 @@ public class MethodOutcome {
/**
* Constructor
- *
- * @param theId
- * The ID of the created/updated resource
- *
- * @param theCreated
- * If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called
- * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
+ *
+ * @param theId The ID of the created/updated resource
+ * @param theCreated If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called
+ * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
*/
@CoverageIgnore
public MethodOutcome(IIdType theId, Boolean theCreated) {
@@ -58,12 +58,9 @@ public class MethodOutcome {
/**
* Constructor
- *
- * @param theId
- * The ID of the created/updated resource
- *
- * @param theBaseOperationOutcome
- * The operation outcome to return with the response (or null for none)
+ *
+ * @param theId The ID of the created/updated resource
+ * @param theBaseOperationOutcome The operation outcome to return with the response (or null for none)
*/
public MethodOutcome(IIdType theId, IBaseOperationOutcome theBaseOperationOutcome) {
myId = theId;
@@ -72,16 +69,11 @@ public class MethodOutcome {
/**
* Constructor
- *
- * @param theId
- * The ID of the created/updated resource
- *
- * @param theBaseOperationOutcome
- * The operation outcome to return with the response (or null for none)
- *
- * @param theCreated
- * If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called
- * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
+ *
+ * @param theId The ID of the created/updated resource
+ * @param theBaseOperationOutcome The operation outcome to return with the response (or null for none)
+ * @param theCreated If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called
+ * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
*/
public MethodOutcome(IIdType theId, IBaseOperationOutcome theBaseOperationOutcome, Boolean theCreated) {
myId = theId;
@@ -91,9 +83,8 @@ public class MethodOutcome {
/**
* Constructor
- *
- * @param theId
- * The ID of the created/updated resource
+ *
+ * @param theId The ID of the created/updated resource
*/
public MethodOutcome(IIdType theId) {
myId = theId;
@@ -101,9 +92,8 @@ public class MethodOutcome {
/**
* Constructor
- *
- * @param theOperationOutcome
- * The operation outcome resource to return
+ *
+ * @param theOperationOutcome The operation outcome resource to return
*/
public MethodOutcome(IBaseOperationOutcome theOperationOutcome) {
myOperationOutcome = theOperationOutcome;
@@ -117,19 +107,54 @@ public class MethodOutcome {
return myCreated;
}
+ /**
+ * If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called whether the
+ * result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
+ *
+ * Users of HAPI should only interact with this method in Server applications
+ *
+ *
+ * @param theCreated If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called
+ * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
+ * @return Returns a reference to this for easy method chaining
+ */
+ public MethodOutcome setCreated(Boolean theCreated) {
+ myCreated = theCreated;
+ return this;
+ }
+
public IIdType getId() {
return myId;
}
+ /**
+ * @param theId The ID of the created/updated resource
+ * @return Returns a reference to this for easy method chaining
+ */
+ public MethodOutcome setId(IIdType theId) {
+ myId = theId;
+ return this;
+ }
+
/**
* Returns the {@link IBaseOperationOutcome} resource to return to the client or null if none.
- *
+ *
* @return This method will return null, unlike many methods in the API.
*/
public IBaseOperationOutcome getOperationOutcome() {
return myOperationOutcome;
}
+ /**
+ * Sets the {@link IBaseOperationOutcome} resource to return to the client. Set to null (which is the default) if none.
+ *
+ * @return Returns a reference to this for easy method chaining
+ */
+ public MethodOutcome setOperationOutcome(IBaseOperationOutcome theBaseOperationOutcome) {
+ myOperationOutcome = theBaseOperationOutcome;
+ return this;
+ }
+
/**
* From a client response: If the method returned an actual resource body (e.g. a create/update with
* "Prefer: return=representation") this field will be populated with the
@@ -139,50 +164,15 @@ public class MethodOutcome {
return myResource;
}
- /**
- * If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called whether the
- * result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
- *
- * Users of HAPI should only interact with this method in Server applications
- *
- *
- * @param theCreated
- * If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called
- * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist.
- * @return Returns a reference to this for easy method chaining
- */
- public MethodOutcome setCreated(Boolean theCreated) {
- myCreated = theCreated;
- return this;
- }
-
- /**
- * @param theId
- * The ID of the created/updated resource
- * @return Returns a reference to this for easy method chaining
- */
- public MethodOutcome setId(IIdType theId) {
- myId = theId;
- return this;
- }
-
- /**
- * Sets the {@link IBaseOperationOutcome} resource to return to the client. Set to null (which is the default) if none.
- * @return Returns a reference to this for easy method chaining
- */
- public MethodOutcome setOperationOutcome(IBaseOperationOutcome theBaseOperationOutcome) {
- myOperationOutcome = theBaseOperationOutcome;
- return this;
- }
-
/**
* In a server response: This field may be populated in server code with the final resource for operations
* where a resource body is being created/updated. E.g. for an update method, this field could be populated with
- * the resource after the update is applied, with the new version ID, lastUpdate time, etc.
+ * the resource after the update is applied, with the new version ID, lastUpdate time, etc.
*
* This field is optional, but if it is populated the server will return the resource body if requested to
* do so via the HTTP Prefer header.
- *
+ *
+ *
* @return Returns a reference to this for easy method chaining
*/
public MethodOutcome setResource(IBaseResource theResource) {
@@ -190,4 +180,23 @@ public class MethodOutcome {
return this;
}
+ /**
+ * Gets the headers for the HTTP response
+ */
+ public Map> getResponseHeaders() {
+ return myResponseHeaders;
+ }
+
+ /**
+ * Sets the headers for the HTTP response
+ */
+ public void setResponseHeaders(Map> theResponseHeaders) {
+ myResponseHeaders = theResponseHeaders;
+ }
+
+ public void setCreatedUsingStatusCode(int theResponseStatusCode) {
+ if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) {
+ setCreated(true);
+ }
+ }
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/IReindexController.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RequestFormatParamStyleEnum.java
similarity index 70%
rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/IReindexController.java
rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RequestFormatParamStyleEnum.java
index b9935057aef..4a1964d5739 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/IReindexController.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/RequestFormatParamStyleEnum.java
@@ -1,8 +1,8 @@
-package ca.uhn.fhir.jpa.util;
+package ca.uhn.fhir.rest.api;
/*-
* #%L
- * HAPI FHIR JPA Server
+ * HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
@@ -20,15 +20,15 @@ package ca.uhn.fhir.jpa.util;
* #L%
*/
-public interface IReindexController {
+public enum RequestFormatParamStyleEnum {
+ /**
+ * Do not include a _format parameter on requests
+ */
+ NONE,
/**
- * This method is called automatically by the scheduler
+ * "xml" or "json"
*/
- void performReindexingPass();
+ SHORT
- /**
- * This method requests that the reindex process happen as soon as possible
- */
- void requestReindex();
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchTotalModeEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchTotalModeEnum.java
index d10613d01a3..1a0d781e921 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchTotalModeEnum.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/SearchTotalModeEnum.java
@@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.api;
+/*-
+ * #%L
+ * HAPI FHIR - Core Library
+ * %%
+ * Copyright (C) 2014 - 2018 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
import java.util.HashMap;
import java.util.Map;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java
index d7780caa397..697215164de 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpRequest.java
@@ -28,16 +28,18 @@ import java.util.Map;
* Http Request. Allows addition of headers and execution of the request.
*/
public interface IHttpRequest {
-
+
/**
* Add a header to the request
- * @param theName the header name
+ *
+ * @param theName the header name
* @param theValue the header value
*/
void addHeader(String theName, String theValue);
/**
* Execute the request
+ *
* @return the response
*/
IHttpResponse execute() throws IOException;
@@ -50,7 +52,8 @@ public interface IHttpRequest {
/**
* Return the request body as a string.
- * If this is not supported by the underlying technology, null is returned
+ * If this is not supported by the underlying technology, null is returned
+ *
* @return a string representation of the request or null if not supported or empty.
*/
String getRequestBodyFromStream() throws IOException;
@@ -59,10 +62,16 @@ public interface IHttpRequest {
* Return the request URI, or null
*/
String getUri();
-
+
/**
* Return the HTTP verb (e.g. "GET")
*/
String getHttpVerbName();
-
+
+ /**
+ * Remove any headers matching the given name
+ *
+ * @param theHeaderName The header name, e.g. "Accept" (must not be null or blank)
+ */
+ void removeHeaders(String theHeaderName);
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpResponse.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpResponse.java
index 9c5a6f395e6..4e84c9f0617 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpResponse.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IHttpResponse.java
@@ -70,7 +70,7 @@ public interface IHttpResponse {
void close();
/**
- * Returna reader for the response entity
+ * Returns a reader for the response entity
*/
Reader createReader() throws IOException;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java
index 36fd4fea5b0..45de5860581 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/api/IRestfulClient.java
@@ -1,5 +1,11 @@
package ca.uhn.fhir.rest.client.api;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.rest.api.EncodingEnum;
+import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
+import ca.uhn.fhir.rest.api.SummaryEnum;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+
import java.util.List;
/*
@@ -22,23 +28,15 @@ import java.util.List;
* #L%
*/
-import org.hl7.fhir.instance.model.api.IBaseResource;
-
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.rest.api.EncodingEnum;
-import ca.uhn.fhir.rest.api.SummaryEnum;
-
public interface IRestfulClient {
/**
* Retrieve the contents at the given URL and parse them as a resource. This
* method could be used as a low level implementation of a read/vread/search
* operation.
- *
- * @param theResourceType
- * The resource type to parse
- * @param theUrl
- * The URL to load
+ *
+ * @param theResourceType The resource type to parse
+ * @param theUrl The URL to load
* @return The parsed resource
*/
T fetchResourceFromUrl(Class theResourceType, String theUrl);
@@ -49,6 +47,17 @@ public interface IRestfulClient {
*/
EncodingEnum getEncoding();
+ /**
+ * Specifies that the client should use the given encoding to do its
+ * queries. This means that the client will append the "_format" param
+ * to GET methods (read/search/etc), and will add an appropriate header for
+ * write methods.
+ *
+ * @param theEncoding The encoding to use in the request, or null not specify
+ * an encoding (which generally implies the use of XML). The default is null.
+ */
+ void setEncoding(EncodingEnum theEncoding);
+
/**
* Returns the FHIR context associated with this client
*/
@@ -76,25 +85,12 @@ public interface IRestfulClient {
*/
void registerInterceptor(IClientInterceptor theInterceptor);
- /**
- * Specifies that the client should use the given encoding to do its
- * queries. This means that the client will append the "_format" param
- * to GET methods (read/search/etc), and will add an appropriate header for
- * write methods.
- *
- * @param theEncoding
- * The encoding to use in the request, or null not specify
- * an encoding (which generally implies the use of XML). The default is null.
- */
- void setEncoding(EncodingEnum theEncoding);
-
/**
* Specifies that the client should request that the server respond with "pretty printing"
* enabled. Note that this is a non-standard parameter, not all servers will
* support it.
- *
- * @param thePrettyPrint
- * The pretty print flag to use in the request (default is false)
+ *
+ * @param thePrettyPrint The pretty print flag to use in the request (default is false)
*/
void setPrettyPrint(Boolean thePrettyPrint);
@@ -109,4 +105,8 @@ public interface IRestfulClient {
*/
void unregisterInterceptor(IClientInterceptor theInterceptor);
+ /**
+ * Configures what style of _format parameter should be used in requests
+ */
+ void setFormatParamStyle(RequestFormatParamStyleEnum theRequestFormatParamStyle);
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/NonFhirResponseException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/NonFhirResponseException.java
index 5cf326bb9f6..763dcaa9cc4 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/NonFhirResponseException.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/NonFhirResponseException.java
@@ -19,15 +19,18 @@ package ca.uhn.fhir.rest.client.exceptions;
* limitations under the License.
* #L%
*/
-import static org.apache.commons.lang3.StringUtils.isBlank;
-
-import java.io.IOException;
-import java.io.Reader;
-
-import org.apache.commons.io.IOUtils;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.util.CoverageIgnore;
+import com.google.common.base.Charsets;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import static org.apache.commons.lang3.StringUtils.isBlank;
@CoverageIgnore
public class NonFhirResponseException extends BaseServerResponseException {
@@ -36,24 +39,30 @@ public class NonFhirResponseException extends BaseServerResponseException {
/**
* Constructor
- *
- * @param theMessage
- * The message
- * @param theResponseText
- * @param theStatusCode
- * @param theResponseReader
- * @param theContentType
+ *
+ * @param theMessage The message
+ * @param theStatusCode The HTTP status code
*/
NonFhirResponseException(int theStatusCode, String theMessage) {
super(theStatusCode, theMessage);
}
+ public static NonFhirResponseException newInstance(int theStatusCode, String theContentType, InputStream theInputStream) {
+ return newInstance(theStatusCode, theContentType, new InputStreamReader(theInputStream, Charsets.UTF_8));
+ }
+
public static NonFhirResponseException newInstance(int theStatusCode, String theContentType, Reader theReader) {
String responseBody = "";
try {
responseBody = IOUtils.toString(theReader);
} catch (IOException e) {
- IOUtils.closeQuietly(theReader);
+ // ignore
+ } finally {
+ try {
+ theReader.close();
+ } catch (IOException theE) {
+ // ignore
+ }
}
NonFhirResponseException retVal;
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IClientExecutable.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IClientExecutable.java
index 331575d2605..ffe305acfad 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IClientExecutable.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IClientExecutable.java
@@ -2,6 +2,7 @@ package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.EncodingEnum;
+import ca.uhn.fhir.rest.api.RequestFormatParamStyleEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -28,12 +29,12 @@ import java.util.List;
*/
-public interface IClientExecutable, Y> {
+public interface IClientExecutable, Y> {
/**
* If set to true, the client will log the request and response to the SLF4J logger. This can be useful for
* debugging, but is generally not desirable in a production situation.
- *
+ *
* @deprecated Use the client logging interceptor to log requests and responses instead. See here for more information.
*/
@Deprecated
@@ -46,16 +47,45 @@ public interface IClientExecutable, Y> {
T cacheControl(CacheControlDirective theCacheControlDirective);
/**
- * Request that the server return subsetted resources, containing only the elements specified in the given parameters.
+ * Request that the server return subsetted resources, containing only the elements specified in the given parameters.
* For example: subsetElements("name", "identifier") requests that the server only return
- * the "name" and "identifier" fields in the returned resource, and omit any others.
+ * the "name" and "identifier" fields in the returned resource, and omit any others.
*/
T elementsSubset(String... theElements);
+ /**
+ * Request that the server respond with JSON via the Accept header and possibly also the
+ * _format parameter if {@link ca.uhn.fhir.rest.client.api.IRestfulClient#setFormatParamStyle(RequestFormatParamStyleEnum) configured to do so}.
+ *
+ * This method will have no effect if {@link #accept(String) a custom Accept header} is specified.
+ *
+ *
+ * @see #accept(String)
+ */
T encoded(EncodingEnum theEncoding);
+ /**
+ * Request that the server respond with JSON via the Accept header and possibly also the
+ * _format parameter if {@link ca.uhn.fhir.rest.client.api.IRestfulClient#setFormatParamStyle(RequestFormatParamStyleEnum) configured to do so}.
+ *
+ * This method will have no effect if {@link #accept(String) a custom Accept header} is specified.
+ *
+ *
+ * @see #accept(String)
+ * @see #encoded(EncodingEnum)
+ */
T encodedJson();
+ /**
+ * Request that the server respond with JSON via the Accept header and possibly also the
+ * _format parameter if {@link ca.uhn.fhir.rest.client.api.IRestfulClient#setFormatParamStyle(RequestFormatParamStyleEnum) configured to do so}.
+ *
+ * This method will have no effect if {@link #accept(String) a custom Accept header} is specified.
+ *
+ *
+ * @see #accept(String)
+ * @see #encoded(EncodingEnum)
+ */
T encodedXml();
/**
@@ -84,11 +114,33 @@ public interface IClientExecutable, Y> {
*/
T preferResponseTypes(List> theTypes);
+ /**
+ * Request pretty-printed response via the _pretty parameter
+ */
T prettyPrint();
/**
- * Request that the server modify the response using the _summary param
+ * Request that the server modify the response using the _summary param
*/
T summaryMode(SummaryEnum theSummary);
+ /**
+ * Specifies a custom Accept header that should be supplied with the
+ * request.
+ *
+ * Note that this method overrides any encoding preferences specified with
+ * {@link #encodedJson()} or {@link #encodedXml()}. It is generally easier to
+ * just use those methods if you simply want to request a specific FHIR encoding.
+ *
+ *
+ * @param theHeaderValue The header value, e.g. "application/fhir+json". Constants such
+ * as {@link ca.uhn.fhir.rest.api.Constants#CT_FHIR_XML_NEW} and
+ * {@link ca.uhn.fhir.rest.api.Constants#CT_FHIR_JSON_NEW} may
+ * be useful. If set to null or an empty string, the
+ * default Accept header will be used.
+ * @see #encoded(EncodingEnum)
+ * @see #encodedJson()
+ * @see #encodedXml()
+ */
+ T accept(String theHeaderValue);
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntyped.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntyped.java
index dff17e286e1..40155444efc 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntyped.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntyped.java
@@ -34,7 +34,7 @@ public interface IOperationUntyped {
* @param theParameters The parameters to use as input. May also be null if the operation
* does not require any input parameters.
*/
- IOperationUntypedWithInput withParameters(T theParameters);
+ IOperationUntypedWithInputAndPartialOutput withParameters(T theParameters);
/**
* The operation does not require any input parameters
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInput.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInput.java
index ba922755475..ea41945171b 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInput.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInput.java
@@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.gclient;
* #L%
*/
+import ca.uhn.fhir.rest.api.MethodOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
public interface IOperationUntypedWithInput extends IClientExecutable, T> {
@@ -43,4 +44,9 @@ public interface IOperationUntypedWithInput extends IClientExecutable IOperationUntypedWithInput returnResourceType(Class theReturnType);
+ /**
+ * Request that the method chain returns a {@link MethodOutcome} object. This object
+ * will contain details
+ */
+ IOperationUntypedWithInput returnMethodOutcome();
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInputAndPartialOutput.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInputAndPartialOutput.java
index ace3562a8c9..71594f48c47 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInputAndPartialOutput.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/IOperationUntypedWithInputAndPartialOutput.java
@@ -38,6 +38,8 @@ public interface IOperationUntypedWithInputAndPartialOutput andParameter(String theName, IBase theValue);
/**
+ * Adds a URL parameter to the request.
+ *
* Use chained method calls to construct a Parameters input. This form is a convenience
* in order to allow simple method chaining to be used to build up a parameters
* resource for the input of an operation without needing to manually construct one.
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ReferenceClientParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ReferenceClientParam.java
index 405261690b6..0b59ed2fc72 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ReferenceClientParam.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ReferenceClientParam.java
@@ -1,9 +1,11 @@
package ca.uhn.fhir.rest.gclient;
+import ca.uhn.fhir.context.FhirContext;
+import org.hl7.fhir.instance.model.api.IIdType;
+
import java.util.Collection;
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.model.primitive.IdDt;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
* #%L
@@ -38,17 +40,43 @@ public class ReferenceClientParam extends BaseClientParam implements IParam {
public String getParamName() {
return myName;
}
-
+
+ /**
+ * Include a chained search. For example:
+ *
+ */
public ICriterion hasChainedProperty(ICriterion> theCriterion) {
return new ReferenceChainCriterion(getParamName(), theCriterion);
}
+ /**
+ * Include a chained search with a resource type. For example:
+ *
+ */
+ public ICriterion hasChainedProperty(String theResourceType, ICriterion> theCriterion) {
+ return new ReferenceChainCriterion(getParamName(), theResourceType, theCriterion);
+ }
+
/**
* Match the referenced resource if the resource has the given ID (this can be
* the logical ID or the absolute URL of the resource)
*/
- public ICriterion hasId(IdDt theId) {
- return new StringCriterion(getParamName(), theId.getValue());
+ public ICriterion hasId(IIdType theId) {
+ return new StringCriterion<>(getParamName(), theId.getValue());
}
/**
@@ -56,7 +84,7 @@ public class ReferenceClientParam extends BaseClientParam implements IParam {
* the logical ID or the absolute URL of the resource)
*/
public ICriterion hasId(String theId) {
- return new StringCriterion(getParamName(), theId);
+ return new StringCriterion<>(getParamName(), theId);
}
/**
@@ -67,22 +95,28 @@ public class ReferenceClientParam extends BaseClientParam implements IParam {
* with the same parameter.
*/
public ICriterion hasAnyOfIds(Collection theIds) {
- return new StringCriterion(getParamName(), theIds);
+ return new StringCriterion<>(getParamName(), theIds);
}
private static class ReferenceChainCriterion implements ICriterion, ICriterionInternal {
+ private final String myResourceTypeQualifier;
private String myParamName;
private ICriterionInternal myWrappedCriterion;
- public ReferenceChainCriterion(String theParamName, ICriterion> theWrappedCriterion) {
+ ReferenceChainCriterion(String theParamName, ICriterion> theWrappedCriterion) {
+ this(theParamName, null, theWrappedCriterion);
+ }
+
+ ReferenceChainCriterion(String theParamName, String theResourceType, ICriterion> theWrappedCriterion) {
myParamName = theParamName;
+ myResourceTypeQualifier = isNotBlank(theResourceType) ? ":" + theResourceType : "";
myWrappedCriterion = (ICriterionInternal) theWrappedCriterion;
}
@Override
public String getParameterName() {
- return myParamName + "." + myWrappedCriterion.getParameterName();
+ return myParamName + myResourceTypeQualifier + "." + myWrappedCriterion.getParameterName();
}
@Override
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java
index c86d241a79f..b311d428563 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/DateRangeParam.java
@@ -255,7 +255,7 @@ public class DateRangeParam implements IQueryParameterAnd {
}
public Date getLowerBoundAsInstant() {
- if (myLowerBound == null) {
+ if (myLowerBound == null || myLowerBound.getValue() == null) {
return null;
}
Date retVal = myLowerBound.getValue();
@@ -310,7 +310,7 @@ public class DateRangeParam implements IQueryParameterAnd {
}
public Date getUpperBoundAsInstant() {
- if (myUpperBound == null) {
+ if (myUpperBound == null || myUpperBound.getValue() == null) {
return null;
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java
index 669ebe2a57a..d25afa5340d 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/TokenParam.java
@@ -100,7 +100,11 @@ public class TokenParam extends BaseParam /*implements IQueryParameterType*/ {
@Override
String doGetValueAsQueryToken(FhirContext theContext) {
if (getSystem() != null) {
- return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|' + ParameterUtil.escape(getValue());
+ if (getValue() != null) {
+ return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|' + ParameterUtil.escape(getValue());
+ } else {
+ return ParameterUtil.escape(StringUtils.defaultString(getSystem())) + '|';
+ }
}
return ParameterUtil.escape(getValue());
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
index 354d52d6201..ec8f590d723 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java
@@ -28,9 +28,9 @@ import static org.apache.commons.lang3.StringUtils.*;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -96,34 +96,36 @@ public class FhirTerser {
/**
* Clones all values from a source object into the equivalent fields in a target object
- * @param theSource The source object (must not be null)
- * @param theTarget The target object to copy values into (must not be null)
+ *
+ * @param theSource The source object (must not be null)
+ * @param theTarget The target object to copy values into (must not be null)
* @param theIgnoreMissingFields The ignore fields in the target which do not exist (if false, an exception will be thrown if the target is unable to accept a value from the source)
+ * @return Returns the target (which will be the same object that was passed into theTarget) for easy chaining
*/
- public void cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissingFields) {
+ public IBase cloneInto(IBase theSource, IBase theTarget, boolean theIgnoreMissingFields) {
Validate.notNull(theSource, "theSource must not be null");
Validate.notNull(theTarget, "theTarget must not be null");
-
+
if (theSource instanceof IPrimitiveType>) {
if (theTarget instanceof IPrimitiveType>) {
- ((IPrimitiveType>)theTarget).setValueAsString(((IPrimitiveType>)theSource).getValueAsString());
- return;
+ ((IPrimitiveType>) theTarget).setValueAsString(((IPrimitiveType>) theSource).getValueAsString());
+ return theSource;
}
if (theIgnoreMissingFields) {
- return;
+ return theSource;
}
throw new DataFormatException("Can not copy value from primitive of type " + theSource.getClass().getName() + " into type " + theTarget.getClass().getName());
}
-
- BaseRuntimeElementCompositeDefinition> sourceDef = (BaseRuntimeElementCompositeDefinition>) myContext.getElementDefinition(theSource.getClass());
+
+ BaseRuntimeElementCompositeDefinition> sourceDef = (BaseRuntimeElementCompositeDefinition>) myContext.getElementDefinition(theSource.getClass());
BaseRuntimeElementCompositeDefinition> targetDef = (BaseRuntimeElementCompositeDefinition>) myContext.getElementDefinition(theTarget.getClass());
-
+
List children = sourceDef.getChildren();
if (sourceDef instanceof RuntimeExtensionDtDefinition) {
- children = ((RuntimeExtensionDtDefinition)sourceDef).getChildrenIncludingUrl();
+ children = ((RuntimeExtensionDtDefinition) sourceDef).getChildrenIncludingUrl();
}
-
- for (BaseRuntimeChildDefinition nextChild : children) {
+
+ for (BaseRuntimeChildDefinition nextChild : children)
for (IBase nextValue : nextChild.getAccessor().getValues(theSource)) {
String elementName = nextChild.getChildNameByDatatype(nextValue.getClass());
BaseRuntimeChildDefinition targetChild = targetDef.getChildByName(elementName);
@@ -133,14 +135,15 @@ public class FhirTerser {
}
throw new DataFormatException("Type " + theTarget.getClass().getName() + " does not have a child with name " + elementName);
}
-
- BaseRuntimeElementDefinition> childDef = targetChild.getChildByName(elementName);
- IBase target = childDef.newInstance();
+
+ BaseRuntimeElementDefinition> element = myContext.getElementDefinition(nextValue.getClass());
+ IBase target = element.newInstance();
+
targetChild.getMutator().addValue(theTarget, target);
cloneInto(nextValue, target, theIgnoreMissingFields);
}
- }
-
+
+ return theTarget;
}
/**
@@ -153,11 +156,9 @@ public class FhirTerser {
* Note on scope: This method will descend into any contained resources ({@link IResource#getContained()}) as well, but will not descend into linked resources (e.g.
* {@link BaseResourceReferenceDt#getResource()}) or embedded resources (e.g. Bundle.entry.resource)
*
- *
- * @param theResource
- * The resource instance to search. Must not be null.
- * @param theType
- * The type to search for. Must not be null.
+ *
+ * @param theResource The resource instance to search. Must not be null.
+ * @param theType The type to search for. Must not be null.
* @return Returns a list of all matching elements
*/
public List getAllPopulatedChildElementsOfType(IBaseResource theResource, final Class theType) {
@@ -274,7 +275,7 @@ public class FhirTerser {
.collect(Collectors.toList());
if (theAddExtension
- && (!(theCurrentObj instanceof IBaseExtension) || (extensionDts.isEmpty() && theSubList.size() == 1))) {
+ && (!(theCurrentObj instanceof IBaseExtension) || (extensionDts.isEmpty() && theSubList.size() == 1))) {
extensionDts.add(createEmptyExtensionDt((ISupportsUndeclaredExtensions) theCurrentObj, extensionUrl));
}
@@ -286,7 +287,7 @@ public class FhirTerser {
extensionDts = ((IBaseExtension) theCurrentObj).getExtension();
if (theAddExtension
- && (extensionDts.isEmpty() && theSubList.size() == 1)) {
+ && (extensionDts.isEmpty() && theSubList.size() == 1)) {
extensionDts.add(createEmptyExtensionDt((IBaseExtension) theCurrentObj, extensionUrl));
}
@@ -311,7 +312,7 @@ public class FhirTerser {
.collect(Collectors.toList());
if (theAddExtension
- && (!(theCurrentObj instanceof IBaseExtension) || (extensions.isEmpty() && theSubList.size() == 1))) {
+ && (!(theCurrentObj instanceof IBaseExtension) || (extensions.isEmpty() && theSubList.size() == 1))) {
extensions.add(createEmptyExtension((IBaseHasExtensions) theCurrentObj, extensionUrl));
}
@@ -396,7 +397,7 @@ public class FhirTerser {
.collect(Collectors.toList());
if (theAddExtension
- && (!(theCurrentObj instanceof IBaseExtension) || (extensions.isEmpty() && theSubList.size() == 1))) {
+ && (!(theCurrentObj instanceof IBaseExtension) || (extensions.isEmpty() && theSubList.size() == 1))) {
extensions.add(createEmptyModifierExtension((IBaseHasModifierExtensions) theCurrentObj, extensionUrl));
}
@@ -478,7 +479,7 @@ public class FhirTerser {
* type {@link Object}.
*
* @param theResource The resource instance to be accessed. Must not be null.
- * @param thePath The path for the element to be accessed.
+ * @param thePath The path for the element to be accessed.
* @return A list of values of type {@link Object}.
*/
public List