Merge branch 'master' into master

This commit is contained in:
James Agnew 2018-03-12 07:41:22 -03:00 committed by GitHub
commit 60354a4cc8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1952 changed files with 1344537 additions and 1378046 deletions

View File

@ -25,5 +25,5 @@ before_script:
script: script:
# - mvn -e -B clean install && cd hapi-fhir-ra && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID clean test jacoco:report coveralls:report # - mvn -e -B clean install && cd hapi-fhir-ra && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID clean test jacoco:report coveralls:report
# - mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report # - mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
- mvn -Dci=true -e -B -P ALLMODULES,REDUCED_JPA_TESTS,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report - mvn -Dci=true -e -B -P ALLMODULES,REDUCED_JPA_TESTS,ERRORPRONE_JDK8 clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report

View File

@ -4,4 +4,4 @@ cache:
- C:\maven\ - C:\maven\
- C:\Users\appveyor\.m2\repository - C:\Users\appveyor\.m2\repository
build_script: build_script:
- cmd: mvn -P MINPARALLEL,ALLMODULES install - cmd: mvn -P MINPARALLEL,ALLMODULES clean install

View File

@ -0,0 +1,121 @@
package example;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.util.ResourceReferenceInfo;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("unused")
public class Copier {
private static final Logger ourLog = LoggerFactory.getLogger(Copier.class);
public static void main(String[] args) {
FhirContext ctx = FhirContext.forDstu3();
IGenericClient source = ctx.newRestfulGenericClient("http://localhost:8080/baseDstu3");
IGenericClient target = ctx.newRestfulGenericClient("https://try.smilecdr.com:8000");
List<String> resType = Arrays.asList(
"Patient", "Organization", "Encounter", "Procedure",
"Observation", "ResearchSubject", "Specimen",
"ResearchStudy", "Location", "Practitioner"
);
List<IBaseResource> queued = new ArrayList<>();
Set<String> sent = new HashSet<>();
for (String next : resType) {
copy(ctx, source, target, next, queued, sent);
}
while (queued.size() > 0) {
ourLog.info("Have {} queued resources to deliver", queued.size());
for (IBaseResource nextQueued : new ArrayList<>(queued)) {
String missingRef = null;
for (ResourceReferenceInfo nextRefInfo : ctx.newTerser().getAllResourceReferences(nextQueued)) {
String nextRef = nextRefInfo.getResourceReference().getReferenceElement().getValue();
if (isNotBlank(nextRef) && !sent.contains(nextRef)) {
missingRef = nextRef;
}
}
if (missingRef != null) {
ourLog.info("Can't send {} because of missing ref {}", nextQueued.getIdElement().getIdPart(), missingRef);
continue;
}
IIdType newId = target
.update()
.resource(nextQueued)
.execute()
.getId();
ourLog.info("Copied resource {} and got ID {}", nextQueued.getIdElement().getValue(), newId);
sent.add(nextQueued.getIdElement().toUnqualifiedVersionless().getValue());
queued.remove(nextQueued);
}
}
}
private static void copy(FhirContext theCtx, IGenericClient theSource, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent) {
Bundle received = theSource
.search()
.forResource(theResType)
.returnBundle(Bundle.class)
.execute();
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
while (received.getLink("next") != null) {
ourLog.info("Fetching next page...");
received = theSource.loadPage().next(received).execute();
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
}
}
private static void copy(FhirContext theCtx, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent, Bundle theReceived) {
for (Bundle.BundleEntryComponent nextEntry : theReceived.getEntry()) {
Resource nextResource = nextEntry.getResource();
nextResource.setId(theResType + "/" + "CR-" + nextResource.getIdElement().getIdPart());
boolean haveUnsentReference = false;
for (ResourceReferenceInfo nextRefInfo : theCtx.newTerser().getAllResourceReferences(nextResource)) {
IIdType nextRef = nextRefInfo.getResourceReference().getReferenceElement();
if (nextRef.hasIdPart()) {
String newRef = nextRef.getResourceType() + "/" + "CR-" + nextRef.getIdPart();
ourLog.info("Changing reference {} to {}", nextRef.getValue(), newRef);
nextRefInfo.getResourceReference().setReference(newRef);
if (!theSent.contains(newRef)) {
haveUnsentReference = true;
}
}
}
if (haveUnsentReference) {
ourLog.info("Queueing {} for delivery after", nextResource.getId());
theQueued.add(nextResource);
continue;
}
IIdType newId = theTarget
.update()
.resource(nextResource)
.execute()
.getId();
ourLog.info("Copied resource {} and got ID {}", nextResource.getId(), newId);
theSent.add(nextResource.getIdElement().toUnqualifiedVersionless().getValue());
}
}
}

View File

@ -40,16 +40,14 @@ public enum FhirVersionEnum {
DSTU2_1("org.hl7.fhir.dstu2016may.hapi.ctx.FhirDstu2_1", null, true, new Version("1.4.0")), DSTU2_1("org.hl7.fhir.dstu2016may.hapi.ctx.FhirDstu2_1", null, true, new Version("1.4.0")),
DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, new Dstu3Version()), DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, new Dstu3Version()),
R4("org.hl7.fhir.r4.hapi.ctx.FhirR4", null, true, new R4Version()), R4("org.hl7.fhir.r4.hapi.ctx.FhirR4", null, true, new R4Version()),;
;
private final FhirVersionEnum myEquivalent; private final FhirVersionEnum myEquivalent;
private final boolean myIsRi; private final boolean myIsRi;
private volatile Boolean myPresentOnClasspath;
private final String myVersionClass; private final String myVersionClass;
private volatile Boolean myPresentOnClasspath;
private volatile IFhirVersion myVersionImplementation; private volatile IFhirVersion myVersionImplementation;
private String myFhirVersionString; private String myFhirVersionString;
@ -124,15 +122,50 @@ public enum FhirVersionEnum {
return myIsRi; return myIsRi;
} }
public FhirContext newContext() {
switch (this) {
case DSTU2:
return FhirContext.forDstu2();
case DSTU2_HL7ORG:
return FhirContext.forDstu2Hl7Org();
case DSTU2_1:
return FhirContext.forDstu2_1();
case DSTU3:
return FhirContext.forDstu3();
case R4:
return FhirContext.forR4();
}
throw new IllegalStateException("Unknown version: " + this); // should not happen
}
/**
* Returns the {@link FhirVersionEnum} which corresponds to a specific version of
* FHIR. Partial version strings (e.g. "3.0") are acceptable.
*
* @return Returns null if no version exists matching the given string
*/
public static FhirVersionEnum forVersionString(String theVersionString) {
for (FhirVersionEnum next : values()) {
if (next.getFhirVersionString().startsWith(theVersionString)) {
return next;
}
}
return null;
}
private interface IVersionProvider {
String provideVersion();
}
private static class Version implements IVersionProvider { private static class Version implements IVersionProvider {
private String myVersion;
public Version(String theVersion) { public Version(String theVersion) {
super(); super();
myVersion = theVersion; myVersion = theVersion;
} }
private String myVersion;
@Override @Override
public String provideVersion() { public String provideVersion() {
return myVersion; return myVersion;
@ -140,18 +173,15 @@ public enum FhirVersionEnum {
} }
private interface IVersionProvider {
String provideVersion();
}
/** /**
* This class attempts to read the FHIR version from the actual model * This class attempts to read the FHIR version from the actual model
* classes in order to supply an accurate version string even over time * classes in order to supply an accurate version string even over time
*
*/ */
private static class Dstu3Version implements IVersionProvider { private static class Dstu3Version implements IVersionProvider {
public Dstu3Version() { private String myVersion;
Dstu3Version() {
try { try {
Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants"); Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null); myVersion = (String) c.getDeclaredField("VERSION").get(null);
@ -160,8 +190,6 @@ public enum FhirVersionEnum {
} }
} }
private String myVersion;
@Override @Override
public String provideVersion() { public String provideVersion() {
return myVersion; return myVersion;
@ -171,7 +199,9 @@ public enum FhirVersionEnum {
private static class R4Version implements IVersionProvider { private static class R4Version implements IVersionProvider {
public R4Version() { private String myVersion;
R4Version() {
try { try {
Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants"); Class<?> c = Class.forName("org.hl7.fhir.r4.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null); myVersion = (String) c.getDeclaredField("VERSION").get(null);
@ -180,8 +210,6 @@ public enum FhirVersionEnum {
} }
} }
private String myVersion;
@Override @Override
public String provideVersion() { public String provideVersion() {
return myVersion; return myVersion;

View File

@ -19,18 +19,6 @@ package ca.uhn.fhir.model.api;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
@ -39,6 +27,14 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.io.Serializable;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/** /**
* Keys in this map refer to <b>resource metadata keys</b>, which are keys used to access information about specific resource instances that live outside of the resource body. Typically, these are * Keys in this map refer to <b>resource metadata keys</b>, which are keys used to access information about specific resource instances that live outside of the resource body. Typically, these are
@ -64,8 +60,6 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
*/ */
public abstract class ResourceMetadataKeyEnum<T> implements Serializable { public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
private static final long serialVersionUID = 1L;
/** /**
* If present and populated with a date/time (as an instance of {@link InstantDt}), this value is an indication that the resource is in the deleted state. This key is only used in a limited number * If present and populated with a date/time (as an instance of {@link InstantDt}), this value is an indication that the resource is in the deleted state. This key is only used in a limited number
* of scenarios, such as POSTing transaction bundles to a server, or returning resource history. * of scenarios, such as POSTing transaction bundles to a server, or returning resource history.
@ -81,23 +75,22 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT); return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
} }
@Override
public void put(IResource theResource, InstantDt theObject) {
theResource.getResourceMetadata().put(DELETED_AT, theObject);
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public IPrimitiveType<Date> get(IAnyResource theResource) { public IPrimitiveType<Date> get(IAnyResource theResource) {
return (IPrimitiveType<Date>) theResource.getUserData(DELETED_AT.name()); return (IPrimitiveType<Date>) theResource.getUserData(DELETED_AT.name());
} }
@Override
public void put(IResource theResource, InstantDt theObject) {
theResource.getResourceMetadata().put(DELETED_AT, theObject);
}
@Override @Override
public void put(IAnyResource theResource, IPrimitiveType<Date> theObject) { public void put(IAnyResource theResource, IPrimitiveType<Date> theObject) {
theResource.setUserData(DELETED_AT.name(), theObject); theResource.setUserData(DELETED_AT.name(), theObject);
} }
}; };
/** /**
* Denotes the search score which a given resource should match in a transaction. See the FHIR transaction definition for information about this. Corresponds to the value in * Denotes the search score which a given resource should match in a transaction. See the FHIR transaction definition for information about this. Corresponds to the value in
* <code>Bundle.entry.score</code> in a Bundle resource. * <code>Bundle.entry.score</code> in a Bundle resource.
@ -121,7 +114,6 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(ENTRY_SCORE, theObject); theResource.getResourceMetadata().put(ENTRY_SCORE, theObject);
} }
}; };
/** /**
* If present and populated with a {@link BundleEntrySearchModeEnum}, contains the "bundle entry search mode", which is the value of the status field in the Bundle entry containing this resource. * If present and populated with a {@link BundleEntrySearchModeEnum}, contains the "bundle entry search mode", which is the value of the status field in the Bundle entry containing this resource.
* The value for this key corresponds to field <code>Bundle.entry.search.mode</code>. This value can be set to provide a status value of "include" for included resources being returned by a * The value for this key corresponds to field <code>Bundle.entry.search.mode</code>. This value can be set to provide a status value of "include" for included resources being returned by a
@ -135,27 +127,27 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
*/ */
public static final ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String> ENTRY_SEARCH_MODE = new ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String>("ENTRY_SEARCH_MODE") { public static final ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String> ENTRY_SEARCH_MODE = new ResourceMetadataKeySupportingAnyResource<BundleEntrySearchModeEnum, String>("ENTRY_SEARCH_MODE") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public BundleEntrySearchModeEnum get(IResource theResource) { public BundleEntrySearchModeEnum get(IResource theResource) {
return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SEARCH_MODE, BundleEntrySearchModeEnum.class, BundleEntrySearchModeEnum.VALUESET_BINDER); return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SEARCH_MODE, BundleEntrySearchModeEnum.class, BundleEntrySearchModeEnum.VALUESET_BINDER);
} }
@Override
public void put(IResource theResource, BundleEntrySearchModeEnum theObject) {
theResource.getResourceMetadata().put(ENTRY_SEARCH_MODE, theObject);
}
@Override @Override
public String get(IAnyResource theResource) { public String get(IAnyResource theResource) {
return (String) theResource.getUserData(ENTRY_SEARCH_MODE.name()); return (String) theResource.getUserData(ENTRY_SEARCH_MODE.name());
} }
@Override
public void put(IResource theResource, BundleEntrySearchModeEnum theObject) {
theResource.getResourceMetadata().put(ENTRY_SEARCH_MODE, theObject);
}
@Override @Override
public void put(IAnyResource theResource, String theObject) { public void put(IAnyResource theResource, String theObject) {
theResource.setUserData(ENTRY_SEARCH_MODE.name(), theObject); theResource.setUserData(ENTRY_SEARCH_MODE.name(), theObject);
} }
}; };
/** /**
* If present and populated with a {@link BundleEntryTransactionMethodEnum}, contains the "bundle entry transaction operation", which is the value of the status field in the Bundle entry * If present and populated with a {@link BundleEntryTransactionMethodEnum}, contains the "bundle entry transaction operation", which is the value of the status field in the Bundle entry
* containing this resource. The value for this key corresponds to field <code>Bundle.entry.transaction.operation</code>. This value can be set in resources being transmitted to a server to * containing this resource. The value for this key corresponds to field <code>Bundle.entry.transaction.operation</code>. This value can be set in resources being transmitted to a server to
@ -169,18 +161,13 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
* </p> * </p>
*/ */
public static final ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String> ENTRY_TRANSACTION_METHOD = new ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String>( public static final ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String> ENTRY_TRANSACTION_METHOD = new ResourceMetadataKeySupportingAnyResource<BundleEntryTransactionMethodEnum, String>(
"ENTRY_TRANSACTION_OPERATION") { "ENTRY_TRANSACTION_OPERATION") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public BundleEntryTransactionMethodEnum get(IResource theResource) { public BundleEntryTransactionMethodEnum get(IResource theResource) {
return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_TRANSACTION_METHOD, BundleEntryTransactionMethodEnum.class, return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_TRANSACTION_METHOD, BundleEntryTransactionMethodEnum.class,
BundleEntryTransactionMethodEnum.VALUESET_BINDER); BundleEntryTransactionMethodEnum.VALUESET_BINDER);
}
@Override
public void put(IResource theResource, BundleEntryTransactionMethodEnum theObject) {
theResource.getResourceMetadata().put(ENTRY_TRANSACTION_METHOD, theObject);
} }
@Override @Override
@ -188,13 +175,17 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
return (String) theResource.getUserData(ENTRY_TRANSACTION_METHOD.name()); return (String) theResource.getUserData(ENTRY_TRANSACTION_METHOD.name());
} }
@Override
public void put(IResource theResource, BundleEntryTransactionMethodEnum theObject) {
theResource.getResourceMetadata().put(ENTRY_TRANSACTION_METHOD, theObject);
}
@Override @Override
public void put(IAnyResource theResource, String theObject) { public void put(IAnyResource theResource, String theObject) {
theResource.setUserData(ENTRY_TRANSACTION_METHOD.name(), theObject); theResource.setUserData(ENTRY_TRANSACTION_METHOD.name(), theObject);
} }
};
};
/** /**
* If present and populated with a string, provides the "alternate link" (the link element in the bundle entry with <code>rel="alternate"</code>). Server implementations may populate this with a * If present and populated with a string, provides the "alternate link" (the link element in the bundle entry with <code>rel="alternate"</code>). Server implementations may populate this with a
* complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient/1243") in which case the server will convert this to * complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient/1243") in which case the server will convert this to
@ -205,6 +196,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
*/ */
public static final ResourceMetadataKeyEnum<String> LINK_ALTERNATE = new ResourceMetadataKeyEnum<String>("LINK_ALTERNATE") { public static final ResourceMetadataKeyEnum<String> LINK_ALTERNATE = new ResourceMetadataKeyEnum<String>("LINK_ALTERNATE") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public String get(IResource theResource) { public String get(IResource theResource) {
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_ALTERNATE); return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_ALTERNATE);
@ -215,7 +207,6 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(LINK_ALTERNATE, theObject); theResource.getResourceMetadata().put(LINK_ALTERNATE, theObject);
} }
}; };
/** /**
* If present and populated with a string, provides the "search link" (the link element in the bundle entry with <code>rel="search"</code>). Server implementations may populate this with a * If present and populated with a string, provides the "search link" (the link element in the bundle entry with <code>rel="search"</code>). Server implementations may populate this with a
* complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient?name=tester") in which case the server will convert * complete URL, in which case the URL will be placed as-is in the bundle. They may alternately specify a resource relative URL (e.g. "Patient?name=tester") in which case the server will convert
@ -226,6 +217,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
*/ */
public static final ResourceMetadataKeyEnum<String> LINK_SEARCH = new ResourceMetadataKeyEnum<String>("LINK_SEARCH") { public static final ResourceMetadataKeyEnum<String> LINK_SEARCH = new ResourceMetadataKeyEnum<String>("LINK_SEARCH") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public String get(IResource theResource) { public String get(IResource theResource) {
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_SEARCH); return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_SEARCH);
@ -236,7 +228,6 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(LINK_SEARCH, theObject); theResource.getResourceMetadata().put(LINK_SEARCH, theObject);
} }
}; };
/** /**
* The value for this key represents a previous ID used to identify this resource. This key is currently only used internally during transaction method processing. * The value for this key represents a previous ID used to identify this resource. This key is currently only used internally during transaction method processing.
* <p> * <p>
@ -245,6 +236,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
*/ */
public static final ResourceMetadataKeyEnum<IdDt> PREVIOUS_ID = new ResourceMetadataKeyEnum<IdDt>("PREVIOUS_ID") { public static final ResourceMetadataKeyEnum<IdDt> PREVIOUS_ID = new ResourceMetadataKeyEnum<IdDt>("PREVIOUS_ID") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public IdDt get(IResource theResource) { public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID); return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID);
@ -255,16 +247,16 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(PREVIOUS_ID, theObject); theResource.getResourceMetadata().put(PREVIOUS_ID, theObject);
} }
}; };
/** /**
* The value for this key represents a {@link List} of profile IDs that this resource claims to conform to. * The value for this key represents a {@link List} of profile IDs that this resource claims to conform to.
* * <p>
* <p> * <p>
* Values for this key are of type <b>List&lt;IdDt&gt;</b>. Note that the returned list is <i>unmodifiable</i>, so you need to create a new list and call <code>put</code> to change its value. * Values for this key are of type <b>List&lt;IdDt&gt;</b>. Note that the returned list is <i>unmodifiable</i>, so you need to create a new list and call <code>put</code> to change its value.
* </p> * </p>
*/ */
public static final ResourceMetadataKeyEnum<List<IdDt>> PROFILES = new ResourceMetadataKeyEnum<List<IdDt>>("PROFILES") { public static final ResourceMetadataKeyEnum<List<IdDt>> PROFILES = new ResourceMetadataKeyEnum<List<IdDt>>("PROFILES") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public List<IdDt> get(IResource theResource) { public List<IdDt> get(IResource theResource) {
return getIdListFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PROFILES); return getIdListFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PROFILES);
@ -275,7 +267,6 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(PROFILES, theObject); theResource.getResourceMetadata().put(PROFILES, theObject);
} }
}; };
/** /**
* The value for this key is the bundle entry <b>Published</b> time. This is defined by FHIR as "Time resource copied into the feed", which is generally best left to the current time. * The value for this key is the bundle entry <b>Published</b> time. This is defined by FHIR as "Time resource copied into the feed", which is generally best left to the current time.
* <p> * <p>
@ -284,11 +275,12 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
* <p> * <p>
* <b>Server Note</b>: In servers, it is generally advisable to leave this value <code>null</code>, in which case the server will substitute the current time automatically. * <b>Server Note</b>: In servers, it is generally advisable to leave this value <code>null</code>, in which case the server will substitute the current time automatically.
* </p> * </p>
* *
* @see InstantDt * @see InstantDt
*/ */
public static final ResourceMetadataKeyEnum<InstantDt> PUBLISHED = new ResourceMetadataKeyEnum<InstantDt>("PUBLISHED") { public static final ResourceMetadataKeyEnum<InstantDt> PUBLISHED = new ResourceMetadataKeyEnum<InstantDt>("PUBLISHED") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public InstantDt get(IResource theResource) { public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED); return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED);
@ -299,9 +291,9 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(PUBLISHED, theObject); theResource.getResourceMetadata().put(PUBLISHED, theObject);
} }
}; };
public static final ResourceMetadataKeyEnum<List<BaseCodingDt>> SECURITY_LABELS = new ResourceMetadataKeyEnum<List<BaseCodingDt>>("SECURITY_LABELS") { public static final ResourceMetadataKeyEnum<List<BaseCodingDt>> SECURITY_LABELS = new ResourceMetadataKeyEnum<List<BaseCodingDt>>("SECURITY_LABELS") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public List<BaseCodingDt> get(IResource resource) { public List<BaseCodingDt> get(IResource resource) {
Object obj = resource.getResourceMetadata().get(SECURITY_LABELS); Object obj = resource.getResourceMetadata().get(SECURITY_LABELS);
@ -317,7 +309,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
return securityLabels; return securityLabels;
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new InternalErrorException("Found an object of type '" + obj.getClass().getCanonicalName() + "' in resource metadata for key SECURITY_LABELS - Expected " throw new InternalErrorException("Found an object of type '" + obj.getClass().getCanonicalName() + "' in resource metadata for key SECURITY_LABELS - Expected "
+ BaseCodingDt.class.getCanonicalName()); + BaseCodingDt.class.getCanonicalName());
} }
} }
@ -328,17 +320,17 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
} }
}; };
/** /**
* The value for this key is the list of tags associated with this resource * The value for this key is the list of tags associated with this resource
* <p> * <p>
* Values for this key are of type <b>{@link TagList}</b> * Values for this key are of type <b>{@link TagList}</b>
* </p> * </p>
* *
* @see TagList * @see TagList
*/ */
public static final ResourceMetadataKeyEnum<TagList> TAG_LIST = new ResourceMetadataKeyEnum<TagList>("TAG_LIST") { public static final ResourceMetadataKeyEnum<TagList> TAG_LIST = new ResourceMetadataKeyEnum<TagList>("TAG_LIST") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public TagList get(IResource theResource) { public TagList get(IResource theResource) {
Object retValObj = theResource.getResourceMetadata().get(TAG_LIST); Object retValObj = theResource.getResourceMetadata().get(TAG_LIST);
@ -351,7 +343,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
return (TagList) retValObj; return (TagList) retValObj;
} }
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected " throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected "
+ TagList.class.getCanonicalName()); + TagList.class.getCanonicalName());
} }
@Override @Override
@ -359,7 +351,6 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(TAG_LIST, theObject); theResource.getResourceMetadata().put(TAG_LIST, theObject);
} }
}; };
/** /**
* If present and populated with a string (as an instance of {@link String}), this value contains the title for this resource, as supplied in any bundles containing the resource. * If present and populated with a string (as an instance of {@link String}), this value contains the title for this resource, as supplied in any bundles containing the resource.
* <p> * <p>
@ -368,6 +359,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
*/ */
public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") { public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public String get(IResource theResource) { public String get(IResource theResource) {
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE); return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE);
@ -378,18 +370,18 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(TITLE, theObject); theResource.getResourceMetadata().put(TITLE, theObject);
} }
}; };
/** /**
* The value for this key is the bundle entry <b>Updated</b> time. This is defined by FHIR as "Last Updated for resource". This value is also used for populating the "Last-Modified" header in the * The value for this key is the bundle entry <b>Updated</b> time. This is defined by FHIR as "Last Updated for resource". This value is also used for populating the "Last-Modified" header in the
* case of methods that return a single resource (read, vread, etc.) * case of methods that return a single resource (read, vread, etc.)
* <p> * <p>
* Values for this key are of type <b>{@link InstantDt}</b> * Values for this key are of type <b>{@link InstantDt}</b>
* </p> * </p>
* *
* @see InstantDt * @see InstantDt
*/ */
public static final ResourceMetadataKeyEnum<InstantDt> UPDATED = new ResourceMetadataKeyEnum<InstantDt>("UPDATED") { public static final ResourceMetadataKeyEnum<InstantDt> UPDATED = new ResourceMetadataKeyEnum<InstantDt>("UPDATED") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public InstantDt get(IResource theResource) { public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED); return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED);
@ -400,7 +392,6 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(UPDATED, theObject); theResource.getResourceMetadata().put(UPDATED, theObject);
} }
}; };
/** /**
* The value for this key is the version ID of the resource object. * The value for this key is the version ID of the resource object.
* <p> * <p>
@ -409,6 +400,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
*/ */
public static final ResourceMetadataKeyEnum<String> VERSION = new ResourceMetadataKeyEnum<String>("VERSION") { public static final ResourceMetadataKeyEnum<String> VERSION = new ResourceMetadataKeyEnum<String>("VERSION") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public String get(IResource theResource) { public String get(IResource theResource) {
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION); return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION);
@ -419,18 +411,18 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(VERSION, theObject); theResource.getResourceMetadata().put(VERSION, theObject);
} }
}; };
/** /**
* The value for this key is the version ID of the resource object. * The value for this key is the version ID of the resource object.
* <p> * <p>
* Values for this key are of type <b>{@link IdDt}</b> * Values for this key are of type <b>{@link IdDt}</b>
* </p> * </p>
* *
* @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method * @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getVersionIdPart()} method
*/ */
@Deprecated @Deprecated
public static final ResourceMetadataKeyEnum<IdDt> VERSION_ID = new ResourceMetadataKeyEnum<IdDt>("VERSION_ID") { public static final ResourceMetadataKeyEnum<IdDt> VERSION_ID = new ResourceMetadataKeyEnum<IdDt>("VERSION_ID") {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Override @Override
public IdDt get(IResource theResource) { public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID); return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
@ -441,7 +433,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
theResource.getResourceMetadata().put(VERSION_ID, theObject); theResource.getResourceMetadata().put(VERSION_ID, theObject);
} }
}; };
private static final long serialVersionUID = 1L;
private final String myValue; private final String myValue;
public ResourceMetadataKeyEnum(String theValue) { public ResourceMetadataKeyEnum(String theValue) {
@ -504,12 +496,12 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
return new DecimalDt((Double) retValObj); return new DecimalDt((Double) retValObj);
} }
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
+ InstantDt.class.getCanonicalName()); + InstantDt.class.getCanonicalName());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static <T extends Enum<?>> T getEnumFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<T> theKey, Class<T> theEnumType, private static <T extends Enum<?>> T getEnumFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<T> theKey, Class<T> theEnumType,
IValueSetEnumBinder<T> theBinder) { IValueSetEnumBinder<T> theBinder) {
Object retValObj = theResourceMetadata.get(theKey); Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) { if (retValObj == null) {
return null; return null;
@ -519,7 +511,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
return theBinder.fromCodeString((String) retValObj); return theBinder.fromCodeString((String) retValObj);
} }
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
+ InstantDt.class.getCanonicalName()); + InstantDt.class.getCanonicalName());
} }
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) { private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
@ -558,11 +550,11 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
} else if (retValObj instanceof InstantDt) { } else if (retValObj instanceof InstantDt) {
if (((InstantDt) retValObj).isEmpty()) { if (((InstantDt) retValObj).isEmpty()) {
return null; return null;
} }
return (InstantDt) retValObj; return (InstantDt) retValObj;
} }
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
+ InstantDt.class.getCanonicalName()); + InstantDt.class.getCanonicalName());
} }
private static String getStringFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<String> theKey) { private static String getStringFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<String> theKey) {
@ -572,11 +564,11 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
} else if (retValObj instanceof String) { } else if (retValObj instanceof String) {
if (StringUtils.isBlank(((String) retValObj))) { if (StringUtils.isBlank(((String) retValObj))) {
return null; return null;
} }
return (String) retValObj; return (String) retValObj;
} }
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
+ String.class.getCanonicalName()); + String.class.getCanonicalName());
} }
private static IdDt toId(ResourceMetadataKeyEnum<?> theKey, Object retValObj) { private static IdDt toId(ResourceMetadataKeyEnum<?> theKey, Object retValObj) {
@ -596,18 +588,17 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
return new IdDt(((Number) retValObj).toString()); return new IdDt(((Number) retValObj).toString());
} }
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
+ IdDt.class.getCanonicalName()); + IdDt.class.getCanonicalName());
} }
public static abstract class ResourceMetadataKeySupportingAnyResource<T, T2> extends ResourceMetadataKeyEnum<T> { public static abstract class ResourceMetadataKeySupportingAnyResource<T, T2> extends ResourceMetadataKeyEnum<T> {
private static final long serialVersionUID = 1L;
public ResourceMetadataKeySupportingAnyResource(String theValue) { public ResourceMetadataKeySupportingAnyResource(String theValue) {
super(theValue); super(theValue);
} }
private static final long serialVersionUID = 1L;
public abstract T2 get(IAnyResource theResource); public abstract T2 get(IAnyResource theResource);
public abstract void put(IAnyResource theResource, T2 theObject); public abstract void put(IAnyResource theResource, T2 theObject);

View File

@ -38,6 +38,7 @@ import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@SuppressWarnings("WeakerAccess")
public abstract class BaseParser implements IParser { public abstract class BaseParser implements IParser {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseParser.class);
@ -156,7 +157,6 @@ public abstract class BaseParser implements IParser {
} }
private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) { private void containResourcesForEncoding(ContainedResources theContained, IBaseResource theResource, IBaseResource theTarget) {
Set<String> allIds = new HashSet<String>();
Map<String, IBaseResource> existingIdToContainedResource = null; Map<String, IBaseResource> existingIdToContainedResource = null;
if (theTarget instanceof IResource) { if (theTarget instanceof IResource) {
@ -167,9 +167,8 @@ public abstract class BaseParser implements IParser {
if (!nextId.startsWith("#")) { if (!nextId.startsWith("#")) {
nextId = '#' + nextId; nextId = '#' + nextId;
} }
allIds.add(nextId);
if (existingIdToContainedResource == null) { if (existingIdToContainedResource == null) {
existingIdToContainedResource = new HashMap<String, IBaseResource>(); existingIdToContainedResource = new HashMap<>();
} }
existingIdToContainedResource.put(nextId, next); existingIdToContainedResource.put(nextId, next);
} }
@ -182,9 +181,8 @@ public abstract class BaseParser implements IParser {
if (!nextId.startsWith("#")) { if (!nextId.startsWith("#")) {
nextId = '#' + nextId; nextId = '#' + nextId;
} }
allIds.add(nextId);
if (existingIdToContainedResource == null) { if (existingIdToContainedResource == null) {
existingIdToContainedResource = new HashMap<String, IBaseResource>(); existingIdToContainedResource = new HashMap<>();
} }
existingIdToContainedResource.put(nextId, next); existingIdToContainedResource.put(nextId, next);
} }
@ -480,7 +478,7 @@ public abstract class BaseParser implements IParser {
@Override @Override
public void setPreferTypes(List<Class<? extends IBaseResource>> thePreferTypes) { public void setPreferTypes(List<Class<? extends IBaseResource>> thePreferTypes) {
if (thePreferTypes != null) { if (thePreferTypes != null) {
ArrayList<Class<? extends IBaseResource>> types = new ArrayList<Class<? extends IBaseResource>>(); ArrayList<Class<? extends IBaseResource>> types = new ArrayList<>();
for (Class<? extends IBaseResource> next : thePreferTypes) { for (Class<? extends IBaseResource> next : thePreferTypes) {
if (Modifier.isAbstract(next.getModifiers()) == false) { if (Modifier.isAbstract(next.getModifiers()) == false) {
types.add(next); types.add(next);
@ -516,8 +514,7 @@ public abstract class BaseParser implements IParser {
} }
} }
List<T> newList = new ArrayList<T>(); List<T> newList = new ArrayList<>(theProfiles);
newList.addAll(theProfiles);
BaseRuntimeElementDefinition<?> idElement = myContext.getElementDefinition("id"); BaseRuntimeElementDefinition<?> idElement = myContext.getElementDefinition("id");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -1195,8 +1192,10 @@ public abstract class BaseParser implements IParser {
} }
public void addContained(IIdType theId, IBaseResource theResource) { public void addContained(IIdType theId, IBaseResource theResource) {
myResourceToId.put(theResource, theId); if (!hasId(theId)) {
myResources.add(theResource); myResourceToId.put(theResource, theId);
myResources.add(theResource);
}
} }
public List<IBaseResource> getContainedResources() { public List<IBaseResource> getContainedResources() {
@ -1207,6 +1206,15 @@ public abstract class BaseParser implements IParser {
return myResourceToId.get(theNext); return myResourceToId.get(theNext);
} }
public boolean hasId(IIdType theId) {
for (IIdType next : myResourceToId.values()) {
if (Objects.equals(next.getValue(), theId.getValue())) {
return true;
}
}
return false;
}
public boolean isEmpty() { public boolean isEmpty() {
return myResourceToId.isEmpty(); return myResourceToId.isEmpty();
} }

View File

@ -55,7 +55,7 @@ public interface IParserErrorHandler {
void incorrectJsonType(IParseLocation theLocation, String theElementName, ValueType theExpectedValueType, ScalarType theExpectedScalarType, ValueType theFoundValueType, ScalarType theFoundScalarType); void incorrectJsonType(IParseLocation theLocation, String theElementName, ValueType theExpectedValueType, ScalarType theExpectedScalarType, ValueType theFoundValueType, ScalarType theFoundScalarType);
/** /**
* The parser detected an atttribute value that was invalid (such as: empty "" values are not permitted) * The parser detected an attribute value that was invalid (such as: empty "" values are not permitted)
* *
* @param theLocation * @param theLocation
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change. * The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
@ -70,7 +70,7 @@ public interface IParserErrorHandler {
* *
* @param theLocation * @param theLocation
* The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change. * The location in the document. Note that this may be <code>null</code> as the ParseLocation feature is experimental. Use with caution, as the API may change.
* @param theReference The actual invalid reference (e.g. "#3") * @param theElementName The missing element name
* @since 2.1 * @since 2.1
*/ */
void missingRequiredElement(IParseLocation theLocation, String theElementName); void missingRequiredElement(IParseLocation theLocation, String theElementName);
@ -123,7 +123,7 @@ public interface IParserErrorHandler {
* type which will currently always be set to null. This interface is included here so that * type which will currently always be set to null. This interface is included here so that
* locations can be added to the API in a future release without changing the API. * locations can be added to the API in a future release without changing the API.
*/ */
public interface IParseLocation { interface IParseLocation {
/** /**
* Returns the name of the parent element (the element containing the element currently being parsed) * Returns the name of the parent element (the element containing the element currently being parsed)

View File

@ -615,8 +615,8 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
write(theEventWriter, "resourceType", resDef.getName()); write(theEventWriter, "resourceType", resDef.getName());
if (theResourceId != null && theResourceId.hasIdPart()) { if (theResourceId != null && theResourceId.hasIdPart()) {
write(theEventWriter, "id", theResourceId.getIdPart()); write(theEventWriter, "id", theResourceId.getIdPart());
final List<HeldExtension> extensions = new ArrayList<HeldExtension>(0); final List<HeldExtension> extensions = new ArrayList<>(0);
final List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0); final List<HeldExtension> modifierExtensions = new ArrayList<>(0);
// Undeclared extensions // Undeclared extensions
extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null); extractUndeclaredExtensions(theResourceId, extensions, modifierExtensions, null, null);
boolean haveExtension = false; boolean haveExtension = false;
@ -724,14 +724,11 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
/** /**
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object * This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object
* called _name): resource extensions, and extension extensions * called _name): resource extensions, and extension extensions
*
* @param theChildElem
* @param theParent
*/ */
private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonLikeWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, private void extractAndWriteExtensionsAsDirectChild(IBase theElement, JsonLikeWriter theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
IBaseResource theResource, CompositeChildElement theChildElem, CompositeChildElement theParent) throws IOException { IBaseResource theResource, CompositeChildElement theChildElem, CompositeChildElement theParent) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0); List<HeldExtension> extensions = new ArrayList<>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0); List<HeldExtension> modifierExtensions = new ArrayList<>(0);
// Undeclared extensions // Undeclared extensions
extractUndeclaredExtensions(theElement, extensions, modifierExtensions, theChildElem, theParent); extractUndeclaredExtensions(theElement, extensions, modifierExtensions, theChildElem, theParent);
@ -1053,7 +1050,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
} else { } else {
parentElementName = "extension"; parentElementName = "extension";
} }
getErrorHandler().missingRequiredElement(new ParseLocation(parentElementName), "url"); getErrorHandler().missingRequiredElement(new ParseLocation().setParentElementName(parentElementName), "url");
url = null; url = null;
} else { } else {
url = getExtensionUrl(jsonElement.getAsString()); url = getExtensionUrl(jsonElement.getAsString());

View File

@ -29,9 +29,8 @@ class ParseLocation implements IParseLocation {
/** /**
* Constructor * Constructor
*/ */
public ParseLocation(String theParentElementName) { public ParseLocation() {
super(); super();
myParentElementName = theParentElementName;
} }
@Override @Override
@ -39,4 +38,9 @@ class ParseLocation implements IParseLocation {
return myParentElementName; return myParentElementName;
} }
public ParseLocation setParentElementName(String theParentElementName) {
myParentElementName = theParentElementName;
return this;
}
} }

View File

@ -806,7 +806,7 @@ class ParserState<T> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<IBase> securityLabels = (List<IBase>) myMap.get(ResourceMetadataKeyEnum.SECURITY_LABELS); List<IBase> securityLabels = (List<IBase>) myMap.get(ResourceMetadataKeyEnum.SECURITY_LABELS);
if (securityLabels == null) { if (securityLabels == null) {
securityLabels = new ArrayList<IBase>(); securityLabels = new ArrayList<>();
myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels); myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels);
} }
IBase securityLabel = myContext.getVersion().newCodingDt(); IBase securityLabel = myContext.getVersion().newCodingDt();

View File

@ -139,7 +139,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
Attribute urlAttr = elem.getAttributeByName(new QName("url")); Attribute urlAttr = elem.getAttributeByName(new QName("url"));
String url; String url;
if (urlAttr == null || isBlank(urlAttr.getValue())) { if (urlAttr == null || isBlank(urlAttr.getValue())) {
getErrorHandler().missingRequiredElement(new ParseLocation("extension"), "url"); getErrorHandler().missingRequiredElement(new ParseLocation().setParentElementName("extension"), "url");
url = null; url = null;
} else { } else {
url = urlAttr.getValue(); url = urlAttr.getValue();
@ -149,7 +149,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
Attribute urlAttr = elem.getAttributeByName(new QName("url")); Attribute urlAttr = elem.getAttributeByName(new QName("url"));
String url; String url;
if (urlAttr == null || isBlank(urlAttr.getValue())) { if (urlAttr == null || isBlank(urlAttr.getValue())) {
getErrorHandler().missingRequiredElement(new ParseLocation("modifierExtension"), "url"); getErrorHandler().missingRequiredElement(new ParseLocation().setParentElementName("modifierExtension"), "url");
url = null; url = null;
} else { } else {
url = urlAttr.getValue(); url = urlAttr.getValue();

View File

@ -19,12 +19,14 @@ package ca.uhn.fhir.rest.api;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import java.util.*;
import org.apache.commons.lang3.ObjectUtils;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import org.apache.commons.lang3.ObjectUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public enum EncodingEnum { public enum EncodingEnum {
@ -40,39 +42,40 @@ public enum EncodingEnum {
public IParser newParser(FhirContext theContext) { public IParser newParser(FhirContext theContext) {
return theContext.newXmlParser(); return theContext.newXmlParser();
} }
} };
; /**
* "json"
/** "json" */ */
public static final String JSON_PLAIN_STRING = "json"; public static final String JSON_PLAIN_STRING = "json";
private static Map<String, EncodingEnum> ourContentTypeToEncoding; /**
* "xml"
private static Map<String, EncodingEnum> ourContentTypeToEncodingNonLegacy; */
private static Map<String, EncodingEnum> ourContentTypeToEncodingStrict;
/** "xml" */
public static final String XML_PLAIN_STRING = "xml"; public static final String XML_PLAIN_STRING = "xml";
private static Map<String, EncodingEnum> ourContentTypeToEncoding;
private static Map<String, EncodingEnum> ourContentTypeToEncodingLegacy;
private static Map<String, EncodingEnum> ourContentTypeToEncodingStrict;
static { static {
ourContentTypeToEncoding = new HashMap<String, EncodingEnum>(); ourContentTypeToEncoding = new HashMap<>();
ourContentTypeToEncodingNonLegacy = new HashMap<String, EncodingEnum>(); ourContentTypeToEncodingLegacy = new HashMap<>();
for (EncodingEnum next : values()) { for (EncodingEnum next : values()) {
ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy, next); ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy, next);
ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy, next); ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy, next);
ourContentTypeToEncodingNonLegacy.put(next.myResourceContentTypeNonLegacy, next); ourContentTypeToEncodingLegacy.put(next.myResourceContentTypeLegacy, next);
/* /*
* See #346 * See #346
*/ */
ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy.replace('+', ' '), next); ourContentTypeToEncoding.put(next.myResourceContentTypeNonLegacy.replace('+', ' '), next);
ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy.replace('+', ' '), next); ourContentTypeToEncoding.put(next.myResourceContentTypeLegacy.replace('+', ' '), next);
ourContentTypeToEncodingNonLegacy.put(next.myResourceContentTypeNonLegacy.replace('+', ' '), next); ourContentTypeToEncodingLegacy.put(next.myResourceContentTypeLegacy.replace('+', ' '), next);
} }
// Add before we add the lenient ones // Add before we add the lenient ones
ourContentTypeToEncodingStrict = Collections.unmodifiableMap(new HashMap<String, EncodingEnum>(ourContentTypeToEncoding)); ourContentTypeToEncodingStrict = Collections.unmodifiableMap(new HashMap<>(ourContentTypeToEncoding));
/* /*
* These are wrong, but we add them just to be tolerant of other * These are wrong, but we add them just to be tolerant of other
@ -89,7 +92,7 @@ public enum EncodingEnum {
ourContentTypeToEncoding.put(JSON_PLAIN_STRING, JSON); ourContentTypeToEncoding.put(JSON_PLAIN_STRING, JSON);
ourContentTypeToEncoding.put(XML_PLAIN_STRING, XML); ourContentTypeToEncoding.put(XML_PLAIN_STRING, XML);
ourContentTypeToEncodingNonLegacy = Collections.unmodifiableMap(ourContentTypeToEncodingNonLegacy); ourContentTypeToEncodingLegacy = Collections.unmodifiableMap(ourContentTypeToEncodingLegacy);
} }
@ -107,10 +110,6 @@ public enum EncodingEnum {
return myFormatContentType; return myFormatContentType;
} }
public String getRequestContentType() {
return myFormatContentType;
}
/** /**
* Will return application/xml+fhir style * Will return application/xml+fhir style
*/ */
@ -150,7 +149,7 @@ public enum EncodingEnum {
/** /**
* Returns the encoding for a given content type, or <code>null</code> if no encoding * Returns the encoding for a given content type, or <code>null</code> if no encoding
* is found. * is found.
* <p> * <p>
* <b>This method is lenient!</b> Things like "application/xml" will return {@link EncodingEnum#XML} * <b>This method is lenient!</b> Things like "application/xml" will return {@link EncodingEnum#XML}
* even if the "+fhir" part is missing from the expected content type. * even if the "+fhir" part is missing from the expected content type.
@ -163,19 +162,23 @@ public enum EncodingEnum {
/** /**
* Returns the encoding for a given content type, or <code>null</code> if no encoding * Returns the encoding for a given content type, or <code>null</code> if no encoding
* is found. * is found.
* <p> * <p>
* <b>This method is NOT lenient!</b> Things like "application/xml" will return <code>null</code> * <b>This method is NOT lenient!</b> Things like "application/xml" will return <code>null</code>
* </p> * </p>
*
* @see #forContentType(String) * @see #forContentType(String)
*/ */
public static EncodingEnum forContentTypeStrict(String theContentType) { public static EncodingEnum forContentTypeStrict(String theContentType) {
return ourContentTypeToEncodingStrict.get(theContentType); return ourContentTypeToEncodingStrict.get(theContentType);
} }
public static boolean isNonLegacy(String theFormat) { /**
return ourContentTypeToEncodingNonLegacy.containsKey(theFormat); * Is the given type a FHIR legacy (pre-DSTU3) content type?
*/
public static boolean isLegacy(String theFormat) {
return ourContentTypeToEncodingLegacy.containsKey(theFormat);
} }
} }

View File

@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.client.api;
* #L% * #L%
*/ */
import ca.uhn.fhir.util.StopWatch;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
@ -57,7 +59,7 @@ public interface IHttpResponse {
* <p> * <p>
* Buffering the message entity data allows for multiple invocations of * Buffering the message entity data allows for multiple invocations of
* {@code readEntity(...)} methods on the response instance. * {@code readEntity(...)} methods on the response instance.
* *
* @since 2.2 * @since 2.2
*/ */
void bufferEntity() throws IOException; void bufferEntity() throws IOException;
@ -65,31 +67,40 @@ public interface IHttpResponse {
/** /**
* Close the response * Close the response
*/ */
public void close(); void close();
/** /**
* Returna reader for the response entity * Returna reader for the response entity
*/ */
public Reader createReader() throws IOException; Reader createReader() throws IOException;
/** /**
* Get map of the response headers and corresponding string values. * Get map of the response headers and corresponding string values.
* *
* @return response headers as a map header keys and they values. * @return response headers as a map header keys and they values.
*/ */
public Map<String, List<String>> getAllHeaders(); Map<String, List<String>> getAllHeaders();
/** /**
* Return all headers in the response with the given type * Return all headers in the response with the given type
*/ */
public List<String> getHeaders(String theName); List<String> getHeaders(String theName);
/** /**
* Extracts {@code Content-Type} value from the response exactly as * Extracts {@code Content-Type} value from the response exactly as
* specified by the {@code Content-Type} header. Returns {@code null} * specified by the {@code Content-Type} header. Returns {@code null}
* if not specified. * if not specified.
*/ */
public String getMimeType(); String getMimeType();
/**
* @return Returns a StopWatch that was started right before
* the client request was started. The time returned by this
* client includes any time that was spent within the HTTP
* library (possibly including waiting for a connection, and
* any network activity)
*/
StopWatch getRequestStopWatch();
/** /**
* @return the native response, depending on the client library used * @return the native response, depending on the client library used
@ -98,21 +109,20 @@ public interface IHttpResponse {
/** /**
* Get the status code associated with the response. * Get the status code associated with the response.
* *
* @return the response status code. * @return the response status code.
*/ */
public int getStatus(); int getStatus();
/** /**
* Get the response status information reason phrase associated with the response. * Get the response status information reason phrase associated with the response.
* *
* @return the reason phrase. * @return the reason phrase.
*/ */
public String getStatusInfo(); String getStatusInfo();
/** /**
* Read the message entity input stream as an InputStream. * Read the message entity input stream as an InputStream.
*/ */
public InputStream readEntity() throws IOException; InputStream readEntity() throws IOException;
} }

View File

@ -20,25 +20,32 @@ package ca.uhn.fhir.rest.param;
* #L% * #L%
*/ */
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterAnd; import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.rest.api.QualifiedParamList; import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import java.util.ArrayList;
import java.util.List;
public abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implements IQueryParameterAnd<T> { public abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implements IQueryParameterAnd<T> {
private List<T> myValues=new ArrayList<T>(); private List<T> myValues = new ArrayList<>();
public abstract BaseAndListParam<T> addAnd(T theValue);
public BaseAndListParam<T> addValue(T theValue) { public BaseAndListParam<T> addValue(T theValue) {
myValues.add(theValue); myValues.add(theValue);
return this; return this;
} }
public abstract BaseAndListParam<T> addAnd(T theValue); @Override
public List<T> getValuesAsQueryTokens() {
return myValues;
}
abstract T newInstance();
@Override @Override
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, List<QualifiedParamList> theParameters) throws InvalidRequestException { public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, List<QualifiedParamList> theParameters) throws InvalidRequestException {
@ -50,11 +57,9 @@ public abstract class BaseAndListParam<T extends IQueryParameterOr<?>> implement
} }
} }
abstract T newInstance();
@Override @Override
public List<T> getValuesAsQueryTokens() { public String toString() {
return myValues; return myValues.toString();
} }

View File

@ -20,23 +20,35 @@ package ca.uhn.fhir.rest.param;
* #L% * #L%
*/ */
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.QualifiedParamList; import ca.uhn.fhir.rest.api.QualifiedParamList;
import java.util.ArrayList;
import java.util.List;
abstract class BaseOrListParam<MT extends BaseOrListParam<?, ?>, PT extends IQueryParameterType> implements IQueryParameterOr<PT> { abstract class BaseOrListParam<MT extends BaseOrListParam<?, ?>, PT extends IQueryParameterType> implements IQueryParameterOr<PT> {
private List<PT> myList=new ArrayList<PT>(); private List<PT> myList = new ArrayList<>();
@SuppressWarnings("unchecked")
public MT add(PT theParameter) {
if (theParameter != null) {
myList.add(theParameter);
}
return (MT) this;
}
public abstract MT addOr(PT theParameter);
@Override
public List<PT> getValuesAsQueryTokens() {
return myList;
}
abstract PT newInstance();
// public void addToken(T theParam) {
// Validate.notNull(theParam,"Param can not be null");
// myList.add(theParam);
// }
@Override @Override
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, QualifiedParamList theParameters) { public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, QualifiedParamList theParameters) {
myList.clear(); myList.clear();
@ -47,21 +59,9 @@ abstract class BaseOrListParam<MT extends BaseOrListParam<?, ?>, PT extends IQue
} }
} }
abstract PT newInstance();
public abstract MT addOr(PT theParameter);
@SuppressWarnings("unchecked")
public MT add(PT theParameter) {
if (theParameter != null) {
myList.add(theParameter);
}
return (MT) this;
}
@Override @Override
public List<PT> getValuesAsQueryTokens() { public String toString() {
return myList; return myList.toString();
} }
} }

View File

@ -107,5 +107,4 @@ public abstract class BaseParamWithPrefix<T extends BaseParam> extends BaseParam
myPrefix = thePrefix; myPrefix = thePrefix;
return (T) this; return (T) this;
} }
} }

View File

@ -20,21 +20,26 @@ package ca.uhn.fhir.rest.param;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import java.util.*; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.util.ValidateUtil;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.context.FhirContext; import java.util.Collections;
import ca.uhn.fhir.model.api.IQueryParameterOr; import java.util.Date;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import java.util.List;
import ca.uhn.fhir.model.primitive.*; import java.util.Objects;
import ca.uhn.fhir.rest.api.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import ca.uhn.fhir.util.ValidateUtil;
public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQueryParameterType , */IQueryParameterOr<DateParam> { public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQueryParameterType , */IQueryParameterOr<DateParam> {
@ -222,6 +227,23 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
} }
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DateParam)) {
return false;
}
DateParam other = (DateParam) obj;
return Objects.equals(getValue(), other.getValue()) &&
Objects.equals(getPrefix(), other.getPrefix());
}
@Override
public int hashCode() {
return Objects.hash(getValue(), getPrefix());
}
@Override @Override
public String toString() { public String toString() {
@ -241,8 +263,5 @@ public class DateParam extends BaseParamWithPrefix<DateParam> implements /*IQuer
protected boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision) { protected boolean isPrecisionAllowed(TemporalPrecisionEnum thePrecision) {
return true; return true;
} }
} }
} }

View File

@ -413,6 +413,24 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
} }
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DateRangeParam)) {
return false;
}
DateRangeParam other = (DateRangeParam) obj;
return Objects.equals(myLowerBound, other.myLowerBound) &&
Objects.equals(myUpperBound, other.myUpperBound);
}
@Override
public int hashCode() {
return Objects.hash(myLowerBound, myUpperBound);
}
@Override @Override
public String toString() { public String toString() {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
@ -484,7 +502,5 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
throw new DataFormatException("Upper bound comparator must be < or <=, can not be " + myUpperBound.getPrefix().getValue()); throw new DataFormatException("Upper bound comparator must be < or <=, can not be " + myUpperBound.getPrefix().getValue());
} }
} }
} }
} }

View File

@ -32,9 +32,9 @@ import java.util.List;
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -192,10 +192,10 @@ public class ParameterUtil {
} }
public static Object fromInteger(Class<?> theType, IntegerDt theArgument) { public static Object fromInteger(Class<?> theType, IntegerDt theArgument) {
if (theArgument == null) {
return null;
}
if (theType.equals(Integer.class)) { if (theType.equals(Integer.class)) {
if (theArgument == null) {
return null;
}
return theArgument.getValue(); return theArgument.getValue();
} }
IPrimitiveType<?> retVal = (IPrimitiveType<?>) ReflectionUtil.newInstance(theType); IPrimitiveType<?> retVal = (IPrimitiveType<?>) ReflectionUtil.newInstance(theType);

View File

@ -104,24 +104,33 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/
void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) { void doSetValueAsQueryToken(FhirContext theContext, String theParamName, String theQualifier, String theValue) {
String q = theQualifier; String q = theQualifier;
String resourceType = null; String resourceType = null;
boolean skipSetValue = false;
if (isNotBlank(q)) { if (isNotBlank(q)) {
if (q.startsWith(":")) { if (q.startsWith(":")) {
int nextIdx = q.indexOf('.'); int nextIdx = q.indexOf('.');
if (nextIdx != -1) { if (nextIdx != -1) {
resourceType = q.substring(1, nextIdx); resourceType = q.substring(1, nextIdx);
myChain = q.substring(nextIdx + 1); myChain = q.substring(nextIdx + 1);
// type is explicitly defined so use it
myId.setParts(null, resourceType, theValue, null);
skipSetValue = true;
} else { } else {
resourceType = q.substring(1); resourceType = q.substring(1);
} }
} else if (q.startsWith(".")) { } else if (q.startsWith(".")) {
myChain = q.substring(1); myChain = q.substring(1);
// type not defined but this is a chain, so treat value as opaque
myId.setParts(null, null, theValue, null);
skipSetValue = true;
} }
} }
setValue(theValue); if (!skipSetValue) {
setValue(theValue);
if (isNotBlank(resourceType) && isBlank(getResourceType())) { if (isNotBlank(resourceType) && isBlank(getResourceType())) {
setValue(resourceType + '/' + theValue); setValue(resourceType + '/' + theValue);
}
} }
} }

View File

@ -69,14 +69,12 @@ public class TokenOrListParam extends BaseOrListParam<TokenOrListParam, TokenPar
/** /**
* Add a new token to this list * Add a new token to this list
* * @param theSystem
* @param theSystem
* The system to use for the one token to pre-populate in this list * The system to use for the one token to pre-populate in this list
* @param theValue
* The value to use for the one token to pre-populate in this list
*/ */
public void add(String theSystem, String theValue) { public TokenOrListParam add(String theSystem, String theValue) {
add(new TokenParam(theSystem, theValue)); add(new TokenParam(theSystem, theValue));
return this;
} }
public List<BaseCodingDt> getListAsCodings() { public List<BaseCodingDt> getListAsCodings() {

View File

@ -36,6 +36,45 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
*/ */
public class BundleUtil { public class BundleUtil {
/**
* @return Returns <code>null</code> if the link isn't found or has no value
*/
public static String getLinkUrlOfType(FhirContext theContext, IBaseBundle theBundle, String theLinkRelation) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
BaseRuntimeChildDefinition entryChild = def.getChildByName("link");
List<IBase> links = entryChild.getAccessor().getValues(theBundle);
for (IBase nextLink : links) {
boolean isRightRel = false;
BaseRuntimeElementCompositeDefinition relDef = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(nextLink.getClass());
BaseRuntimeChildDefinition relChild = relDef.getChildByName("relation");
List<IBase> relValues = relChild.getAccessor().getValues(nextLink);
for (IBase next : relValues) {
IPrimitiveType<?> nextValue = (IPrimitiveType<?>)next;
if (theLinkRelation.equals(nextValue.getValueAsString())) {
isRightRel = true;
}
}
if (!isRightRel) {
continue;
}
BaseRuntimeElementCompositeDefinition linkDef = (BaseRuntimeElementCompositeDefinition) theContext.getElementDefinition(nextLink.getClass());
BaseRuntimeChildDefinition urlChild = linkDef.getChildByName("url");
List<IBase> values = urlChild.getAccessor().getValues(nextLink);
for (IBase nextUrl : values) {
IPrimitiveType<?> nextValue = (IPrimitiveType<?>)nextUrl;
if (isNotBlank(nextValue.getValueAsString())) {
return nextValue.getValueAsString();
}
}
}
return null;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static List<Pair<String, IBaseResource>> getBundleEntryUrlsAndResources(FhirContext theContext, IBaseBundle theBundle) { public static List<Pair<String, IBaseResource>> getBundleEntryUrlsAndResources(FhirContext theContext, IBaseBundle theBundle) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
@ -50,7 +89,7 @@ public class BundleUtil {
BaseRuntimeChildDefinition urlChild = requestDef.getChildByName("url"); BaseRuntimeChildDefinition urlChild = requestDef.getChildByName("url");
List<Pair<String, IBaseResource>> retVal = new ArrayList<Pair<String,IBaseResource>>(entries.size()); List<Pair<String, IBaseResource>> retVal = new ArrayList<>(entries.size());
for (IBase nextEntry : entries) { for (IBase nextEntry : entries) {
String url = null; String url = null;
@ -88,7 +127,7 @@ public class BundleUtil {
* Extract all of the resources from a given bundle * Extract all of the resources from a given bundle
*/ */
public static List<BundleEntryParts> toListOfEntries(FhirContext theContext, IBaseBundle theBundle) { public static List<BundleEntryParts> toListOfEntries(FhirContext theContext, IBaseBundle theBundle) {
List<BundleEntryParts> retVal = new ArrayList<BundleEntryParts>(); List<BundleEntryParts> retVal = new ArrayList<>();
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry"); BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
@ -145,7 +184,7 @@ public class BundleUtil {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends IBaseResource> List<T> toListOfResourcesOfType(FhirContext theContext, IBaseBundle theBundle, Class<T> theTypeToInclude) { public static <T extends IBaseResource> List<T> toListOfResourcesOfType(FhirContext theContext, IBaseBundle theBundle, Class<T> theTypeToInclude) {
List<T> retVal = new ArrayList<T>(); List<T> retVal = new ArrayList<>();
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle); RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry"); BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
@ -170,7 +209,7 @@ public class BundleUtil {
private final RequestTypeEnum myRequestType; private final RequestTypeEnum myRequestType;
private final IBaseResource myResource; private final IBaseResource myResource;
private final String myUrl; private final String myUrl;
public BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource) { BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource) {
super(); super();
myRequestType = theRequestType; myRequestType = theRequestType;
myUrl = theUrl; myUrl = theUrl;

View File

@ -20,25 +20,23 @@ package ca.uhn.fhir.util;
* #L% * #L%
*/ */
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.primitive.StringDt;
public class DatatypeUtil { public class DatatypeUtil {
/** /**
* Convert a list of FHIR String objects to a set of native java Strings * Convert a list of FHIR String objects to a set of native java Strings
*/ */
public static Set<String> toStringSet(List<StringDt> theStringList) { public static Set<String> toStringSet(List<? extends IPrimitiveType<?>> theStringList) {
HashSet<String> retVal = new HashSet<String>(); HashSet<String> retVal = new HashSet<>();
if (theStringList != null) { if (theStringList != null) {
for (StringDt string : theStringList) { for (IPrimitiveType<?> string : theStringList) {
if (string != null && string.getValue()!=null) { if (string != null && string.getValue()!=null) {
retVal.add(string.getValue()); retVal.add(string.getValueAsString());
} }
} }
} }

View File

@ -0,0 +1,52 @@
package ca.uhn.fhir.util;
/*-
* #%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 ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.util.ArrayList;
import java.util.List;
public class SearchParameterUtil {
public static List<String> getBaseAsStrings(FhirContext theContext, IBaseResource theResource) {
Validate.notNull(theContext, "theContext must not be null");
Validate.notNull(theResource, "theResource must not be null");
RuntimeResourceDefinition def = theContext.getResourceDefinition(theResource);
BaseRuntimeChildDefinition base = def.getChildByName("base");
List<IBase> baseValues = base.getAccessor().getValues(theResource);
List<String> retVal = new ArrayList<>();
for (IBase next : baseValues) {
IPrimitiveType<?> nextPrimitive = (IPrimitiveType<?>) next;
retVal.add(nextPrimitive.getValueAsString());
}
return retVal;
}
}

View File

@ -0,0 +1,202 @@
package ca.uhn.fhir.util;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.time.DateUtils;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/*
* #%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%
*/
/**
* @since HAPI FHIR 3.3.0
*/
public class StopWatch {
private static final NumberFormat DAY_FORMAT = new DecimalFormat("0.0");
private static final NumberFormat TEN_DAY_FORMAT = new DecimalFormat("0");
private static Long ourNowForUnitTest;
private long myStarted = now();
/**
* Constructor
*/
public StopWatch() {
super();
}
/**
* Constructor
*
* @param theStart The time to record as the start for this timer
*/
public StopWatch(Date theStart) {
myStarted = theStart.getTime();
}
public String formatThroughput(int theNumOperations, TimeUnit theUnit) {
double throughput = getThroughput(theNumOperations, theUnit);
return new DecimalFormat("0.0").format(throughput);
}
/**
* Given an amount of something completed so far, and a total amount, calculates how long it will take for something to complete
*
* @param theCompleteToDate The amount so far
* @param theTotal The total (must be higher than theCompleteToDate
* @return A formatted amount of time
*/
public String getEstimatedTimeRemaining(double theCompleteToDate, double theTotal) {
double millis = getMillis();
long millisRemaining = (long) (((theTotal / theCompleteToDate) * millis) - (millis));
return formatMillis(millisRemaining);
}
public long getMillis(Date theNow) {
return theNow.getTime() - myStarted;
}
public long getMillis() {
long now = now();
return now - myStarted;
}
public long getMillisAndRestart() {
long now = now();
long retVal = now - myStarted;
myStarted = now;
return retVal;
}
/**
* @param theNumOperations Ok for this to be 0, it will be treated as 1
*/
public int getMillisPerOperation(int theNumOperations) {
return (int) (((double) getMillis()) / Math.max(1.0, theNumOperations));
}
public Date getStartedDate() {
return new Date(myStarted);
}
public double getThroughput(int theNumOperations, TimeUnit theUnit) {
if (theNumOperations <= 0) {
return 0.0f;
}
long millisElapsed = Math.max(1, getMillis());
long periodMillis = theUnit.toMillis(1);
double numerator = theNumOperations;
double denominator = ((double) millisElapsed) / ((double) periodMillis);
return numerator / denominator;
}
public void restart() {
myStarted = now();
}
/**
* Formats value in an appropriate format. See {@link #formatMillis(long)}}
* for a description of the format
*
* @see #formatMillis(long)
*/
@Override
public String toString() {
return formatMillis(getMillis());
}
/**
* Append a right-aligned and zero-padded numeric value to a `StringBuilder`.
*/
static private void append(StringBuilder tgt, String pfx, int dgt, long val) {
tgt.append(pfx);
if (dgt > 1) {
int pad = (dgt - 1);
for (long xa = val; xa > 9 && pad > 0; xa /= 10) {
pad--;
}
for (int xa = 0; xa < pad; xa++) {
tgt.append('0');
}
}
tgt.append(val);
}
/**
* Formats a number of milliseconds for display (e.g.
* in a log file), tailoring the output to how big
* the value actually is.
* <p>
* Example outputs:
* </p>
* <ul>
* <li>133ms</li>
* <li>00:00:10.223</li>
* <li>1.7 days</li>
* <li>64 days</li>
* </ul>
*/
public static String formatMillis(long val) {
StringBuilder buf = new StringBuilder(20);
if (val < (10 * DateUtils.MILLIS_PER_SECOND)) {
buf.append(val);
buf.append("ms");
} else if (val >= DateUtils.MILLIS_PER_DAY) {
double days = (double) val / DateUtils.MILLIS_PER_DAY;
if (days >= 10) {
buf.append(TEN_DAY_FORMAT.format(days));
buf.append(" days");
} else if (days != 1.0f) {
buf.append(DAY_FORMAT.format(days));
buf.append(" days");
} else {
buf.append(DAY_FORMAT.format(days));
buf.append(" day");
}
} else {
append(buf, "", 2, ((val % DateUtils.MILLIS_PER_DAY) / DateUtils.MILLIS_PER_HOUR));
append(buf, ":", 2, ((val % DateUtils.MILLIS_PER_HOUR) / DateUtils.MILLIS_PER_MINUTE));
append(buf, ":", 2, ((val % DateUtils.MILLIS_PER_MINUTE) / DateUtils.MILLIS_PER_SECOND));
if (val <= DateUtils.MILLIS_PER_MINUTE) {
append(buf, ".", 3, (val % DateUtils.MILLIS_PER_SECOND));
}
}
return buf.toString();
}
private static long now() {
if (ourNowForUnitTest != null) {
return ourNowForUnitTest;
}
return System.currentTimeMillis();
}
@VisibleForTesting
static void setNowForUnitTestForUnitTest(Long theNowForUnitTest) {
ourNowForUnitTest = theNowForUnitTest;
}
}

View File

@ -0,0 +1,64 @@
package ca.uhn.fhir.util;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.thymeleaf.util.Validate;
import java.util.List;
/*
* #%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%
*/
/**
* Utilities for working with the subscription resource
*/
public class SubscriptionUtil {
private static void populatePrimitiveValue(FhirContext theContext, IBaseResource theSubscription, String theChildName, String theValue) {
RuntimeResourceDefinition def = theContext.getResourceDefinition(theSubscription);
Validate.isTrue(def.getName().equals("Subscription"), "theResource is not a subscription");
BaseRuntimeChildDefinition statusChild = def.getChildByName(theChildName);
List<IBase> entries = statusChild.getAccessor().getValues(theSubscription);
IPrimitiveType<?> instance;
if (entries.size() == 0) {
BaseRuntimeElementDefinition<?> statusElement = statusChild.getChildByName(theChildName);
instance = (IPrimitiveType<?>) statusElement.newInstance(statusChild.getInstanceConstructorArguments());
statusChild.getMutator().addValue(theSubscription, instance);
} else {
instance = (IPrimitiveType<?>) entries.get(0);
}
instance.setValueAsString(theValue);
}
public static void setReason(FhirContext theContext, IBaseResource theSubscription, String theMessage) {
populatePrimitiveValue(theContext, theSubscription, "reason", theMessage);
}
public static void setStatus(FhirContext theContext, IBaseResource theSubscription, String theStatus) {
populatePrimitiveValue(theContext, theSubscription, "status", theStatus);
}
}

View File

@ -44,12 +44,12 @@ public interface IBaseResource extends IBase, IElement {
/** /**
* Include constant for <code>*</code> (return all includes) * Include constant for <code>*</code> (return all includes)
*/ */
public static final Include INCLUDE_ALL = new Include("*", false).toLocked(); Include INCLUDE_ALL = new Include("*", false).toLocked();
/** /**
* Include set containing only {@link #INCLUDE_ALL} * Include set containing only {@link #INCLUDE_ALL}
*/ */
public static final Set<Include> WILDCARD_ALL_SET = Collections.unmodifiableSet(new HashSet<Include>(Arrays.asList(INCLUDE_ALL))); Set<Include> WILDCARD_ALL_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(INCLUDE_ALL)));
IIdType getIdElement(); IIdType getIdElement();

View File

@ -49,6 +49,7 @@ ca.uhn.fhir.parser.ParserState.wrongResourceTypeFound=Incorrect resource type fo
ca.uhn.fhir.rest.server.RestfulServer.getPagesNonHttpGet=Requests for _getpages must use HTTP GET ca.uhn.fhir.rest.server.RestfulServer.getPagesNonHttpGet=Requests for _getpages must use HTTP GET
ca.uhn.fhir.rest.server.RestfulServer.unknownMethod=Invalid request: The FHIR endpoint on this server does not know how to handle {0} operation[{1}] with parameters [{2}] ca.uhn.fhir.rest.server.RestfulServer.unknownMethod=Invalid request: The FHIR endpoint on this server does not know how to handle {0} operation[{1}] with parameters [{2}]
ca.uhn.fhir.rest.server.RestfulServer.rootRequest=This is the base URL of FHIR server. Unable to handle this request, as it does not contain a resource type or operation name. ca.uhn.fhir.rest.server.RestfulServer.rootRequest=This is the base URL of FHIR server. Unable to handle this request, as it does not contain a resource type or operation name.
ca.uhn.fhir.rest.server.RestfulServer.rootRequest.multitenant=This is the base URL of a multitenant FHIR server. Unable to handle this request, as it does not contain a tenant ID.
ca.uhn.fhir.validation.ValidationContext.unableToDetermineEncoding=Unable to determine encoding (e.g. XML / JSON) on validation input. Is this a valid FHIR resource body? ca.uhn.fhir.validation.ValidationContext.unableToDetermineEncoding=Unable to determine encoding (e.g. XML / JSON) on validation input. Is this a valid FHIR resource body?
ca.uhn.fhir.validation.FhirValidator.noPhlocWarningOnStartup=Phloc-schematron library not found on classpath, will not attempt to perform schematron validation ca.uhn.fhir.validation.FhirValidator.noPhlocWarningOnStartup=Phloc-schematron library not found on classpath, will not attempt to perform schematron validation
ca.uhn.fhir.validation.FhirValidator.noPhlocError=Phloc-schematron library not found on classpath, can not enable perform schematron validation ca.uhn.fhir.validation.FhirValidator.noPhlocError=Phloc-schematron library not found on classpath, can not enable perform schematron validation
@ -94,4 +95,3 @@ ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc.cannotCreateDuplicateCodeSystemUri=C
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted! ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!

View File

@ -0,0 +1,195 @@
package ca.uhn.fhir.util;
import org.apache.commons.lang3.time.DateUtils;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Test;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class StopWatchTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StopWatchTest.class);
@After
public void after() {
StopWatch.setNowForUnitTestForUnitTest(null);
}
private double calculateThroughput(int theMinutesElapsed, int theNumOperations) {
StopWatch sw = new StopWatch(DateUtils.addMinutes(new Date(), -theMinutesElapsed));
double throughput = sw.getThroughput(theNumOperations, TimeUnit.MINUTES);
ourLog.info("{} operations in {}ms = {} ops / second", theNumOperations, sw.getMillis(), throughput);
return throughput;
}
@Test
public void testEstimatedTimeRemainingOutOfOne() {
StopWatch.setNowForUnitTestForUnitTest(777777777L);
StopWatch sw = new StopWatch();
// Less than half
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("00:09:00", sw.getEstimatedTimeRemaining(0.1, 1.0));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("00:09:00", sw.getEstimatedTimeRemaining(1, 10));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE + 100);
assertEquals("00:09:00", sw.getEstimatedTimeRemaining(0.1, 1.0));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("00:19:00", sw.getEstimatedTimeRemaining(0.05, 1.0));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("00:39:00", sw.getEstimatedTimeRemaining(0.025, 1.0));
// More than half
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("00:01:00.000", sw.getEstimatedTimeRemaining(0.5, 1.0));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("00:00:59.760", sw.getEstimatedTimeRemaining(0.501, 1.0));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("00:00:40.000", sw.getEstimatedTimeRemaining(0.6, 1.0));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("6666ms", sw.getEstimatedTimeRemaining(0.9, 1.0));
StopWatch.setNowForUnitTestForUnitTest(777777777L + DateUtils.MILLIS_PER_MINUTE);
assertEquals("60ms", sw.getEstimatedTimeRemaining(0.999, 1.0));
}
@Test
public void testEstimatedTimeRemainingOutOfOneHundred() {
StopWatch.setNowForUnitTestForUnitTest(777777777L);
StopWatch sw = new StopWatch();
StopWatch.setNowForUnitTestForUnitTest(777777777L + (10 * DateUtils.MILLIS_PER_MINUTE));
assertEquals("01:30:00", sw.getEstimatedTimeRemaining(10, 100));
StopWatch.setNowForUnitTestForUnitTest(777777777L + (DateUtils.MILLIS_PER_MINUTE));
assertEquals("00:04:00", sw.getEstimatedTimeRemaining(20, 100));
StopWatch.setNowForUnitTestForUnitTest(777777777L + (30 * DateUtils.MILLIS_PER_MINUTE));
assertEquals("01:10:00", sw.getEstimatedTimeRemaining(30, 100));
StopWatch.setNowForUnitTestForUnitTest(777777777L + (40 * DateUtils.MILLIS_PER_MINUTE));
assertEquals("01:00:00", sw.getEstimatedTimeRemaining(40, 100));
StopWatch.setNowForUnitTestForUnitTest(777777777L + (50 * DateUtils.MILLIS_PER_MINUTE));
assertEquals("00:50:00", sw.getEstimatedTimeRemaining(50, 100));
StopWatch.setNowForUnitTestForUnitTest(777777777L + (60 * DateUtils.MILLIS_PER_MINUTE));
assertEquals("00:40:00", sw.getEstimatedTimeRemaining(60, 100));
StopWatch.setNowForUnitTestForUnitTest(777777777L + (60 * DateUtils.MILLIS_PER_MINUTE));
assertEquals("00:00:36.363", sw.getEstimatedTimeRemaining(99, 100));
StopWatch.setNowForUnitTestForUnitTest(777777777L + (60 * DateUtils.MILLIS_PER_MINUTE));
assertEquals("360ms", sw.getEstimatedTimeRemaining(99.99, 100));
}
@Test
public void testFormatMillis() {
assertEquals("1000ms", StopWatch.formatMillis(DateUtils.MILLIS_PER_SECOND));
assertEquals("00:01:00.000", StopWatch.formatMillis(DateUtils.MILLIS_PER_MINUTE));
assertEquals("00:01:01", StopWatch.formatMillis(DateUtils.MILLIS_PER_MINUTE + DateUtils.MILLIS_PER_SECOND));
assertEquals("01:00:00", StopWatch.formatMillis(DateUtils.MILLIS_PER_HOUR));
assertEquals("1.0 day", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY));
assertEquals("2.0 days", StopWatch.formatMillis(DateUtils.MILLIS_PER_DAY * 2));
assertEquals("2.0 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 2) + 1));
assertEquals("2.4 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 2) + (10 * DateUtils.MILLIS_PER_HOUR)));
assertEquals("11 days", StopWatch.formatMillis((DateUtils.MILLIS_PER_DAY * 11) + (10 * DateUtils.MILLIS_PER_HOUR)));
}
@Test
public void testFormatThroughput60Ops4Min() {
StopWatch sw = new StopWatch(DateUtils.addMinutes(new Date(), -4));
String throughput = sw.formatThroughput(60, TimeUnit.MINUTES);
ourLog.info("{} operations in {}ms = {} ops / second", 60, sw.getMillis(), throughput);
assertThat(throughput, oneOf("14.9", "15.0", "15.1", "14,9", "15,0", "15,1"));
}
@Test
public void testMillisPerOperation() {
int minutes = 60;
StopWatch sw = new StopWatch(DateUtils.addMinutes(new Date(), -minutes));
int numOperations = 60;
int millis = sw.getMillisPerOperation(numOperations);
ourLog.info("{} operations in {}ms = {}ms / operation", numOperations, minutes * DateUtils.MILLIS_PER_MINUTE, millis);
assertThat(millis, Matchers.lessThan(62000));
assertThat(millis, Matchers.greaterThan(58000));
}
@Test
public void testOperationThroughput30Ops1Min() {
double throughput = calculateThroughput(1, 30);
assertThat(throughput, greaterThan(29.0));
assertThat(throughput, lessThan(31.0));
}
@Test
public void testOperationThroughput60Ops1Min() {
double throughput = calculateThroughput(1, 60);
assertThat(throughput, greaterThan(59.0));
assertThat(throughput, lessThan(61.0));
}
@Test
public void testOperationThroughput60Ops4Min() {
double throughput = calculateThroughput(4, 60);
assertThat(throughput, greaterThan(14.0));
assertThat(throughput, lessThan(16.0));
}
@Test
public void testRestart() throws InterruptedException {
StopWatch sw = new StopWatch();
Thread.sleep(500);
sw.restart();
assertThat(sw.getMillis(), lessThan(100L));
}
@Test
public void testStopwatch() throws Exception {
StopWatch sw = new StopWatch();
Thread.sleep(100);
assertThat(sw.getMillis(new Date()), greaterThan(10L));
assertThat(sw.getMillis(), greaterThan(10L));
assertThat(sw.getStartedDate().getTime(), lessThan(System.currentTimeMillis()));
}
@Test
public void testStopwatchWithDate() throws Exception {
StopWatch sw = new StopWatch(new Date());
Thread.sleep(100);
assertThat(sw.getMillis(new Date()), greaterThan(10L));
assertThat(sw.getMillis(), greaterThan(10L));
assertThat(sw.getStartedDate().getTime(), lessThan(System.currentTimeMillis()));
}
@Test
public void testToString() throws Exception {
StopWatch sw = new StopWatch();
Thread.sleep(100);
String string = sw.toString();
ourLog.info(string);
assertThat(string, matchesPattern("^[0-9]{3,4}ms$"));
}
}

View File

@ -191,6 +191,23 @@
<artifactId>phloc-commons</artifactId> <artifactId>phloc-commons</artifactId>
</dependency> </dependency>
<!--
These have been added as explicit dependencies
as JDK9 no longer includes them by default
-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.fusesource.jansi</groupId> <groupId>org.fusesource.jansi</groupId>
<artifactId>jansi</artifactId> <artifactId>jansi</artifactId>

View File

@ -7,6 +7,7 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition; import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option; import org.apache.commons.cli.Option;
@ -163,7 +164,7 @@ public class ValidationDataUploader extends BaseCommand {
ValueSet next = (ValueSet) i.getResource(); ValueSet next = (ValueSet) i.getResource();
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading ValueSet {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading ValueSet {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); client.update().resource(next).execute();
count++; count++;
@ -182,7 +183,7 @@ public class ValidationDataUploader extends BaseCommand {
ValueSet next = (ValueSet) i.getResource(); ValueSet next = (ValueSet) i.getResource();
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading v3-codesystems ValueSet {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading v3-codesystems ValueSet {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); client.update().resource(next).execute();
count++; count++;
@ -200,7 +201,7 @@ public class ValidationDataUploader extends BaseCommand {
ValueSet next = (ValueSet) i.getResource(); ValueSet next = (ValueSet) i.getResource();
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading v2-tables ValueSet {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading v2-tables ValueSet {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); client.update().resource(next).execute();
count++; count++;
} }
@ -225,7 +226,7 @@ public class ValidationDataUploader extends BaseCommand {
} }
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading StructureDefinition {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading StructureDefinition {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
try { try {
client.update().resource(next).execute(); client.update().resource(next).execute();
} catch (Exception e) { } catch (Exception e) {
@ -268,12 +269,14 @@ public class ValidationDataUploader extends BaseCommand {
int bytes = ctx.newXmlParser().encodeResourceToString(next).length(); int bytes = ctx.newXmlParser().encodeResourceToString(next).length();
ourLog.info("Uploading ValueSet {}/{} : {} ({} bytes}", new Object[]{count, total, next.getIdElement().getValue(), bytes}); ourLog.info("Uploading ValueSet {}/{} : {} ({} bytes}", new Object[] {count, total, next.getIdElement().getValue(), bytes});
try { try {
IIdType id = client.update().resource(next).execute().getId(); IIdType id = client.update().resource(next).execute().getId();
ourLog.info(" - Got ID: {}", id.getValue()); ourLog.info(" - Got ID: {}", id.getValue());
} catch (UnprocessableEntityException e) { } catch (UnprocessableEntityException e) {
ourLog.warn("UnprocessableEntityException: " + e.toString()); ourLog.warn("UnprocessableEntityException: " + e.toString());
} catch (BaseServerResponseException e) {
ourLog.warn("Server responded HTTP " + e.getStatusCode() + ": " + e.toString());
} }
count++; count++;
} }
@ -293,7 +296,7 @@ public class ValidationDataUploader extends BaseCommand {
org.hl7.fhir.dstu3.model.Resource next = i.getResource(); org.hl7.fhir.dstu3.model.Resource next = i.getResource();
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading v3-codesystems ValueSet {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading v3-codesystems ValueSet {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
try { try {
client.update().resource(next).execute(); client.update().resource(next).execute();
} catch (Exception e) { } catch (Exception e) {
@ -318,7 +321,7 @@ public class ValidationDataUploader extends BaseCommand {
} }
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading v2-tables ValueSet {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading v2-tables ValueSet {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); client.update().resource(next).execute();
count++; count++;
} }
@ -364,7 +367,7 @@ public class ValidationDataUploader extends BaseCommand {
int bytes = theCtx.newXmlParser().encodeResourceToString(next).length(); int bytes = theCtx.newXmlParser().encodeResourceToString(next).length();
ourLog.info("Uploading ValueSet {}/{} : {} ({} bytes}", new Object[]{count, total, next.getIdElement().getValue(), bytes}); ourLog.info("Uploading ValueSet {}/{} : {} ({} bytes}", new Object[] {count, total, next.getIdElement().getValue(), bytes});
try { try {
IIdType id = client.update().resource(next).execute().getId(); IIdType id = client.update().resource(next).execute().getId();
ourLog.info(" - Got ID: {}", id.getValue()); ourLog.info(" - Got ID: {}", id.getValue());
@ -388,7 +391,7 @@ public class ValidationDataUploader extends BaseCommand {
org.hl7.fhir.r4.model.Resource next = i.getResource(); org.hl7.fhir.r4.model.Resource next = i.getResource();
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading v3-codesystems ValueSet {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading v3-codesystems ValueSet {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); client.update().resource(next).execute();
count++; count++;
@ -410,7 +413,7 @@ public class ValidationDataUploader extends BaseCommand {
} }
next.setId(next.getIdElement().toUnqualifiedVersionless()); next.setId(next.getIdElement().toUnqualifiedVersionless());
ourLog.info("Uploading v2-tables ValueSet {}/{} : {}", new Object[]{count, total, next.getIdElement().getValue()}); ourLog.info("Uploading v2-tables ValueSet {}/{} : {}", new Object[] {count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); client.update().resource(next).execute();
count++; count++;
} }
@ -470,7 +473,7 @@ public class ValidationDataUploader extends BaseCommand {
continue; continue;
} }
ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[]{name, count, total, next.getIdElement().getValue()}); ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[] {name, count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); client.update().resource(next).execute();
count++; count++;
@ -518,8 +521,12 @@ public class ValidationDataUploader extends BaseCommand {
continue; continue;
} }
ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[]{name, count, total, next.getIdElement().getValue()}); ourLog.info("Uploading {} StructureDefinition {}/{} : {}", new Object[] {name, count, total, next.getIdElement().getValue()});
client.update().resource(next).execute(); try {
client.update().resource(next).execute();
} catch (BaseServerResponseException e) {
ourLog.warn("Server responded HTTP " + e.getStatusCode() + ": " + e.toString());
}
count++; count++;
} }

View File

@ -27,6 +27,7 @@ import java.util.Map;
import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import okhttp3.Call; import okhttp3.Call;
import okhttp3.Call.Factory; import okhttp3.Call.Factory;
import okhttp3.Request; import okhttp3.Request;
@ -65,9 +66,10 @@ public class OkHttpRestfulRequest implements IHttpRequest {
@Override @Override
public IHttpResponse execute() throws IOException { public IHttpResponse execute() throws IOException {
myRequestBuilder.method(getHttpVerbName(), myRequestBody); StopWatch responseStopWatch = new StopWatch();
Call call = myClient.newCall(myRequestBuilder.build()); myRequestBuilder.method(getHttpVerbName(), myRequestBody);
return new OkHttpRestfulResponse(call.execute()); Call call = myClient.newCall(myRequestBuilder.build());
return new OkHttpRestfulResponse(call.execute(), responseStopWatch);
} }
@Override @Override

View File

@ -4,6 +4,8 @@ import java.io.*;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
@ -38,13 +40,14 @@ import okhttp3.Response;
* *
* @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health * @author Matthew Clarke | matthew.clarke@orionhealth.com | Orion Health
*/ */
public class OkHttpRestfulResponse implements IHttpResponse { public class OkHttpRestfulResponse extends BaseHttpResponse implements IHttpResponse {
private boolean myEntityBuffered = false; private boolean myEntityBuffered = false;
private byte[] myEntityBytes; private byte[] myEntityBytes;
private Response myResponse; private Response myResponse;
public OkHttpRestfulResponse(Response theResponse) { public OkHttpRestfulResponse(Response theResponse, StopWatch theResponseStopWatch) {
super(theResponseStopWatch);
this.myResponse = theResponse; this.myResponse = theResponse;
} }

View File

@ -27,10 +27,12 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.Header; import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.ContentType; import org.apache.http.entity.ContentType;
@ -61,7 +63,9 @@ public class ApacheHttpRequest implements IHttpRequest {
@Override @Override
public IHttpResponse execute() throws IOException { public IHttpResponse execute() throws IOException {
return new ApacheHttpResponse(myClient.execute(myRequest)); StopWatch responseStopWatch = new StopWatch();
HttpResponse httpResponse = myClient.execute(myRequest);
return new ApacheHttpResponse(httpResponse, responseStopWatch);
} }
@Override @Override

View File

@ -23,6 +23,8 @@ import java.io.*;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.*; import java.util.*;
import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.http.*; import org.apache.http.*;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
@ -38,7 +40,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
* *
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/ */
public class ApacheHttpResponse implements IHttpResponse { public class ApacheHttpResponse extends BaseHttpResponse implements IHttpResponse {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ApacheHttpResponse.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ApacheHttpResponse.class);
@ -46,7 +48,8 @@ public class ApacheHttpResponse implements IHttpResponse {
private byte[] myEntityBytes; private byte[] myEntityBytes;
private final HttpResponse myResponse; private final HttpResponse myResponse;
public ApacheHttpResponse(HttpResponse theResponse) { public ApacheHttpResponse(HttpResponse theResponse, StopWatch theResponseStopWatch) {
super(theResponseStopWatch);
this.myResponse = theResponse; this.myResponse = theResponse;
} }

View File

@ -0,0 +1,37 @@
package ca.uhn.fhir.rest.client.impl;
/*-
* #%L
* HAPI FHIR - Client Framework
* %%
* 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 ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.util.StopWatch;
public abstract class BaseHttpResponse implements IHttpResponse {
private final StopWatch myRequestStopWatch;
public BaseHttpResponse(StopWatch theRequestStopWatch) {
myRequestStopWatch = theRequestStopWatch;
}
@Override
public StopWatch getRequestStopWatch() {
return myRequestStopWatch;
}
}

View File

@ -115,7 +115,9 @@ public class LoggingInterceptor implements IClientInterceptor {
} }
respLocation = " (" + locationValue + ")"; respLocation = " (" + locationValue + ")";
} }
myLog.info("Client response: {}{}", message, respLocation);
String timing = " in " + theResponse.getRequestStopWatch().toString();
myLog.info("Client response: {}{}{}", message, respLocation, timing);
} }
if (myLogResponseHeaders) { if (myLogResponseHeaders) {

View File

@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
@ -11,6 +12,119 @@
<artifactId>hapi-fhir-converter</artifactId> <artifactId>hapi-fhir-converter</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>3.3.0-SNAPSHOT</version>
</dependency>
<!-- Server -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<!-- Testing -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xmlunit</groupId>
<artifactId>xmlunit-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-http</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<name>HAPI FHIR - Converter</name> <name>HAPI FHIR - Converter</name>
<build> <build>
@ -21,48 +135,4 @@
</resource> </resource>
</resources> </resources>
</build> </build>
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>3.3.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>3.3.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
</dependencies>
</project> </project>

View File

@ -0,0 +1,138 @@
package ca.uhn.hapi.converters.server;
/*-
* #%L
* HAPI FHIR - Converter
* %%
* 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 ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.ResponseDetails;
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import org.hl7.fhir.convertors.*;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.StringTokenizer;
import static org.apache.commons.lang3.StringUtils.*;
/**
* <b>This is an experimental interceptor! Use with caution as
* behaviour may change or be removed in a future version of
* FHIR.</b>
* <p>
* This interceptor partially implements the proposed
* Versioned API features.
* </p>
*/
public class VersionedApiConverterInterceptor extends InterceptorAdapter {
private final FhirContext myCtxDstu2;
private final FhirContext myCtxDstu2Hl7Org;
private VersionConvertor_30_40 myVersionConvertor_30_40;
private VersionConvertor_10_40 myVersionConvertor_10_40;
private VersionConvertor_10_30 myVersionConvertor_10_30;
public VersionedApiConverterInterceptor() {
myVersionConvertor_30_40 = new VersionConvertor_30_40();
VersionConvertorAdvisor40 advisor40 = new NullVersionConverterAdvisor40();
myVersionConvertor_10_40 = new VersionConvertor_10_40(advisor40);
VersionConvertorAdvisor30 advisor30 = new NullVersionConverterAdvisor30();
myVersionConvertor_10_30 = new VersionConvertor_10_30(advisor30);
myCtxDstu2 = FhirContext.forDstu2();
myCtxDstu2Hl7Org = FhirContext.forDstu2Hl7Org();
}
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
String[] formatParams = theRequestDetails.getParameters().get(Constants.PARAM_FORMAT);
String accept = null;
if (formatParams != null && formatParams.length > 0) {
accept = formatParams[0];
}
if (isBlank(accept)) {
accept = defaultString(theServletRequest.getHeader(Constants.HEADER_ACCEPT));
}
StringTokenizer tok = new StringTokenizer(accept, ";");
String wantVersionString = null;
while (tok.hasMoreTokens()) {
String next = tok.nextToken().trim();
if (next.startsWith("fhirVersion=")) {
wantVersionString = next.substring("fhirVersion=".length()).trim();
break;
}
}
FhirVersionEnum wantVersion = null;
if (isNotBlank(wantVersionString)) {
wantVersion = FhirVersionEnum.forVersionString(wantVersionString);
}
IBaseResource responseResource = theResponseDetails.getResponseResource();
FhirVersionEnum haveVersion = responseResource.getStructureFhirVersionEnum();
IBaseResource converted = null;
try {
if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU3) {
converted = myVersionConvertor_30_40.convertResource(toDstu3(responseResource));
} else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.R4) {
converted = myVersionConvertor_30_40.convertResource(toR4(responseResource));
} else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.R4) {
converted = myVersionConvertor_10_40.convertResource(toR4(responseResource));
} else if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU2) {
converted = myVersionConvertor_10_40.convertResource(toDstu2(responseResource));
} else if (wantVersion == FhirVersionEnum.DSTU2 && haveVersion == FhirVersionEnum.DSTU3) {
converted = myVersionConvertor_10_30.convertResource(toDstu3(responseResource));
} else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.DSTU2) {
converted = myVersionConvertor_10_30.convertResource(toDstu2(responseResource));
}
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
if (converted != null) {
theResponseDetails.setResponseResource(converted);
}
return true;
}
private org.hl7.fhir.instance.model.Resource toDstu2(IBaseResource theResponseResource) {
if (theResponseResource instanceof IResource) {
return (org.hl7.fhir.instance.model.Resource) myCtxDstu2Hl7Org.newJsonParser().parseResource(myCtxDstu2.newJsonParser().encodeResourceToString(theResponseResource));
}
return (org.hl7.fhir.instance.model.Resource) theResponseResource;
}
private Resource toDstu3(IBaseResource theResponseResource) {
return (Resource) theResponseResource;
}
private org.hl7.fhir.r4.model.Resource toR4(IBaseResource theResponseResource) {
return (org.hl7.fhir.r4.model.Resource) theResponseResource;
}
}

View File

@ -0,0 +1,56 @@
package org.hl7.fhir.convertors;
/*
* #%L
* HAPI FHIR - Converter
* %%
* 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 org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.Resource;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.ValueSet;
public class NullVersionConverterAdvisor40 implements VersionConvertorAdvisor40 {
@Override
public Resource convertR2(org.hl7.fhir.r4.model.Resource resource) throws FHIRException {
return null;
}
@Override
public org.hl7.fhir.dstu3.model.Resource convertR3(org.hl7.fhir.r4.model.Resource resource) throws FHIRException {
return null;
}
@Override
public CodeSystem getCodeSystem(ValueSet theSrc) {
return null;
}
@Override
public void handleCodeSystem(CodeSystem theTgtcs, ValueSet theSource) {
//nothing
}
@Override
public boolean ignoreEntry(BundleEntryComponent theSrc) {
return false;
}
}

View File

@ -0,0 +1,28 @@
package org.hl7.fhir.convertors;
/*-
* #%L
* HAPI FHIR - Converter
* %%
* 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%
*/
public class VersionConvertorConstants {
public final static String MODIFIER_REASON_EXTENSION = "http://hl7.org/fhir/tooling/StructureDefinition/r4ModifierReason";
public final static String MODIFIER_REASON_LEGACY = "No Modifier Reason provideed in previous versions of FHIR";
}

View File

@ -26,7 +26,7 @@ import java.util.List;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.hl7.fhir.instance.model.CodeableConcept; import org.hl7.fhir.instance.model.CodeableConcept;
import org.hl7.fhir.instance.model.Reference; import org.hl7.fhir.instance.model.Reference;
import org.hl7.fhir.dstu2.utils.ToolingExtensions; import org.hl7.fhir.instance.utils.ToolingExtensions;
import org.hl7.fhir.dstu3.conformance.ProfileUtilities; import org.hl7.fhir.dstu3.conformance.ProfileUtilities;
import org.hl7.fhir.dstu3.model.Annotation; import org.hl7.fhir.dstu3.model.Annotation;
import org.hl7.fhir.dstu3.model.CapabilityStatement.SystemRestfulInteraction; import org.hl7.fhir.dstu3.model.CapabilityStatement.SystemRestfulInteraction;
@ -10974,7 +10974,7 @@ public class VersionConvertor_10_30 {
tgt.setBase(t.asStringValue()); tgt.setBase(t.asStringValue());
tgt.setType(convertSearchParamType(src.getType())); tgt.setType(convertSearchParamType(src.getType()));
tgt.setDescription(src.getDescription()); tgt.setDescription(src.getDescription());
org.hl7.fhir.dstu2.utils.ToolingExtensions.setStringExtension(tgt, ToolingExtensions.EXT_EXPRESSION, src.getExpression()); org.hl7.fhir.instance.utils.ToolingExtensions.setStringExtension(tgt, ToolingExtensions.EXT_EXPRESSION, src.getExpression());
tgt.setXpath(src.getXpath()); tgt.setXpath(src.getXpath());
tgt.setXpathUsage(convertXPathUsageType(src.getXpathUsage())); tgt.setXpathUsage(convertXPathUsageType(src.getXpathUsage()));
for (org.hl7.fhir.dstu3.model.CodeType t : src.getTarget()) for (org.hl7.fhir.dstu3.model.CodeType t : src.getTarget())

View File

@ -20,6 +20,7 @@ package org.hl7.fhir.convertors;
* #L% * #L%
*/ */
/* /*
Copyright (c) 2011+, HL7, Inc. Copyright (c) 2011+, HL7, Inc.
All rights reserved. All rights reserved.
@ -54,19 +55,26 @@ package org.hl7.fhir.convertors;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hl7.fhir.dstu2016may.model.BooleanType;
import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionPropertyComponent; import org.hl7.fhir.dstu2016may.model.CodeSystem.ConceptDefinitionPropertyComponent;
import org.hl7.fhir.dstu2016may.model.Reference;
import org.hl7.fhir.dstu2016may.model.CodeableConcept; import org.hl7.fhir.dstu2016may.model.CodeableConcept;
import org.hl7.fhir.r4.conformance.ProfileUtilities; import org.hl7.fhir.r4.conformance.ProfileUtilities;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeSystem.FilterOperator; import org.hl7.fhir.r4.model.CodeSystem.FilterOperator;
import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent; import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent;
import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent; import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent;
import org.hl7.fhir.r4.model.ContactDetail; import org.hl7.fhir.r4.model.ContactDetail;
import org.hl7.fhir.r4.model.ElementDefinition;
import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent; import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionSlicingDiscriminatorComponent;
import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r4.model.Enumeration; import org.hl7.fhir.r4.model.Enumeration;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOperator;
import org.hl7.fhir.r4.model.Timing.EventTiming; import org.hl7.fhir.r4.model.Timing.EventTiming;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.r4.model.UsageContext; import org.hl7.fhir.r4.model.UsageContext;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent; import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent;
import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent; import org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
@ -1225,7 +1233,7 @@ public class VersionConvertor_14_40 {
if (src.hasContentReference()) if (src.hasContentReference())
tgt.setContentReference(src.getContentReference()); tgt.setContentReference(src.getContentReference());
for (org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent t : src.getType()) for (org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent t : src.getType())
tgt.addType(convertTypeRefComponent(t)); convertTypeRefComponent(t, tgt.getType());
tgt.setDefaultValue(convertType(src.getDefaultValue())); tgt.setDefaultValue(convertType(src.getDefaultValue()));
if (src.hasMeaningWhenMissing()) if (src.hasMeaningWhenMissing())
tgt.setMeaningWhenMissing(src.getMeaningWhenMissing()); tgt.setMeaningWhenMissing(src.getMeaningWhenMissing());
@ -1245,6 +1253,12 @@ public class VersionConvertor_14_40 {
tgt.setMustSupport(src.getMustSupport()); tgt.setMustSupport(src.getMustSupport());
if (src.hasIsModifier()) if (src.hasIsModifier())
tgt.setIsModifier(src.getIsModifier()); tgt.setIsModifier(src.getIsModifier());
if (tgt.getIsModifier()) {
String reason = org.hl7.fhir.dstu2016may.utils.ToolingExtensions.readStringExtension(src, VersionConvertorConstants.MODIFIER_REASON_EXTENSION);
if (Utilities.noString(reason))
reason = VersionConvertorConstants.MODIFIER_REASON_LEGACY;
tgt.setIsModifierReason(reason);
}
if (src.hasIsSummary()) if (src.hasIsSummary())
tgt.setIsSummary(src.getIsSummary()); tgt.setIsSummary(src.getIsSummary());
tgt.setBinding(convertElementDefinitionBindingComponent(src.getBinding())); tgt.setBinding(convertElementDefinitionBindingComponent(src.getBinding()));
@ -1288,7 +1302,7 @@ public class VersionConvertor_14_40 {
if (src.hasContentReference()) if (src.hasContentReference())
tgt.setContentReference(src.getContentReference()); tgt.setContentReference(src.getContentReference());
for (org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent t : src.getType()) for (org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent t : src.getType())
tgt.addType(convertTypeRefComponent(t)); convertTypeRefComponent(t, tgt.getType());
tgt.setDefaultValue(convertType(src.getDefaultValue())); tgt.setDefaultValue(convertType(src.getDefaultValue()));
if (src.hasMeaningWhenMissing()) if (src.hasMeaningWhenMissing())
tgt.setMeaningWhenMissing(src.getMeaningWhenMissing()); tgt.setMeaningWhenMissing(src.getMeaningWhenMissing());
@ -1308,6 +1322,8 @@ public class VersionConvertor_14_40 {
tgt.setMustSupport(src.getMustSupport()); tgt.setMustSupport(src.getMustSupport());
if (src.hasIsModifier()) if (src.hasIsModifier())
tgt.setIsModifier(src.getIsModifier()); tgt.setIsModifier(src.getIsModifier());
if (src.hasIsModifierReason() && !VersionConvertorConstants.MODIFIER_REASON_LEGACY.equals(src.getIsModifierReason()))
org.hl7.fhir.dstu2016may.utils.ToolingExtensions.setStringExtension(tgt, VersionConvertorConstants.MODIFIER_REASON_EXTENSION, src.getIsModifierReason());
if (src.hasIsSummary()) if (src.hasIsSummary())
tgt.setIsSummary(src.getIsSummary()); tgt.setIsSummary(src.getIsSummary());
if (src.hasBinding()) if (src.hasBinding())
@ -1438,38 +1454,51 @@ public class VersionConvertor_14_40 {
return tgt; return tgt;
} }
public static org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent convertTypeRefComponent(org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent src) throws FHIRException { static void convertTypeRefComponent(org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent src, List<org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent> list) throws FHIRException {
if (src == null || src.isEmpty()) if (src == null)
return null; return ;
org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent tgt = new org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent(); org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent tgt = null;
copyElement(src, tgt); for (org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent t : list)
tgt.setCode(src.getCode()); if (t.getCode().equals(src.getCode()))
for (org.hl7.fhir.dstu2016may.model.UriType t : src.getProfile()) { tgt = t;
if (src.getCode().equals("Reference")) if (tgt == null) {
tgt.setTargetProfile(t.getValueAsString()); tgt = new org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent();
else list.add(tgt);
tgt.setProfile(t.getValueAsString()); copyElement(src, tgt);
tgt.setCode(src.getCode());
} }
for (org.hl7.fhir.dstu2016may.model.Enumeration<org.hl7.fhir.dstu2016may.model.ElementDefinition.AggregationMode> t : src.getAggregation()) if (tgt.hasTarget()) {
tgt.addAggregation(convertAggregationMode(t.getValue())); for (org.hl7.fhir.dstu2016may.model.UriType u : src.getProfile())
tgt.setVersioning(convertReferenceVersionRules(src.getVersioning())); tgt.addTargetProfile(u.getValue());
return tgt; } else {
for (org.hl7.fhir.dstu2016may.model.UriType u : src.getProfile())
tgt.addProfile(u.getValue());
}
for (org.hl7.fhir.dstu2016may.model.Enumeration<org.hl7.fhir.dstu2016may.model.ElementDefinition.AggregationMode> t : src.getAggregation()) {
org.hl7.fhir.r4.model.ElementDefinition.AggregationMode a = convertAggregationMode(t.getValue());
if (!tgt.hasAggregation(a))
tgt.addAggregation(a);
}
if (src.hasVersioning())
tgt.setVersioning(convertReferenceVersionRules(src.getVersioning()));
} }
public static org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent convertTypeRefComponent(org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent src) throws FHIRException { public static void convertTypeRefComponent(org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent src, List<org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent> list) throws FHIRException {
if (src == null || src.isEmpty()) if (src == null)
return null; return;
org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent tgt = new org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent(); org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent tgt = new org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setCode(src.getCode()); tgt.setCode(src.getCode());
if (src.hasCode() && "Reference".equals(src.getCode())) list.add(tgt);
tgt.addProfile(src.getTargetProfile()); if (src.hasTarget()) {
else for (org.hl7.fhir.r4.model.UriType u : src.getTargetProfile()) {
tgt.addProfile(src.getProfile()); tgt.addProfile(u.getValue());
for (org.hl7.fhir.r4.model.Enumeration<org.hl7.fhir.r4.model.ElementDefinition.AggregationMode> t : src.getAggregation()) }
tgt.addAggregation(convertAggregationMode(t.getValue())); } else {
tgt.setVersioning(convertReferenceVersionRules(src.getVersioning())); for (org.hl7.fhir.r4.model.UriType u : src.getProfile()) {
return tgt; tgt.addProfile(u.getValue());
}
}
} }
private static org.hl7.fhir.r4.model.ElementDefinition.AggregationMode convertAggregationMode(org.hl7.fhir.dstu2016may.model.ElementDefinition.AggregationMode src) throws FHIRException { private static org.hl7.fhir.r4.model.ElementDefinition.AggregationMode convertAggregationMode(org.hl7.fhir.dstu2016may.model.ElementDefinition.AggregationMode src) throws FHIRException {
@ -1576,7 +1605,10 @@ public class VersionConvertor_14_40 {
tgt.setStrength(convertBindingStrength(src.getStrength())); tgt.setStrength(convertBindingStrength(src.getStrength()));
if (src.hasDescription()) if (src.hasDescription())
tgt.setDescription(src.getDescription()); tgt.setDescription(src.getDescription());
tgt.setValueSet(convertType(src.getValueSet())); if (src.hasValueSet()) {
org.hl7.fhir.r4.model.Type vs = convertType(src.getValueSet());
tgt.setValueSet(vs instanceof org.hl7.fhir.r4.model.Reference ? new CanonicalType(((org.hl7.fhir.r4.model.Reference) vs).getReference()) : vs);
}
return tgt; return tgt;
} }
@ -1588,7 +1620,7 @@ public class VersionConvertor_14_40 {
tgt.setStrength(convertBindingStrength(src.getStrength())); tgt.setStrength(convertBindingStrength(src.getStrength()));
if (src.hasDescription()) if (src.hasDescription())
tgt.setDescription(src.getDescription()); tgt.setDescription(src.getDescription());
tgt.setValueSet(convertType(src.getValueSet())); tgt.setValueSet(src.hasValueSetCanonicalType() ? new org.hl7.fhir.dstu2016may.model.Reference(src.getValueSetCanonicalType().getValue()) : convertType(src.getValueSet()));
return tgt; return tgt;
} }
@ -3251,7 +3283,7 @@ public class VersionConvertor_14_40 {
tgt.setSoftware(convertConformanceSoftwareComponent(src.getSoftware())); tgt.setSoftware(convertConformanceSoftwareComponent(src.getSoftware()));
tgt.setImplementation(convertConformanceImplementationComponent(src.getImplementation())); tgt.setImplementation(convertConformanceImplementationComponent(src.getImplementation()));
tgt.setFhirVersion(src.getFhirVersion()); tgt.setFhirVersion(src.getFhirVersion());
tgt.setAcceptUnknown(convertUnknownContentCode(src.getAcceptUnknown())); // tgt.setAcceptUnknown(convertUnknownContentCode(src.getAcceptUnknown()));
for (org.hl7.fhir.dstu2016may.model.CodeType t : src.getFormat()) for (org.hl7.fhir.dstu2016may.model.CodeType t : src.getFormat())
tgt.addFormat(t.getValue()); tgt.addFormat(t.getValue());
// for (org.hl7.fhir.dstu2016may.model.Reference t : src.getProfile()) // for (org.hl7.fhir.dstu2016may.model.Reference t : src.getProfile())
@ -3265,6 +3297,18 @@ public class VersionConvertor_14_40 {
return tgt; return tgt;
} }
private static CanonicalType convertReferenceToCanonical(Reference src) throws FHIRException {
CanonicalType dst = new CanonicalType(src.getReference());
copyElement(src, dst);
return dst;
}
private static Reference convertCanonicalToReference(CanonicalType src) throws FHIRException {
Reference dst = new Reference(src.getValue());
copyElement(src, dst);
return dst;
}
public static org.hl7.fhir.dstu2016may.model.Conformance convertConformance(org.hl7.fhir.r4.model.CapabilityStatement src) throws FHIRException { public static org.hl7.fhir.dstu2016may.model.Conformance convertConformance(org.hl7.fhir.r4.model.CapabilityStatement src) throws FHIRException {
if (src == null || src.isEmpty()) if (src == null || src.isEmpty())
return null; return null;
@ -3300,13 +3344,13 @@ public class VersionConvertor_14_40 {
tgt.setSoftware(convertConformanceSoftwareComponent(src.getSoftware())); tgt.setSoftware(convertConformanceSoftwareComponent(src.getSoftware()));
tgt.setImplementation(convertConformanceImplementationComponent(src.getImplementation())); tgt.setImplementation(convertConformanceImplementationComponent(src.getImplementation()));
tgt.setFhirVersion(src.getFhirVersion()); tgt.setFhirVersion(src.getFhirVersion());
tgt.setAcceptUnknown(convertUnknownContentCode(src.getAcceptUnknown())); // tgt.setAcceptUnknown(convertUnknownContentCode(src.getAcceptUnknown()));
for (org.hl7.fhir.r4.model.CodeType t : src.getFormat()) for (org.hl7.fhir.r4.model.CodeType t : src.getFormat())
tgt.addFormat(t.getValue()); tgt.addFormat(t.getValue());
for (CapabilityStatementRestComponent r : src.getRest()) for (CapabilityStatementRestComponent r : src.getRest())
for (CapabilityStatementRestResourceComponent rr : r.getResource()) for (CapabilityStatementRestResourceComponent rr : r.getResource())
for (org.hl7.fhir.r4.model.Reference t : rr.getSupportedProfile()) for (org.hl7.fhir.r4.model.CanonicalType t : rr.getSupportedProfile())
tgt.addProfile(convertReference(t)); tgt.addProfile(convertCanonicalToReference(t));
for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent t : src.getRest()) for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestComponent t : src.getRest())
tgt.addRest(convertConformanceRestComponent(t)); tgt.addRest(convertConformanceRestComponent(t));
for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementMessagingComponent t : src.getMessaging()) for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementMessagingComponent t : src.getMessaging())
@ -3337,30 +3381,30 @@ public class VersionConvertor_14_40 {
default: return org.hl7.fhir.dstu2016may.model.Conformance.ConformanceStatementKind.NULL; default: return org.hl7.fhir.dstu2016may.model.Conformance.ConformanceStatementKind.NULL;
} }
} }
//
private static org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode convertUnknownContentCode(org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode src) throws FHIRException { // private static org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode convertUnknownContentCode(org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode src) throws FHIRException {
if (src == null) // if (src == null)
return null; // return null;
switch (src) { // switch (src) {
case NO: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.NO; // case NO: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.NO;
case EXTENSIONS: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.EXTENSIONS; // case EXTENSIONS: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.EXTENSIONS;
case ELEMENTS: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.ELEMENTS; // case ELEMENTS: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.ELEMENTS;
case BOTH: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.BOTH; // case BOTH: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.BOTH;
default: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.NULL; // default: return org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode.NULL;
} // }
} // }
//
private static org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode convertUnknownContentCode(org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode src) throws FHIRException { // private static org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode convertUnknownContentCode(org.hl7.fhir.r4.model.CapabilityStatement.UnknownContentCode src) throws FHIRException {
if (src == null) // if (src == null)
return null; // return null;
switch (src) { // switch (src) {
case NO: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.NO; // case NO: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.NO;
case EXTENSIONS: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.EXTENSIONS; // case EXTENSIONS: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.EXTENSIONS;
case ELEMENTS: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.ELEMENTS; // case ELEMENTS: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.ELEMENTS;
case BOTH: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.BOTH; // case BOTH: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.BOTH;
default: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.NULL; // default: return org.hl7.fhir.dstu2016may.model.Conformance.UnknownContentCode.NULL;
} // }
} // }
public static org.hl7.fhir.r4.model.ContactDetail convertConformanceContactComponent(org.hl7.fhir.dstu2016may.model.Conformance.ConformanceContactComponent src) throws FHIRException { public static org.hl7.fhir.r4.model.ContactDetail convertConformanceContactComponent(org.hl7.fhir.dstu2016may.model.Conformance.ConformanceContactComponent src) throws FHIRException {
if (src == null || src.isEmpty()) if (src == null || src.isEmpty())
@ -3509,8 +3553,6 @@ public class VersionConvertor_14_40 {
tgt.addService(convertCodeableConcept(t)); tgt.addService(convertCodeableConcept(t));
if (src.hasDescription()) if (src.hasDescription())
tgt.setDescription(src.getDescription()); tgt.setDescription(src.getDescription());
for (org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestSecurityCertificateComponent t : src.getCertificate())
tgt.addCertificate(convertConformanceRestSecurityCertificateComponent(t));
return tgt; return tgt;
} }
@ -3525,32 +3567,6 @@ public class VersionConvertor_14_40 {
tgt.addService(convertCodeableConcept(t)); tgt.addService(convertCodeableConcept(t));
if (src.hasDescription()) if (src.hasDescription())
tgt.setDescription(src.getDescription()); tgt.setDescription(src.getDescription());
for (org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityCertificateComponent t : src.getCertificate())
tgt.addCertificate(convertConformanceRestSecurityCertificateComponent(t));
return tgt;
}
public static org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityCertificateComponent convertConformanceRestSecurityCertificateComponent(org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestSecurityCertificateComponent src) throws FHIRException {
if (src == null || src.isEmpty())
return null;
org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityCertificateComponent tgt = new org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityCertificateComponent();
copyElement(src, tgt);
if (src.hasType())
tgt.setType(src.getType());
if (src.hasBlob())
tgt.setBlob(src.getBlob());
return tgt;
}
public static org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestSecurityCertificateComponent convertConformanceRestSecurityCertificateComponent(org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestSecurityCertificateComponent src) throws FHIRException {
if (src == null || src.isEmpty())
return null;
org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestSecurityCertificateComponent tgt = new org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestSecurityCertificateComponent();
copyElement(src, tgt);
if (src.hasType())
tgt.setType(src.getType());
if (src.hasBlob())
tgt.setBlob(src.getBlob());
return tgt; return tgt;
} }
@ -3560,7 +3576,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent tgt = new org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent(); org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent tgt = new org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setType(src.getType()); tgt.setType(src.getType());
tgt.setProfile(convertReference(src.getProfile())); tgt.setProfileElement(convertReferenceToCanonical(src.getProfile()));
for (org.hl7.fhir.dstu2016may.model.Conformance.ResourceInteractionComponent t : src.getInteraction()) for (org.hl7.fhir.dstu2016may.model.Conformance.ResourceInteractionComponent t : src.getInteraction())
tgt.addInteraction(convertResourceInteractionComponent(t)); tgt.addInteraction(convertResourceInteractionComponent(t));
tgt.setVersioning(convertResourceVersionPolicy(src.getVersioning())); tgt.setVersioning(convertResourceVersionPolicy(src.getVersioning()));
@ -3588,7 +3604,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestResourceComponent tgt = new org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestResourceComponent(); org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestResourceComponent tgt = new org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestResourceComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setType(src.getType()); tgt.setType(src.getType());
tgt.setProfile(convertReference(src.getProfile())); tgt.setProfile(convertCanonicalToReference(src.getProfileElement()));
for (org.hl7.fhir.r4.model.CapabilityStatement.ResourceInteractionComponent t : src.getInteraction()) for (org.hl7.fhir.r4.model.CapabilityStatement.ResourceInteractionComponent t : src.getInteraction())
tgt.addInteraction(convertResourceInteractionComponent(t)); tgt.addInteraction(convertResourceInteractionComponent(t));
tgt.setVersioning(convertResourceVersionPolicy(src.getVersioning())); tgt.setVersioning(convertResourceVersionPolicy(src.getVersioning()));
@ -3818,7 +3834,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent tgt = new org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent(); org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent tgt = new org.hl7.fhir.r4.model.CapabilityStatement.CapabilityStatementRestResourceOperationComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setName(src.getName()); tgt.setName(src.getName());
tgt.setDefinition(convertReference(src.getDefinition())); tgt.setDefinitionElement(convertReferenceToCanonical(src.getDefinition()));
return tgt; return tgt;
} }
@ -3828,7 +3844,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestOperationComponent tgt = new org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestOperationComponent(); org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestOperationComponent tgt = new org.hl7.fhir.dstu2016may.model.Conformance.ConformanceRestOperationComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setName(src.getName()); tgt.setName(src.getName());
tgt.setDefinition(convertReference(src.getDefinition())); tgt.setDefinition(convertCanonicalToReference(src.getDefinitionElement()));
return tgt; return tgt;
} }
@ -3908,7 +3924,7 @@ public class VersionConvertor_14_40 {
tgt.setMode(convertDocumentMode(src.getMode())); tgt.setMode(convertDocumentMode(src.getMode()));
if (src.hasDocumentation()) if (src.hasDocumentation())
tgt.setDocumentation(src.getDocumentation()); tgt.setDocumentation(src.getDocumentation());
tgt.setProfile(convertReference(src.getProfile())); tgt.setProfileElement(convertReferenceToCanonical(src.getProfile()));
return tgt; return tgt;
} }
@ -3920,7 +3936,7 @@ public class VersionConvertor_14_40 {
tgt.setMode(convertDocumentMode(src.getMode())); tgt.setMode(convertDocumentMode(src.getMode()));
if (src.hasDocumentation()) if (src.hasDocumentation())
tgt.setDocumentation(src.getDocumentation()); tgt.setDocumentation(src.getDocumentation());
tgt.setProfile(convertReference(src.getProfile())); tgt.setProfile(convertCanonicalToReference(src.getProfileElement()));
return tgt; return tgt;
} }
@ -4292,7 +4308,7 @@ public class VersionConvertor_14_40 {
tgt.setAcronym(src.getAcronym()); tgt.setAcronym(src.getAcronym());
tgt.setSource(convertType(src.getSource())); tgt.setSource(convertType(src.getSource()));
if (src.hasExampleFor()) if (src.hasExampleFor())
tgt.setExampleFor(convertReference(src.getExampleFor())); tgt.setExampleForElement(convertReferenceToCanonical(src.getExampleFor()));
return tgt; return tgt;
} }
@ -4310,7 +4326,7 @@ public class VersionConvertor_14_40 {
tgt.setAcronym(src.getAcronym()); tgt.setAcronym(src.getAcronym());
tgt.setSource(convertType(src.getSource())); tgt.setSource(convertType(src.getSource()));
if (src.hasExampleFor()) if (src.hasExampleFor())
tgt.setExampleFor(convertReference(src.getExampleFor())); tgt.setExampleFor(convertCanonicalToReference(src.getExampleForElement()));
return tgt; return tgt;
} }
@ -4320,7 +4336,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.r4.model.ImplementationGuide.ImplementationGuideGlobalComponent tgt = new org.hl7.fhir.r4.model.ImplementationGuide.ImplementationGuideGlobalComponent(); org.hl7.fhir.r4.model.ImplementationGuide.ImplementationGuideGlobalComponent tgt = new org.hl7.fhir.r4.model.ImplementationGuide.ImplementationGuideGlobalComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setType(src.getType()); tgt.setType(src.getType());
tgt.setProfile(convertReference(src.getProfile())); tgt.setProfileElement(convertReferenceToCanonical(src.getProfile()));
return tgt; return tgt;
} }
@ -4330,7 +4346,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.dstu2016may.model.ImplementationGuide.ImplementationGuideGlobalComponent tgt = new org.hl7.fhir.dstu2016may.model.ImplementationGuide.ImplementationGuideGlobalComponent(); org.hl7.fhir.dstu2016may.model.ImplementationGuide.ImplementationGuideGlobalComponent tgt = new org.hl7.fhir.dstu2016may.model.ImplementationGuide.ImplementationGuideGlobalComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setType(src.getType()); tgt.setType(src.getType());
tgt.setProfile(convertReference(src.getProfile())); tgt.setProfile(convertCanonicalToReference(src.getProfileElement()));
return tgt; return tgt;
} }
@ -4594,7 +4610,7 @@ public class VersionConvertor_14_40 {
tgt.setCode(src.getCode()); tgt.setCode(src.getCode());
if (src.hasComment()) if (src.hasComment())
tgt.setComment(src.getComment()); tgt.setComment(src.getComment());
tgt.setBase(convertReference(src.getBase())); tgt.setBaseElement(convertReferenceToCanonical(src.getBase()));
tgt.setSystem(src.getSystem()); tgt.setSystem(src.getSystem());
for (org.hl7.fhir.dstu2016may.model.CodeType t : src.getType()) for (org.hl7.fhir.dstu2016may.model.CodeType t : src.getType())
tgt.addResource(t.getValue()); tgt.addResource(t.getValue());
@ -4639,7 +4655,7 @@ public class VersionConvertor_14_40 {
tgt.setCode(src.getCode()); tgt.setCode(src.getCode());
if (src.hasComment()) if (src.hasComment())
tgt.setComment(src.getComment()); tgt.setComment(src.getComment());
tgt.setBase(convertReference(src.getBase())); tgt.setBase(convertCanonicalToReference(src.getBaseElement()));
tgt.setSystem(src.getSystem()); tgt.setSystem(src.getSystem());
if (src.getType()) if (src.getType())
for (org.hl7.fhir.r4.model.CodeType t : src.getResource()) for (org.hl7.fhir.r4.model.CodeType t : src.getResource())
@ -5054,10 +5070,10 @@ public class VersionConvertor_14_40 {
tgt.setReadOnly(src.getReadOnly()); tgt.setReadOnly(src.getReadOnly());
if (src.hasMaxLength()) if (src.hasMaxLength())
tgt.setMaxLength(src.getMaxLength()); tgt.setMaxLength(src.getMaxLength());
tgt.setOptions(convertReference(src.getOptions())); tgt.setOptionsElement(convertReferenceToCanonical(src.getOptions()));
for (org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemOptionComponent t : src.getOption()) for (org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemOptionComponent t : src.getOption())
tgt.addOption(convertQuestionnaireItemOptionComponent(t)); tgt.addOption(convertQuestionnaireItemOptionComponent(t));
tgt.setInitial(convertType(src.getInitial())); tgt.addInitial().setValue(convertType(src.getInitial()));
for (org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemComponent t : src.getItem()) for (org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemComponent t : src.getItem())
tgt.addItem(convertQuestionnaireItemComponent(t)); tgt.addItem(convertQuestionnaireItemComponent(t));
return tgt; return tgt;
@ -5087,10 +5103,11 @@ public class VersionConvertor_14_40 {
tgt.setReadOnly(src.getReadOnly()); tgt.setReadOnly(src.getReadOnly());
if (src.hasMaxLength()) if (src.hasMaxLength())
tgt.setMaxLength(src.getMaxLength()); tgt.setMaxLength(src.getMaxLength());
tgt.setOptions(convertReference(src.getOptions())); tgt.setOptions(convertCanonicalToReference(src.getOptionsElement()));
for (org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOptionComponent t : src.getOption()) for (org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemOptionComponent t : src.getOption())
tgt.addOption(convertQuestionnaireItemOptionComponent(t)); tgt.addOption(convertQuestionnaireItemOptionComponent(t));
tgt.setInitial(convertType(src.getInitial())); if (src.hasInitial())
tgt.setInitial(convertType(src.getInitialFirstRep().getValue()));
for (org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent t : src.getItem()) for (org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemComponent t : src.getItem())
tgt.addItem(convertQuestionnaireItemComponent(t)); tgt.addItem(convertQuestionnaireItemComponent(t));
return tgt; return tgt;
@ -5153,8 +5170,10 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent tgt = new org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent(); org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent tgt = new org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemEnableWhenComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setQuestion(src.getQuestion()); tgt.setQuestion(src.getQuestion());
if (src.hasAnswered()) if (src.hasAnswered()) {
tgt.setHasAnswer(src.getAnswered()); tgt.setOperator(QuestionnaireItemOperator.EXISTS);
tgt.setAnswer(convertType(src.getAnsweredElement()));
}
tgt.setAnswer(convertType(src.getAnswer())); tgt.setAnswer(convertType(src.getAnswer()));
return tgt; return tgt;
} }
@ -5165,9 +5184,10 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemEnableWhenComponent tgt = new org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemEnableWhenComponent(); org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemEnableWhenComponent tgt = new org.hl7.fhir.dstu2016may.model.Questionnaire.QuestionnaireItemEnableWhenComponent();
copyElement(src, tgt); copyElement(src, tgt);
tgt.setQuestion(src.getQuestion()); tgt.setQuestion(src.getQuestion());
if (src.hasHasAnswer()) if (src.hasOperator() && src.getOperator() == QuestionnaireItemOperator.EXISTS)
tgt.setAnswered(src.getHasAnswer()); tgt.setAnswered(src.getAnswerBooleanType().getValue());
tgt.setAnswer(convertType(src.getAnswer())); else
tgt.setAnswer(convertType(src.getAnswer()));
return tgt; return tgt;
} }
@ -5195,7 +5215,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.r4.model.QuestionnaireResponse tgt = new org.hl7.fhir.r4.model.QuestionnaireResponse(); org.hl7.fhir.r4.model.QuestionnaireResponse tgt = new org.hl7.fhir.r4.model.QuestionnaireResponse();
copyDomainResource(src, tgt); copyDomainResource(src, tgt);
tgt.setIdentifier(convertIdentifier(src.getIdentifier())); tgt.setIdentifier(convertIdentifier(src.getIdentifier()));
tgt.setQuestionnaire(convertReference(src.getQuestionnaire())); tgt.setQuestionnaireElement(convertReferenceToCanonical(src.getQuestionnaire()));
tgt.setStatus(convertQuestionnaireResponseStatus(src.getStatus())); tgt.setStatus(convertQuestionnaireResponseStatus(src.getStatus()));
tgt.setSubject(convertReference(src.getSubject())); tgt.setSubject(convertReference(src.getSubject()));
tgt.setContext(convertReference(src.getEncounter())); tgt.setContext(convertReference(src.getEncounter()));
@ -5214,7 +5234,7 @@ public class VersionConvertor_14_40 {
org.hl7.fhir.dstu2016may.model.QuestionnaireResponse tgt = new org.hl7.fhir.dstu2016may.model.QuestionnaireResponse(); org.hl7.fhir.dstu2016may.model.QuestionnaireResponse tgt = new org.hl7.fhir.dstu2016may.model.QuestionnaireResponse();
copyDomainResource(src, tgt); copyDomainResource(src, tgt);
tgt.setIdentifier(convertIdentifier(src.getIdentifier())); tgt.setIdentifier(convertIdentifier(src.getIdentifier()));
tgt.setQuestionnaire(convertReference(src.getQuestionnaire())); tgt.setQuestionnaire(convertCanonicalToReference(src.getQuestionnaireElement()));
tgt.setStatus(convertQuestionnaireResponseStatus(src.getStatus())); tgt.setStatus(convertQuestionnaireResponseStatus(src.getStatus()));
tgt.setSubject(convertReference(src.getSubject())); tgt.setSubject(convertReference(src.getSubject()));
tgt.setEncounter(convertReference(src.getContext())); tgt.setEncounter(convertReference(src.getContext()));
@ -5472,9 +5492,11 @@ public class VersionConvertor_14_40 {
tgt.addMapping(convertStructureDefinitionMappingComponent(t)); tgt.addMapping(convertStructureDefinitionMappingComponent(t));
tgt.setKind(convertStructureDefinitionKind(src.getKind())); tgt.setKind(convertStructureDefinitionKind(src.getKind()));
tgt.setAbstract(src.getAbstract()); tgt.setAbstract(src.getAbstract());
tgt.setContextType(convertExtensionContext(src.getContextType())); for (org.hl7.fhir.dstu2016may.model.StringType t : src.getContext()) {
for (org.hl7.fhir.dstu2016may.model.StringType t : src.getContext()) org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionContextComponent ec = tgt.addContext();
tgt.addContext(t.getValue()); ec.setType(convertExtensionContext(src.getContextType()));
ec.setExpression(t.getValue());
}
if (src.getDerivation() == org.hl7.fhir.dstu2016may.model.StructureDefinition.TypeDerivationRule.CONSTRAINT) if (src.getDerivation() == org.hl7.fhir.dstu2016may.model.StructureDefinition.TypeDerivationRule.CONSTRAINT)
tgt.setType(src.getBaseType()); tgt.setType(src.getBaseType());
else else
@ -5490,6 +5512,13 @@ public class VersionConvertor_14_40 {
tgt.setDifferential(convertStructureDefinitionDifferentialComponent(src.getDifferential())); tgt.setDifferential(convertStructureDefinitionDifferentialComponent(src.getDifferential()));
tgt.getDifferential().getElementFirstRep().getType().clear(); tgt.getDifferential().getElementFirstRep().getType().clear();
} }
if (tgt.getDerivation() == TypeDerivationRule.SPECIALIZATION) {
for (ElementDefinition ed : tgt.getSnapshot().getElement()) {
if (!ed.hasBase()) {
ed.getBase().setPath(ed.getPath()).setMin(ed.getMin()).setMax(ed.getMax());
}
}
}
return tgt; return tgt;
} }
@ -5535,9 +5564,11 @@ public class VersionConvertor_14_40 {
tgt.addMapping(convertStructureDefinitionMappingComponent(t)); tgt.addMapping(convertStructureDefinitionMappingComponent(t));
tgt.setKind(convertStructureDefinitionKind(src.getKind())); tgt.setKind(convertStructureDefinitionKind(src.getKind()));
tgt.setAbstract(src.getAbstract()); tgt.setAbstract(src.getAbstract());
tgt.setContextType(convertExtensionContext(src.getContextType())); for (org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionContextComponent t : src.getContext()) {
for (org.hl7.fhir.r4.model.StringType t : src.getContext()) if (!tgt.hasContextType())
tgt.addContext(t.getValue()); tgt.setContextType(convertExtensionContext(t.getType()));
tgt.addContext(t.getExpression());
}
if (src.hasBaseDefinition()) if (src.hasBaseDefinition())
tgt.setBaseDefinition(src.getBaseDefinition()); tgt.setBaseDefinition(src.getBaseDefinition());
if (src.hasType() && src.getDerivation() == org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule.CONSTRAINT) if (src.hasType() && src.getDerivation() == org.hl7.fhir.r4.model.StructureDefinition.TypeDerivationRule.CONSTRAINT)
@ -5572,23 +5603,23 @@ public class VersionConvertor_14_40 {
} }
} }
private static org.hl7.fhir.r4.model.StructureDefinition.ExtensionContext convertExtensionContext(org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext src) throws FHIRException { private static org.hl7.fhir.r4.model.StructureDefinition.ExtensionContextType convertExtensionContext(org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext src) throws FHIRException {
if (src == null) if (src == null)
return null; return null;
switch (src) { switch (src) {
case RESOURCE: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContext.RESOURCE; case RESOURCE: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContextType.FHIRPATH;
case DATATYPE: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContext.DATATYPE; case DATATYPE: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContextType.ELEMENT;
case EXTENSION: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContext.EXTENSION; case EXTENSION: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContextType.EXTENSION;
default: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContext.NULL; default: return org.hl7.fhir.r4.model.StructureDefinition.ExtensionContextType.NULL;
} }
} }
private static org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext convertExtensionContext(org.hl7.fhir.r4.model.StructureDefinition.ExtensionContext src) throws FHIRException { private static org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext convertExtensionContext(org.hl7.fhir.r4.model.StructureDefinition.ExtensionContextType src) throws FHIRException {
if (src == null) if (src == null)
return null; return null;
switch (src) { switch (src) {
case RESOURCE: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.RESOURCE; case FHIRPATH: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.RESOURCE;
case DATATYPE: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.DATATYPE; case ELEMENT: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.DATATYPE;
case EXTENSION: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.EXTENSION; case EXTENSION: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.EXTENSION;
default: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.NULL; default: return org.hl7.fhir.dstu2016may.model.StructureDefinition.ExtensionContext.NULL;
} }
@ -6186,6 +6217,8 @@ public class VersionConvertor_14_40 {
tgt.setLabel(src.getLabel()); tgt.setLabel(src.getLabel());
if (src.hasDescription()) if (src.hasDescription())
tgt.setDescription(src.getDescription()); tgt.setDescription(src.getDescription());
tgt.setAccept(convertContentType(src.getAccept()));
tgt.setContentType(convertContentType(src.getContentType()));
if (src.hasDestination()) if (src.hasDestination())
tgt.setDestination(src.getDestination()); tgt.setDestination(src.getDestination());
if (src.hasEncodeRequestUrl()) if (src.hasEncodeRequestUrl())
@ -6218,6 +6251,8 @@ public class VersionConvertor_14_40 {
tgt.setLabel(src.getLabel()); tgt.setLabel(src.getLabel());
if (src.hasDescription()) if (src.hasDescription())
tgt.setDescription(src.getDescription()); tgt.setDescription(src.getDescription());
tgt.setAccept(convertContentType(src.getAccept()));
tgt.setContentType(convertContentType(src.getContentType()));
if (src.hasDestination()) if (src.hasDestination())
tgt.setDestination(src.getDestination()); tgt.setDestination(src.getDestination());
if (src.hasEncodeRequestUrl()) if (src.hasEncodeRequestUrl())
@ -6239,6 +6274,24 @@ public class VersionConvertor_14_40 {
return tgt; return tgt;
} }
private static String convertContentType(org.hl7.fhir.dstu2016may.model.TestScript.ContentType src) throws FHIRException {
if (src == null)
return null;
switch (src) {
case XML: return "application/fhir+xml";
case JSON: return "application/fhir+json";
default: return null;
}
}
private static org.hl7.fhir.dstu2016may.model.TestScript.ContentType convertContentType(String src) throws FHIRException {
if (src == null)
return null;
if (src.contains("xml")) return org.hl7.fhir.dstu2016may.model.TestScript.ContentType.XML;
if (src.contains("json")) return org.hl7.fhir.dstu2016may.model.TestScript.ContentType.JSON;
return org.hl7.fhir.dstu2016may.model.TestScript.ContentType.NULL;
}
public static org.hl7.fhir.r4.model.TestScript.SetupActionOperationRequestHeaderComponent convertSetupActionOperationRequestHeaderComponent(org.hl7.fhir.dstu2016may.model.TestScript.SetupActionOperationRequestHeaderComponent src) throws FHIRException { public static org.hl7.fhir.r4.model.TestScript.SetupActionOperationRequestHeaderComponent convertSetupActionOperationRequestHeaderComponent(org.hl7.fhir.dstu2016may.model.TestScript.SetupActionOperationRequestHeaderComponent src) throws FHIRException {
if (src == null || src.isEmpty()) if (src == null || src.isEmpty())
@ -6274,6 +6327,7 @@ public class VersionConvertor_14_40 {
tgt.setCompareToSourceId(src.getCompareToSourceId()); tgt.setCompareToSourceId(src.getCompareToSourceId());
if (src.hasCompareToSourcePath()) if (src.hasCompareToSourcePath())
tgt.setCompareToSourcePath(src.getCompareToSourcePath()); tgt.setCompareToSourcePath(src.getCompareToSourcePath());
tgt.setContentType(convertContentType(src.getContentType()));
if (src.hasHeaderField()) if (src.hasHeaderField())
tgt.setHeaderField(src.getHeaderField()); tgt.setHeaderField(src.getHeaderField());
if (src.hasMinimumId()) if (src.hasMinimumId())
@ -6315,6 +6369,7 @@ public class VersionConvertor_14_40 {
tgt.setCompareToSourceId(src.getCompareToSourceId()); tgt.setCompareToSourceId(src.getCompareToSourceId());
if (src.hasCompareToSourcePath()) if (src.hasCompareToSourcePath())
tgt.setCompareToSourcePath(src.getCompareToSourcePath()); tgt.setCompareToSourcePath(src.getCompareToSourcePath());
tgt.setContentType(convertContentType(src.getContentType()));
if (src.hasHeaderField()) if (src.hasHeaderField())
tgt.setHeaderField(src.getHeaderField()); tgt.setHeaderField(src.getHeaderField());
if (src.hasMinimumId()) if (src.hasMinimumId())

View File

@ -1,487 +0,0 @@
package org.hl7.fhir.dstu2.utils;
/*
* #%L
* HAPI FHIR - Converter
* %%
* 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.net.URISyntaxException;
/*
Copyright (c) 2011+, HL7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hl7.fhir.instance.model.BooleanType;
import org.hl7.fhir.instance.model.CodeType;
import org.hl7.fhir.instance.model.CodeableConcept;
import org.hl7.fhir.instance.model.Coding;
import org.hl7.fhir.instance.model.DataElement;
import org.hl7.fhir.instance.model.DomainResource;
import org.hl7.fhir.instance.model.Element;
import org.hl7.fhir.instance.model.ElementDefinition;
import org.hl7.fhir.instance.model.Extension;
import org.hl7.fhir.instance.model.ExtensionHelper;
import org.hl7.fhir.instance.model.Factory;
import org.hl7.fhir.instance.model.Identifier;
import org.hl7.fhir.instance.model.IntegerType;
import org.hl7.fhir.instance.model.MarkdownType;
import org.hl7.fhir.instance.model.PrimitiveType;
import org.hl7.fhir.instance.model.Questionnaire.GroupComponent;
import org.hl7.fhir.instance.model.Questionnaire.QuestionComponent;
import org.hl7.fhir.instance.model.Reference;
import org.hl7.fhir.instance.model.StringType;
import org.hl7.fhir.instance.model.Type;
import org.hl7.fhir.instance.model.UriType;
import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptDefinitionComponent;
import org.hl7.fhir.instance.model.ValueSet.ValueSetCodeSystemComponent;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.Utilities;
public class ToolingExtensions {
// validated
public static final String EXT_SUBSUMES = "http://hl7.org/fhir/StructureDefinition/valueset-subsumes";
private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid";
public static final String EXT_DEPRECATED = "http://hl7.org/fhir/StructureDefinition/valueset-deprecated";
public static final String EXT_DEFINITION = "http://hl7.org/fhir/StructureDefinition/valueset-definition";
public static final String EXT_COMMENT = "http://hl7.org/fhir/StructureDefinition/valueset-comments";
private static final String EXT_IDENTIFIER = "http://hl7.org/fhir/StructureDefinition/identifier";
private static final String EXT_TRANSLATION = "http://hl7.org/fhir/StructureDefinition/translation";
public static final String EXT_ISSUE_SOURCE = "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-source";
public static final String EXT_DISPLAY_HINT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-display-hint";
public static final String EXT_REPLACED_BY = "http://hl7.org/fhir/StructureDefinition/valueset-replacedby";
public static final String EXT_JSON_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-json-type";
public static final String EXT_XML_TYPE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-xml-type";
public static final String EXT_REGEX = "http://hl7.org/fhir/StructureDefinition/structuredefinition-regex";
public static final String EXT_EXPRESSION = "http://hl7.org/fhir/StructureDefinition/structuredefinition-expression";
public static final String EXT_SEARCH_EXPRESSION = "http://hl7.org/fhir/StructureDefinition/searchparameter-expression";
// unregistered?
public static final String EXT_FLYOVER = "http://hl7.org/fhir/Profile/questionnaire-extensions#flyover";
private static final String EXT_QTYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
private static final String EXT_QREF = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
private static final String EXTENSION_FILTER_ONLY = "http://www.healthintersections.com.au/fhir/Profile/metadata#expandNeedsFilter";
private static final String EXT_TYPE = "http://www.healthintersections.com.au/fhir/Profile/metadata#type";
private static final String EXT_REFERENCE = "http://www.healthintersections.com.au/fhir/Profile/metadata#reference";
private static final String EXT_ALLOWABLE_UNITS = "http://hl7.org/fhir/StructureDefinition/elementdefinition-allowedUnits";
public static final String EXT_CIMI_REFERENCE = "http://hl7.org/fhir/StructureDefinition/cimi-reference";
public static final String EXT_UNCLOSED = "http://hl7.org/fhir/StructureDefinition/valueset-unclosed";
public static final String EXT_FMM_LEVEL = "http://hl7.org/fhir/StructureDefinition/structuredefinition-fmm";
// specific extension helpers
public static Extension makeIssueSource(Source source) {
Extension ex = new Extension();
// todo: write this up and get it published with the pack (and handle the redirect?)
ex.setUrl(ToolingExtensions.EXT_ISSUE_SOURCE);
CodeType c = new CodeType();
c.setValue(source.toString());
ex.setValue(c);
return ex;
}
public static boolean hasExtension(DomainResource de, String url) {
return getExtension(de, url) != null;
}
public static boolean hasExtension(Element e, String url) {
return getExtension(e, url) != null;
}
public static void addStringExtension(DomainResource dr, String url, String content) {
if (!Utilities.noString(content)) {
Extension ex = getExtension(dr, url);
if (ex != null)
ex.setValue(new StringType(content));
else
dr.getExtension().add(Factory.newExtension(url, new StringType(content), true));
}
}
public static void addStringExtension(Element e, String url, String content) {
if (!Utilities.noString(content)) {
Extension ex = getExtension(e, url);
if (ex != null)
ex.setValue(new StringType(content));
else
e.getExtension().add(Factory.newExtension(url, new StringType(content), true));
}
}
public static void addIntegerExtension(DomainResource dr, String url, int value) {
Extension ex = getExtension(dr, url);
if (ex != null)
ex.setValue(new IntegerType(value));
else
dr.getExtension().add(Factory.newExtension(url, new IntegerType(value), true));
}
public static void addComment(Element nc, String comment) {
if (!Utilities.noString(comment))
nc.getExtension().add(Factory.newExtension(EXT_COMMENT, Factory.newString_(comment), true));
}
public static void markDeprecated(Element nc) {
setDeprecated(nc);
}
public static void addSubsumes(ConceptDefinitionComponent nc, String code) {
nc.getModifierExtension().add(Factory.newExtension(EXT_SUBSUMES, Factory.newCode(code), true));
}
public static void addDefinition(Element nc, String definition) {
if (!Utilities.noString(definition))
nc.getExtension().add(Factory.newExtension(EXT_DEFINITION, Factory.newString_(definition), true));
}
public static void addDisplayHint(Element def, String hint) {
if (!Utilities.noString(hint))
def.getExtension().add(Factory.newExtension(EXT_DISPLAY_HINT, Factory.newString_(hint), true));
}
public static String getDisplayHint(Element def) {
return readStringExtension(def, EXT_DISPLAY_HINT);
}
public static String readStringExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return null;
if (ex.getValue() instanceof UriType)
return ((UriType) ex.getValue()).getValue();
if (!(ex.getValue() instanceof StringType))
return null;
return ((StringType) ex.getValue()).getValue();
}
public static String readStringExtension(DomainResource c, String uri) {
Extension ex = getExtension(c, uri);
if (ex == null)
return null;
if ((ex.getValue() instanceof StringType))
return ((StringType) ex.getValue()).getValue();
if ((ex.getValue() instanceof UriType))
return ((UriType) ex.getValue()).getValue();
if ((ex.getValue() instanceof MarkdownType))
return ((MarkdownType) ex.getValue()).getValue();
return null;
}
@SuppressWarnings("unchecked")
public static PrimitiveType<Type> readPrimitiveExtension(DomainResource c, String uri) {
Extension ex = getExtension(c, uri);
if (ex == null)
return null;
return (PrimitiveType<Type>) ex.getValue();
}
public static boolean findStringExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return false;
if (!(ex.getValue() instanceof StringType))
return false;
return !Utilities.noString(((StringType) ex.getValue()).getValue());
}
public static Boolean readBooleanExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return null;
if (!(ex.getValue() instanceof BooleanType))
return null;
return ((BooleanType) ex.getValue()).getValue();
}
public static boolean findBooleanExtension(Element c, String uri) {
Extension ex = ExtensionHelper.getExtension(c, uri);
if (ex == null)
return false;
if (!(ex.getValue() instanceof BooleanType))
return false;
return true;
}
public static String getComment(ConceptDefinitionComponent c) {
return readStringExtension(c, EXT_COMMENT);
}
public static Boolean getDeprecated(Element c) {
return readBooleanExtension(c, EXT_DEPRECATED);
}
public static boolean hasComment(ConceptDefinitionComponent c) {
return findStringExtension(c, EXT_COMMENT);
}
public static boolean hasDeprecated(Element c) {
return findBooleanExtension(c, EXT_DEPRECATED);
}
public static List<CodeType> getSubsumes(ConceptDefinitionComponent c) {
List<CodeType> res = new ArrayList<CodeType>();
for (Extension e : c.getExtension()) {
if (EXT_SUBSUMES.equals(e.getUrl()))
res.add((CodeType) e.getValue());
}
return res;
}
public static void addFlyOver(GroupComponent group, String text) {
if (!Utilities.noString(text))
group.getExtension().add(Factory.newExtension(EXT_FLYOVER, Factory.newString_(text), true));
}
public static void setQuestionType(GroupComponent group, String text) {
if (!Utilities.noString(text))
group.getExtension().add(Factory.newExtension(EXT_QTYPE, Factory.newString_(text), true));
}
public static void setQuestionReference(GroupComponent group, String text) {
if (!Utilities.noString(text))
group.getExtension().add(Factory.newExtension(EXT_QREF, Factory.newString_(text), true));
}
public static void addFlyOver(Element element, String text) {
element.getExtension().add(Factory.newExtension(EXT_FLYOVER, Factory.newString_(text), true));
}
public static void addFilterOnly(Reference element, boolean value) {
element.getExtension().add(Factory.newExtension(EXTENSION_FILTER_ONLY, Factory.newBoolean(value), true));
}
public static void addType(GroupComponent group, String value) {
group.getExtension().add(Factory.newExtension(EXT_TYPE, Factory.newString_(value), true));
}
public static void addReference(QuestionComponent group, String value) {
group.getExtension().add(Factory.newExtension(EXT_REFERENCE, Factory.newString_(value), true));
}
public static void addIdentifier(Element element, Identifier value) {
element.getExtension().add(Factory.newExtension(EXT_IDENTIFIER, value, true));
}
/**
* @param name the identity of the extension of interest
* @return The extension, if on this element, else null
*/
public static Extension getExtension(DomainResource resource, String name) {
if (name == null)
return null;
if (!resource.hasExtension())
return null;
for (Extension e : resource.getExtension()) {
if (name.equals(e.getUrl()))
return e;
}
return null;
}
public static Extension getExtension(Element el, String name) {
if (name == null)
return null;
if (!el.hasExtension())
return null;
for (Extension e : el.getExtension()) {
if (name.equals(e.getUrl()))
return e;
}
return null;
}
public static void setStringExtension(DomainResource resource, String uri, String value) {
Extension ext = getExtension(resource, uri);
if (ext != null)
ext.setValue(new StringType(value));
else
resource.getExtension().add(new Extension(new UriType(uri)).setValue(new StringType(value)));
}
public static String getOID(ValueSetCodeSystemComponent define) {
return readStringExtension(define, EXT_OID);
}
public static String getOID(ValueSet vs) {
return readStringExtension(vs, EXT_OID);
}
public static void setOID(ValueSetCodeSystemComponent define, String oid) throws FHIRFormatError, URISyntaxException {
if (!oid.startsWith("urn:oid:"))
throw new FHIRFormatError("Error in OID format");
if (oid.startsWith("urn:oid:urn:oid:"))
throw new FHIRFormatError("Error in OID format");
define.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));
}
public static void setOID(ValueSet vs, String oid) throws FHIRFormatError, URISyntaxException {
if (!oid.startsWith("urn:oid:"))
throw new FHIRFormatError("Error in OID format");
if (oid.startsWith("urn:oid:urn:oid:"))
throw new FHIRFormatError("Error in OID format");
vs.getExtension().add(Factory.newExtension(EXT_OID, Factory.newUri(oid), false));
}
public static boolean hasLanguageTranslation(Element element, String lang) {
for (Extension e : element.getExtension()) {
if (e.getUrl().equals(EXT_TRANSLATION)) {
Extension e1 = ExtensionHelper.getExtension(e, "lang");
if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang))
return true;
}
}
return false;
}
public static String getLanguageTranslation(Element element, String lang) {
for (Extension e : element.getExtension()) {
if (e.getUrl().equals(EXT_TRANSLATION)) {
Extension e1 = ExtensionHelper.getExtension(e, "lang");
if (e1 != null && e1.getValue() instanceof CodeType && ((CodeType) e.getValue()).getValue().equals(lang)) {
e1 = ExtensionHelper.getExtension(e, "content");
return ((StringType) e.getValue()).getValue();
}
}
}
return null;
}
public static void addLanguageTranslation(Element element, String lang, String value) {
Extension extension = new Extension().setUrl(EXT_TRANSLATION);
extension.addExtension().setUrl("lang").setValue(new StringType(lang));
extension.addExtension().setUrl("content").setValue(new StringType(value));
element.getExtension().add(extension);
}
public static Type getAllowedUnits(ElementDefinition eld) {
for (Extension e : eld.getExtension())
if (e.getUrl().equals(EXT_ALLOWABLE_UNITS))
return e.getValue();
return null;
}
public static void setAllowableUnits(ElementDefinition eld, CodeableConcept cc) {
for (Extension e : eld.getExtension())
if (e.getUrl().equals(EXT_ALLOWABLE_UNITS)) {
e.setValue(cc);
return;
}
eld.getExtension().add(new Extension().setUrl(EXT_ALLOWABLE_UNITS).setValue(cc));
}
public static List<Extension> getExtensions(Element element, String url) {
List<Extension> results = new ArrayList<Extension>();
for (Extension ex : element.getExtension())
if (ex.getUrl().equals(url))
results.add(ex);
return results;
}
public static List<Extension> getExtensions(DomainResource resource, String url) {
List<Extension> results = new ArrayList<Extension>();
for (Extension ex : resource.getExtension())
if (ex.getUrl().equals(url))
results.add(ex);
return results;
}
public static void addDEReference(DataElement de, String value) {
for (Extension e : de.getExtension())
if (e.getUrl().equals(EXT_CIMI_REFERENCE)) {
e.setValue(new UriType(value));
return;
}
de.getExtension().add(new Extension().setUrl(EXT_CIMI_REFERENCE).setValue(new UriType(value)));
}
public static void setDeprecated(Element nc) {
for (Extension e : nc.getExtension())
if (e.getUrl().equals(EXT_DEPRECATED)) {
e.setValue(new BooleanType(true));
return;
}
nc.getExtension().add(new Extension().setUrl(EXT_DEPRECATED).setValue(new BooleanType(true)));
}
public static void setExtension(Element focus, String url, Coding c) {
for (Extension e : focus.getExtension())
if (e.getUrl().equals(url)) {
e.setValue(c);
return;
}
focus.getExtension().add(new Extension().setUrl(url).setValue(c));
}
public static void removeExtension(DomainResource focus, String url) {
Iterator<Extension> i = focus.getExtension().iterator();
while (i.hasNext()) {
Extension e = i.next(); // must be called before you can call i.remove()
if (e.getUrl().equals(url)) {
i.remove();
}
}
}
public static void removeExtension(Element focus, String url) {
Iterator<Extension> i = focus.getExtension().iterator();
while (i.hasNext()) {
Extension e = i.next(); // must be called before you can call i.remove()
if (e.getUrl().equals(url)) {
i.remove();
}
}
}
}

View File

@ -0,0 +1,131 @@
package ca.uhn.hapi.converters.server;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
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;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.MatcherAssert.assertThat;
public class VersionedApiConverterInterceptorR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(VersionedApiConverterInterceptorR4Test.class);
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = FhirContext.forDstu3();
private static int ourPort;
private static Server ourServer;
@Test
public void testSearchNormal() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient");
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertThat(responseContent, containsString("\"family\": \"FAMILY\""));
}
}
@Test
public void testSearchConvertToR2() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient");
httpGet.addHeader("Accept", "application/fhir+json; fhirVersion=1.0");
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertThat(responseContent, containsString("\"family\": ["));
}
}
@Test
public void testSearchConvertToR2ByFormatParam() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_format=" + UrlUtil.escapeUrlParam("application/fhir+json; fhirVersion=1.0"));
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(responseContent);
assertThat(responseContent, containsString("\"family\": ["));
}
}
@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);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer(ourCtx);
servlet.setDefaultResponseEncoding(EncodingEnum.JSON);
servlet.setDefaultPrettyPrint(true);
servlet.registerInterceptor(new VersionedApiConverterInterceptor());
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
public static class DummyPatientResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@SuppressWarnings("rawtypes")
@Search()
public List search() {
ArrayList<Patient> retVal = new ArrayList<>();
Patient patient = new Patient();
patient.getIdElement().setValue("Patient/A");
patient.addName(new HumanName().setFamily("FAMILY"));
retVal.add(patient);
return retVal;
}
}
}

View File

@ -0,0 +1,48 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
</encoder>
</appender>
<logger name="org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.eclipse.jetty.websocket" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="org.eclipse" additivity="false" level="error">
</logger>
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<logger name="ca.uhn.fhir.jpa.dao" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<!-- Set to 'trace' to enable SQL logging -->
<logger name="org.hibernate.SQL" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<!-- Set to 'trace' to enable SQL Value logging -->
<logger name="org.hibernate.type" additivity="false" level="info">
<appender-ref ref="STDOUT" />
</logger>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -23,7 +23,7 @@ package ca.uhn.fhir.igpacks.parser;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;

View File

@ -20,31 +20,31 @@ package ca.uhn.fhir.jaxrs.client;
* #L% * #L%
*/ */
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Response;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.Response;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
/** /**
* A Http Request based on JaxRs. This is an adapter around the class * A Http Request based on JaxRs. This is an adapter around the class
* {@link javax.ws.rs.client.Invocation Invocation} * {@link javax.ws.rs.client.Invocation Invocation}
* *
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/ */
public class JaxRsHttpRequest implements IHttpRequest { public class JaxRsHttpRequest implements IHttpRequest {
private final Map<String, List<String>> myHeaders = new HashMap<String, List<String>>();
private Invocation.Builder myRequest; private Invocation.Builder myRequest;
private RequestTypeEnum myRequestType; private RequestTypeEnum myRequestType;
private Entity<?> myEntity; private Entity<?> myEntity;
private final Map<String, List<String>> myHeaders = new HashMap<String, List<String>>();
public JaxRsHttpRequest(Invocation.Builder theRequest, RequestTypeEnum theRequestType, Entity<?> theEntity) { public JaxRsHttpRequest(Invocation.Builder theRequest, RequestTypeEnum theRequestType, Entity<?> theEntity) {
this.myRequest = theRequest; this.myRequest = theRequest;
@ -61,24 +61,22 @@ public class JaxRsHttpRequest implements IHttpRequest {
getRequest().header(theName, theValue); getRequest().header(theName, theValue);
} }
/** @Override
* Get the Request public IHttpResponse execute() {
* @return the Request StopWatch responseStopWatch = new StopWatch();
*/ Invocation invocation = getRequest().build(getRequestType().name(), getEntity());
public Invocation.Builder getRequest() { Response response = invocation.invoke();
return myRequest; return new JaxRsHttpResponse(response, responseStopWatch);
} }
/** @Override
* Get the Request Type public Map<String, List<String>> getAllHeaders() {
* @return the request type return this.myHeaders;
*/
public RequestTypeEnum getRequestType() {
return myRequestType == null ? RequestTypeEnum.GET : myRequestType;
} }
/** /**
* Get the Entity * Get the Entity
*
* @return the entity * @return the entity
*/ */
public Entity<?> getEntity() { public Entity<?> getEntity() {
@ -86,15 +84,17 @@ public class JaxRsHttpRequest implements IHttpRequest {
} }
@Override @Override
public IHttpResponse execute() { public String getHttpVerbName() {
Invocation invocation = getRequest().build(getRequestType().name(), getEntity()); return myRequestType.name();
Response response = invocation.invoke();
return new JaxRsHttpResponse(response);
} }
@Override /**
public Map<String, List<String>> getAllHeaders() { * Get the Request
return this.myHeaders; *
* @return the Request
*/
public Invocation.Builder getRequest() {
return myRequest;
} }
@Override @Override
@ -103,14 +103,18 @@ public class JaxRsHttpRequest implements IHttpRequest {
return null; return null;
} }
/**
* Get the Request Type
*
* @return the request type
*/
public RequestTypeEnum getRequestType() {
return myRequestType == null ? RequestTypeEnum.GET : myRequestType;
}
@Override @Override
public String getUri() { public String getUri() {
return ""; // TODO: can we get this from somewhere? return ""; // TODO: can we get this from somewhere?
} }
@Override
public String getHttpVerbName() {
return myRequestType.name();
}
} }

View File

@ -25,7 +25,6 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -34,7 +33,8 @@ import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.apache.commons.lang3.ObjectUtils; import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.client.api.IHttpResponse; import ca.uhn.fhir.rest.client.api.IHttpResponse;
@ -42,12 +42,13 @@ import ca.uhn.fhir.rest.client.api.IHttpResponse;
* A Http Response based on JaxRs. This is an adapter around the class {@link javax.ws.rs.core.Response Response} * A Http Response based on JaxRs. This is an adapter around the class {@link javax.ws.rs.core.Response Response}
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare * @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/ */
public class JaxRsHttpResponse implements IHttpResponse { public class JaxRsHttpResponse extends BaseHttpResponse implements IHttpResponse {
private boolean myBufferedEntity = false; private boolean myBufferedEntity = false;
private final Response myResponse; private final Response myResponse;
public JaxRsHttpResponse(Response theResponse) { public JaxRsHttpResponse(Response theResponse, StopWatch theResponseStopWatch) {
super(theResponseStopWatch);
this.myResponse = theResponse; this.myResponse = theResponse;
} }

View File

@ -91,7 +91,7 @@ public class JaxRsResponse extends RestfulResponse<JaxRsRequest> {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
if (outcome != null) { if (outcome != null) {
FhirContext fhirContext = getRequestDetails().getServer().getFhirContext(); FhirContext fhirContext = getRequestDetails().getServer().getFhirContext();
IParser parser = RestfulServerUtils.getNewParser(fhirContext, getRequestDetails()); IParser parser = RestfulServerUtils.getNewParser(fhirContext, fhirContext.getVersion().getVersion(), getRequestDetails());
outcome.execute(parser, writer); outcome.execute(parser, writer);
} }
return sendWriterResponse(operationStatus, getParserType(), null, writer); return sendWriterResponse(operationStatus, getParserType(), null, writer);

View File

@ -114,19 +114,19 @@ public class JaxRsResponseDstu3Test {
boolean allowPrefer = true; boolean allowPrefer = true;
String resourceName = "Patient"; String resourceName = "Patient";
MethodOutcome methodOutcome = new MethodOutcome(theId); MethodOutcome methodOutcome = new MethodOutcome(theId);
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML}); response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML});
boolean addContentLocationHeader = true; boolean addContentLocationHeader = true;
boolean respondGzip = true; boolean respondGzip = true;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request); Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request);
assertEquals(200, result.getStatus()); assertEquals(200, result.getStatus());
assertEquals("application/xml+fhir; charset=UTF-8", result.getHeaderString(Constants.HEADER_CONTENT_TYPE)); assertEquals("application/fhir+xml; charset=UTF-8", result.getHeaderString(Constants.HEADER_CONTENT_TYPE));
assertTrue(result.getEntity().toString().contains("<Patient")); assertTrue(result.getEntity().toString().contains("<Patient"));
assertTrue(result.getEntity().toString().contains("15")); assertTrue(result.getEntity().toString().contains("15"));
} }
@Test @Test
public void testNoOutcomeXml() throws IOException { public void testNoOutcomeXml() throws IOException {
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML}); response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[]{Constants.CT_XML});
boolean addContentLocationHeader = true; boolean addContentLocationHeader = true;
boolean respondGzip = true; boolean respondGzip = true;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), null, theSummaryMode, 204, addContentLocationHeader, respondGzip, this.request); Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), null, theSummaryMode, 204, addContentLocationHeader, respondGzip, this.request);

View File

@ -88,7 +88,7 @@ public class JaxRsResponseTest {
@Test @Test
public void testReturnResponseAsXml() throws IOException { public void testReturnResponseAsXml() throws IOException {
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML }); response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML });
boolean addContentLocationHeader = true; boolean addContentLocationHeader = true;
boolean respondGzip = true; boolean respondGzip = true;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request); Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request);
@ -100,7 +100,7 @@ public class JaxRsResponseTest {
@Test @Test
public void testNoOutcomeXml() throws IOException { public void testNoOutcomeXml() throws IOException {
response.getRequestDetails().getParameters().put(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML }); response.getRequestDetails().addParameter(Constants.PARAM_FORMAT, new String[] { Constants.CT_XML });
boolean addContentLocationHeader = true; boolean addContentLocationHeader = true;
boolean respondGzip = true; boolean respondGzip = true;
Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request); Response result = (Response) RestfulServerUtils.streamResponseAsResource(request.getServer(), createPatient(), theSummaryMode, 200, addContentLocationHeader, respondGzip, this.request);

View File

@ -550,6 +550,27 @@
</configuration> </configuration>
</execution> </execution>
</executions> </executions>
<dependencies>
<!--
These have been added as explicit dependencies
as JDK9 no longer includes them by default
-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb_api_version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>${jaxb_core_version}</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>${jaxb_core_version}</version>
</dependency>
</dependencies>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>

View File

@ -49,6 +49,8 @@ import javax.annotation.Resource;
@EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data") @EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data")
public class BaseConfig implements SchedulingConfigurer { public class BaseConfig implements SchedulingConfigurer {
public static final String TASK_EXECUTOR_NAME = "hapiJpaTaskExecutor";
@Autowired @Autowired
protected Environment myEnv; protected Environment myEnv;
@Resource @Resource
@ -87,18 +89,6 @@ public class BaseConfig implements SchedulingConfigurer {
return new StaleSearchDeletingSvcImpl(); return new StaleSearchDeletingSvcImpl();
} }
@Bean
@Lazy
public SubscriptionRestHookInterceptor subscriptionRestHookInterceptor() {
return new SubscriptionRestHookInterceptor();
}
@Bean
@Lazy
public SubscriptionWebsocketInterceptor subscriptionWebsocketInterceptor() {
return new SubscriptionWebsocketInterceptor();
}
/** /**
* Note: If you're going to use this, you need to provide a bean * Note: If you're going to use this, you need to provide a bean
* of type {@link ca.uhn.fhir.jpa.subscription.email.IEmailSender} * of type {@link ca.uhn.fhir.jpa.subscription.email.IEmailSender}
@ -111,6 +101,18 @@ public class BaseConfig implements SchedulingConfigurer {
} }
@Bean @Bean
@Lazy
public SubscriptionRestHookInterceptor subscriptionRestHookInterceptor() {
return new SubscriptionRestHookInterceptor();
}
@Bean
@Lazy
public SubscriptionWebsocketInterceptor subscriptionWebsocketInterceptor() {
return new SubscriptionWebsocketInterceptor();
}
@Bean(name=TASK_EXECUTOR_NAME)
public TaskScheduler taskScheduler() { public TaskScheduler taskScheduler() {
ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler(); ConcurrentTaskScheduler retVal = new ConcurrentTaskScheduler();
retVal.setConcurrentExecutor(scheduledExecutorService().getObject()); retVal.setConcurrentExecutor(scheduledExecutorService().getObject());

View File

@ -18,7 +18,7 @@ import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.IValidatorModule;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator; import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
import org.hl7.fhir.dstu3.utils.IResourceValidator.BestPracticeWarningLevel; import org.hl7.fhir.r4.utils.IResourceValidator;
import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -66,7 +66,7 @@ public class BaseDstu3Config extends BaseConfig {
@Lazy @Lazy
public IValidatorModule instanceValidatorDstu3() { public IValidatorModule instanceValidatorDstu3() {
FhirInstanceValidator val = new FhirInstanceValidator(); FhirInstanceValidator val = new FhirInstanceValidator();
val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning); val.setBestPracticeWarningLevel(IResourceValidator.BestPracticeWarningLevel.Warning);
val.setValidationSupport(validationSupportChainDstu3()); val.setValidationSupport(validationSupportChainDstu3());
return val; return val;
} }

View File

@ -50,6 +50,7 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.*; import ca.uhn.fhir.util.*;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@ -60,9 +61,14 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils; import org.apache.http.client.utils.URLEncodedUtils;
import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.r4.model.BaseResource; import org.hl7.fhir.r4.model.BaseResource;
import org.hl7.fhir.r4.model.Bundle.HTTPVerb; import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
import org.hl7.fhir.r4.model.CanonicalType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Reference;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@ -75,18 +81,17 @@ import javax.xml.stream.events.Characters;
import javax.xml.stream.events.XMLEvent; import javax.xml.stream.events.XMLEvent;
import java.io.CharArrayWriter; import java.io.CharArrayWriter;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.nio.CharBuffer;
import java.text.Normalizer; import java.text.Normalizer;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.*;
@SuppressWarnings("WeakerAccess")
public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao { public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
public static final long INDEX_STATUS_INDEXED = Long.valueOf(1L); public static final long INDEX_STATUS_INDEXED = 1L;
public static final long INDEX_STATUS_INDEXING_FAILED = Long.valueOf(2L); public static final long INDEX_STATUS_INDEXING_FAILED = 2L;
public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile"; public static final String NS_JPA_PROFILE = "https://github.com/jamesagnew/hapi-fhir/ns/jpa/profile";
public static final String OO_SEVERITY_ERROR = "error"; public static final String OO_SEVERITY_ERROR = "error";
public static final String OO_SEVERITY_INFO = "information"; public static final String OO_SEVERITY_INFO = "information";
@ -104,6 +109,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirDao.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirDao.class);
private static final Map<FhirVersionEnum, FhirContext> ourRetrievalContexts = new HashMap<FhirVersionEnum, FhirContext>(); private static final Map<FhirVersionEnum, FhirContext> ourRetrievalContexts = new HashMap<FhirVersionEnum, FhirContext>();
private static final String PROCESSING_SUB_REQUEST = "BaseHapiFhirDao.processingSubRequest"; private static final String PROCESSING_SUB_REQUEST = "BaseHapiFhirDao.processingSubRequest";
private static boolean ourValidationDisabledForUnitTest;
static { static {
Map<String, Class<? extends IQueryParameterType>> resourceMetaParams = new HashMap<String, Class<? extends IQueryParameterType>>(); Map<String, Class<? extends IQueryParameterType>> resourceMetaParams = new HashMap<String, Class<? extends IQueryParameterType>>();
@ -310,6 +316,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
nextObject = ((IBaseExtension<?, ?>) nextObject).getValue(); nextObject = ((IBaseExtension<?, ?>) nextObject).getValue();
} }
if (nextObject instanceof CanonicalType) {
nextObject = new Reference(((CanonicalType) nextObject).getValueAsString());
}
IIdType nextId; IIdType nextId;
if (nextObject instanceof IBaseReference) { if (nextObject instanceof IBaseReference) {
IBaseReference nextValue = (IBaseReference) nextObject; IBaseReference nextValue = (IBaseReference) nextObject;
@ -379,7 +389,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} else { } else {
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime); ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
if (theLinks.add(resourceLink)) { if (theLinks.add(resourceLink)) {
ourLog.info("Indexing remote resource reference URL: {}", nextId); ourLog.debug("Indexing remote resource reference URL: {}", nextId);
} }
continue; continue;
} }
@ -417,7 +427,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
IBaseResource newResource = missingResourceDef.newInstance(); IBaseResource newResource = missingResourceDef.newInstance();
newResource.setId(resName + "/" + id); newResource.setId(resName + "/" + id);
IFhirResourceDao<IBaseResource> placeholderResourceDao = (IFhirResourceDao<IBaseResource>) getDao(newResource.getClass()); IFhirResourceDao<IBaseResource> placeholderResourceDao = (IFhirResourceDao<IBaseResource>) getDao(newResource.getClass());
ourLog.info("Automatically creating empty placeholder resource: {}", newResource.getIdElement().getValue()); ourLog.debug("Automatically creating empty placeholder resource: {}", newResource.getIdElement().getValue());
valueOf = placeholderResourceDao.update(newResource).getEntity().getId(); valueOf = placeholderResourceDao.update(newResource).getEntity().getId();
} else { } else {
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
@ -632,6 +642,16 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
} }
protected void flushJpaSession() {
SessionImpl session = (SessionImpl) myEntityManager.unwrap(Session.class);
int insertionCount = session.getActionQueue().numberOfInsertions();
int updateCount = session.getActionQueue().numberOfUpdates();
StopWatch sw = new StopWatch();
myEntityManager.flush();
ourLog.debug("Session flush took {}ms for {} inserts and {} updates", sw.getMillis(), insertionCount, updateCount);
}
private Set<TagDefinition> getAllTagDefinitions(ResourceTable theEntity) { private Set<TagDefinition> getAllTagDefinitions(ResourceTable theEntity) {
HashSet<TagDefinition> retVal = Sets.newHashSet(); HashSet<TagDefinition> retVal = Sets.newHashSet();
for (ResourceTag next : theEntity.getTags()) { for (ResourceTag next : theEntity.getTags()) {
@ -673,7 +693,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <R extends IBaseResource> IFhirResourceDao<R> getDao(Class<R> theType) { public <R extends IBaseResource> IFhirResourceDao<R> getDao(Class<R> theType) {
if (myResourceTypeToDao == null) { if (myResourceTypeToDao == null) {
Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> theResourceTypeToDao = new HashMap<Class<? extends IBaseResource>, IFhirResourceDao<?>>(); Map<Class<? extends IBaseResource>, IFhirResourceDao<?>> theResourceTypeToDao = new HashMap<>();
for (IFhirResourceDao<?> next : myResourceDaos) { for (IFhirResourceDao<?> next : myResourceDaos) {
theResourceTypeToDao.put(next.getResourceType(), next); theResourceTypeToDao.put(next.getResourceType(), next);
} }
@ -1213,6 +1233,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
retVal.removeAll(theToRemove); retVal.removeAll(theToRemove);
return retVal; return retVal;
} }
private void setUpdatedTime(Collection<? extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) { private void setUpdatedTime(Collection<? extends BaseResourceIndexedSearchParam> theParams, Date theUpdateTime) {
for (BaseResourceIndexedSearchParam nextSearchParam : theParams) { for (BaseResourceIndexedSearchParam nextSearchParam : theParams) {
nextSearchParam.setUpdated(theUpdateTime); nextSearchParam.setUpdated(theUpdateTime);
@ -1377,7 +1398,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
*/ */
if (theResource != null) { if (theResource != null) {
if (thePerformIndexing) { if (thePerformIndexing) {
validateResourceForStorage((T) theResource, theEntity); if (!ourValidationDisabledForUnitTest) {
validateResourceForStorage((T) theResource, theEntity);
}
} }
String resourceType = myContext.getResourceDefinition(theResource).getName(); String resourceType = myContext.getResourceDefinition(theResource).getName();
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) { if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
@ -1546,7 +1569,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
Long next = matches.iterator().next(); Long next = matches.iterator().next();
String newId = translatePidIdToForcedId(resourceTypeString, next); String newId = translatePidIdToForcedId(resourceTypeString, next);
ourLog.info("Replacing inline match URL[{}] with ID[{}}", nextId.getValue(), newId); ourLog.debug("Replacing inline match URL[{}] with ID[{}}", nextId.getValue(), newId);
nextRef.setReference(newId); nextRef.setReference(newId);
} }
} }
@ -1612,7 +1635,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
if (!changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange()) { if (!changed.isChanged() && !theForceUpdate && myConfig.isSuppressUpdatesWithNoChange()) {
ourLog.info("Resource {} has not changed", theEntity.getIdDt().toUnqualified().getValue()); ourLog.debug("Resource {} has not changed", theEntity.getIdDt().toUnqualified().getValue());
if (theResource != null) { if (theResource != null) {
populateResourceIdFromEntity(theEntity, theResource); populateResourceIdFromEntity(theEntity, theResource);
} }
@ -1654,7 +1677,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
historyEntry.setEncoding(changed.getEncoding()); historyEntry.setEncoding(changed.getEncoding());
historyEntry.setResource(changed.getResource()); historyEntry.setResource(changed.getResource());
ourLog.info("Saving history entry {}", historyEntry.getIdDt()); ourLog.debug("Saving history entry {}", historyEntry.getIdDt());
myResourceHistoryTableDao.save(historyEntry); myResourceHistoryTableDao.save(historyEntry);
} }
@ -1691,6 +1714,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
for (ResourceIndexedSearchParamString next : removeCommon(existingStringParams, stringParams)) { for (ResourceIndexedSearchParamString next : removeCommon(existingStringParams, stringParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getParamsString().remove(next);
} }
for (ResourceIndexedSearchParamString next : removeCommon(stringParams, existingStringParams)) { for (ResourceIndexedSearchParamString next : removeCommon(stringParams, existingStringParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1698,6 +1722,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
for (ResourceIndexedSearchParamToken next : removeCommon(existingTokenParams, tokenParams)) { for (ResourceIndexedSearchParamToken next : removeCommon(existingTokenParams, tokenParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getParamsToken().remove(next);
} }
for (ResourceIndexedSearchParamToken next : removeCommon(tokenParams, existingTokenParams)) { for (ResourceIndexedSearchParamToken next : removeCommon(tokenParams, existingTokenParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1705,6 +1730,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
for (ResourceIndexedSearchParamNumber next : removeCommon(existingNumberParams, numberParams)) { for (ResourceIndexedSearchParamNumber next : removeCommon(existingNumberParams, numberParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getParamsNumber().remove(next);
} }
for (ResourceIndexedSearchParamNumber next : removeCommon(numberParams, existingNumberParams)) { for (ResourceIndexedSearchParamNumber next : removeCommon(numberParams, existingNumberParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1712,6 +1738,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
for (ResourceIndexedSearchParamQuantity next : removeCommon(existingQuantityParams, quantityParams)) { for (ResourceIndexedSearchParamQuantity next : removeCommon(existingQuantityParams, quantityParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getParamsQuantity().remove(next);
} }
for (ResourceIndexedSearchParamQuantity next : removeCommon(quantityParams, existingQuantityParams)) { for (ResourceIndexedSearchParamQuantity next : removeCommon(quantityParams, existingQuantityParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1720,6 +1747,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
// Store date SP's // Store date SP's
for (ResourceIndexedSearchParamDate next : removeCommon(existingDateParams, dateParams)) { for (ResourceIndexedSearchParamDate next : removeCommon(existingDateParams, dateParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getParamsDate().remove(next);
} }
for (ResourceIndexedSearchParamDate next : removeCommon(dateParams, existingDateParams)) { for (ResourceIndexedSearchParamDate next : removeCommon(dateParams, existingDateParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1728,6 +1756,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
// Store URI SP's // Store URI SP's
for (ResourceIndexedSearchParamUri next : removeCommon(existingUriParams, uriParams)) { for (ResourceIndexedSearchParamUri next : removeCommon(existingUriParams, uriParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getParamsUri().remove(next);
} }
for (ResourceIndexedSearchParamUri next : removeCommon(uriParams, existingUriParams)) { for (ResourceIndexedSearchParamUri next : removeCommon(uriParams, existingUriParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1736,6 +1765,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
// Store Coords SP's // Store Coords SP's
for (ResourceIndexedSearchParamCoords next : removeCommon(existingCoordsParams, coordsParams)) { for (ResourceIndexedSearchParamCoords next : removeCommon(existingCoordsParams, coordsParams)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getParamsCoords().remove(next);
} }
for (ResourceIndexedSearchParamCoords next : removeCommon(coordsParams, existingCoordsParams)) { for (ResourceIndexedSearchParamCoords next : removeCommon(coordsParams, existingCoordsParams)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1744,6 +1774,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
// Store resource links // Store resource links
for (ResourceLink next : removeCommon(existingResourceLinks, links)) { for (ResourceLink next : removeCommon(existingResourceLinks, links)) {
myEntityManager.remove(next); myEntityManager.remove(next);
theEntity.getResourceLinks().remove(next);
} }
for (ResourceLink next : removeCommon(links, existingResourceLinks)) { for (ResourceLink next : removeCommon(links, existingResourceLinks)) {
myEntityManager.persist(next); myEntityManager.persist(next);
@ -1753,23 +1784,20 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
// Store composite string uniques // Store composite string uniques
if (getConfig().isUniqueIndexesEnabled()) { if (getConfig().isUniqueIndexesEnabled()) {
for (ResourceIndexedCompositeStringUnique next : existingCompositeStringUniques) { for (ResourceIndexedCompositeStringUnique next : removeCommon(existingCompositeStringUniques, compositeStringUniques)) {
if (!compositeStringUniques.contains(next)) { ourLog.debug("Removing unique index: {}", next);
ourLog.debug("Removing unique index: {}", next); myEntityManager.remove(next);
myEntityManager.remove(next); theEntity.getParamsCompositeStringUnique().remove(next);
}
} }
for (ResourceIndexedCompositeStringUnique next : compositeStringUniques) { for (ResourceIndexedCompositeStringUnique next : removeCommon(compositeStringUniques, existingCompositeStringUniques)) {
if (!existingCompositeStringUniques.contains(next)) { if (myConfig.isUniqueIndexesCheckedBeforeSave()) {
if (myConfig.isUniqueIndexesCheckedBeforeSave()) { ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString());
ResourceIndexedCompositeStringUnique existing = myResourceIndexedCompositeStringUniqueDao.findByQueryString(next.getIndexString()); if (existing != null) {
if (existing != null) { throw new PreconditionFailedException("Can not create resource of type " + theEntity.getResourceType() + " as it would create a duplicate index matching query: " + next.getIndexString() + " (existing index belongs to " + existing.getResource().getIdDt().toUnqualifiedVersionless().getValue() + ")");
throw new PreconditionFailedException("Can not create resource of type " + theEntity.getResourceType() + " as it would create a duplicate index matching query: " + next.getIndexString() + " (existing index belongs to " + existing.getResource().getIdDt().toUnqualifiedVersionless().getValue() + ")");
}
} }
ourLog.debug("Persisting unique index: {}", next);
myEntityManager.persist(next);
} }
ourLog.debug("Persisting unique index: {}", next);
myEntityManager.persist(next);
} }
} }
@ -1813,7 +1841,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
if (nextChildDef instanceof RuntimeChildResourceDefinition) { if (nextChildDef instanceof RuntimeChildResourceDefinition) {
RuntimeChildResourceDefinition nextChildDefRes = (RuntimeChildResourceDefinition) nextChildDef; RuntimeChildResourceDefinition nextChildDefRes = (RuntimeChildResourceDefinition) nextChildDef;
Set<String> validTypes = new HashSet<String>(); Set<String> validTypes = new HashSet<>();
boolean allowAny = false; boolean allowAny = false;
for (Class<? extends IBaseResource> nextValidType : nextChildDefRes.getResourceTypes()) { for (Class<? extends IBaseResource> nextValidType : nextChildDefRes.getResourceTypes()) {
if (nextValidType.isInterface()) { if (nextValidType.isInterface()) {
@ -2105,6 +2133,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return b.toString(); return b.toString();
} }
/**
* Do not call this method outside of unit tests
*/
@VisibleForTesting
public static void setValidationDisabledForUnitTest(boolean theValidationDisabledForUnitTest) {
ourValidationDisabledForUnitTest = theValidationDisabledForUnitTest;
}
private static List<BaseCodingDt> toBaseCodingList(List<IBaseCoding> theSecurityLabels) { private static List<BaseCodingDt> toBaseCodingList(List<IBaseCoding> theSecurityLabels) {
ArrayList<BaseCodingDt> retVal = new ArrayList<BaseCodingDt>(theSecurityLabels.size()); ArrayList<BaseCodingDt> retVal = new ArrayList<BaseCodingDt>(theSecurityLabels.size());
for (IBaseCoding next : theSecurityLabels) { for (IBaseCoding next : theSecurityLabels) {
@ -2131,7 +2167,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
if (forcedId.isEmpty() == false) { if (forcedId.isEmpty() == false) {
List<Long> retVal = new ArrayList<Long>(forcedId.size()); List<Long> retVal = new ArrayList<>(forcedId.size());
for (ForcedId next : forcedId) { for (ForcedId next : forcedId) {
retVal.add(next.getResourcePid()); retVal.add(next.getResourcePid());
} }

View File

@ -32,7 +32,7 @@ import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider; import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.jpa.util.DeleteConflict; import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils; import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils; import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
import ca.uhn.fhir.model.api.*; import ca.uhn.fhir.model.api.*;
@ -56,6 +56,7 @@ import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.instance.model.api.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Required;
import org.springframework.lang.NonNull;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
@ -126,7 +127,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
} }
} }
ourLog.info("Processed addTag {}/{} on {} in {}ms", new Object[]{theScheme, theTerm, theId, w.getMillisAndRestart()}); ourLog.debug("Processed addTag {}/{} on {} in {}ms", theScheme, theTerm, theId, w.getMillisAndRestart());
} }
@Override @Override
@ -272,7 +273,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateDeleteConflictsEmptyOrThrowException(deleteConflicts); validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart()); ourLog.debug("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return retVal; return retVal;
} }
@ -349,7 +350,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code); OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
} }
ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", new Object[]{theUrl, deletedResources.size(), w.getMillis()}); ourLog.debug("Processed delete on {} (matched {} resource(s)) in {}ms", theUrl, deletedResources.size(), w.getMillis());
DeleteMethodOutcome retVal = new DeleteMethodOutcome(); DeleteMethodOutcome retVal = new DeleteMethodOutcome();
retVal.setDeletedEntities(deletedResources); retVal.setDeletedEntities(deletedResources);
@ -461,7 +462,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart()); String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart());
outcome.setOperationOutcome(createInfoOperationOutcome(msg)); outcome.setOperationOutcome(createInfoOperationOutcome(msg));
ourLog.info(msg); ourLog.debug(msg);
return outcome; return outcome;
} }
@ -529,7 +530,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
TagList tags = super.getTags(myResourceType, null); TagList tags = super.getTags(myResourceType, null);
ourLog.info("Processed getTags on {} in {}ms", myResourceName, w.getMillisAndRestart()); ourLog.debug("Processed getTags on {} in {}ms", myResourceName, w.getMillisAndRestart());
return tags; return tags;
} }
@ -556,7 +557,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
TagList retVal = super.getTags(myResourceType, theResourceId); TagList retVal = super.getTags(myResourceType, theResourceId);
ourLog.info("Processed getTags on {} in {}ms", theResourceId, w.getMillisAndRestart()); ourLog.debug("Processed getTags on {} in {}ms", theResourceId, w.getMillisAndRestart());
return retVal; return retVal;
} }
@ -568,7 +569,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
IBundleProvider retVal = super.history(myResourceName, null, theSince, theUntil); IBundleProvider retVal = super.history(myResourceName, null, theSince, theUntil);
ourLog.info("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart()); ourLog.debug("Processed history on {} in {}ms", myResourceName, w.getMillisAndRestart());
return retVal; return retVal;
} }
@ -585,7 +586,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
IBundleProvider retVal = super.history(myResourceName, entity.getId(), theSince, theUntil); IBundleProvider retVal = super.history(myResourceName, entity.getId(), theSince, theUntil);
ourLog.info("Processed history on {} in {}ms", id, w.getMillisAndRestart()); ourLog.debug("Processed history on {} in {}ms", id, w.getMillisAndRestart());
return retVal; return retVal;
} }
@ -612,25 +613,31 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return theRequestDetails.getServer().getPagingProvider() instanceof DatabaseBackedPagingProvider; return theRequestDetails.getServer().getPagingProvider() instanceof DatabaseBackedPagingProvider;
} }
protected void markResourcesMatchingExpressionAsNeedingReindexing(String theExpression) { protected void markResourcesMatchingExpressionAsNeedingReindexing(Boolean theCurrentlyReindexing, String theExpression) {
// Avoid endless loops
if (Boolean.TRUE.equals(theCurrentlyReindexing)) {
return;
}
if (myDaoConfig.isMarkResourcesForReindexingUponSearchParameterChange()) { if (myDaoConfig.isMarkResourcesForReindexingUponSearchParameterChange()) {
if (isNotBlank(theExpression)) { if (isNotBlank(theExpression)) {
final String resourceType = theExpression.substring(0, theExpression.indexOf('.')); final String resourceType = theExpression.substring(0, theExpression.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", resourceType, theExpression); ourLog.debug("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", resourceType, theExpression);
TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager); TransactionTemplate txTemplate = new TransactionTemplate(myPlatformTransactionManager);
txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
int updatedCount = txTemplate.execute(new TransactionCallback<Integer>() { Integer updatedCount = txTemplate.execute(new TransactionCallback<Integer>() {
@Override @Override
public Integer doInTransaction(TransactionStatus theStatus) { public @NonNull Integer doInTransaction(TransactionStatus theStatus) {
return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType); return myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
} }
}); });
ourLog.info("Marked {} resources for reindexing", updatedCount); ourLog.debug("Marked {} resources for reindexing", updatedCount);
} }
} }
mySearchParamRegistry.forceRefresh();
mySearchParamRegistry.requestRefresh();
} }
@Override @Override
@ -658,7 +665,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
doMetaAdd(theMetaAdd, history); doMetaAdd(theMetaAdd, history);
} }
ourLog.info("Processed metaAddOperation on {} in {}ms", new Object[]{theResourceId, w.getMillisAndRestart()}); ourLog.debug("Processed metaAddOperation on {} in {}ms", new Object[]{theResourceId, w.getMillisAndRestart()});
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails); MT retVal = (MT) metaGetOperation(theMetaAdd.getClass(), theResourceId, theRequestDetails);
@ -692,7 +699,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
myEntityManager.flush(); myEntityManager.flush();
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[]{theResourceId.getValue(), w.getMillisAndRestart()}); ourLog.debug("Processed metaDeleteOperation on {} in {}ms", new Object[]{theResourceId.getValue(), w.getMillisAndRestart()});
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails); MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
@ -707,7 +714,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
notifyInterceptors(RestOperationTypeEnum.META, requestDetails); notifyInterceptors(RestOperationTypeEnum.META, requestDetails);
} }
Set<TagDefinition> tagDefs = new HashSet<TagDefinition>(); Set<TagDefinition> tagDefs = new HashSet<>();
BaseHasResource entity = readEntity(theId); BaseHasResource entity = readEntity(theId);
for (BaseTag next : entity.getTags()) { for (BaseTag next : entity.getTags()) {
tagDefs.add(next.getTag()); tagDefs.add(next.getTag());
@ -733,9 +740,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
q.setParameter("res_type", myResourceName); q.setParameter("res_type", myResourceName);
List<TagDefinition> tagDefinitions = q.getResultList(); List<TagDefinition> tagDefinitions = q.getResultList();
MT retVal = toMetaDt(theType, tagDefinitions); return toMetaDt(theType, tagDefinitions);
return retVal;
} }
@Override @Override
@ -859,15 +864,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
throw new ResourceGoneException("Resource was deleted at " + deleted.getValueAsString()); throw new ResourceGoneException("Resource was deleted at " + deleted.getValueAsString());
} }
ourLog.info("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart()); ourLog.debug("Processed read on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return retVal; return retVal;
} }
@Override @Override
public BaseHasResource readEntity(IIdType theId) { public BaseHasResource readEntity(IIdType theId) {
BaseHasResource entity = readEntity(theId, true);
return entity; return readEntity(theId, true);
} }
@Override @Override
@ -885,7 +889,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (theId.isVersionIdPartValidLong() == false) { if (theId.isVersionIdPartValidLong() == false) {
throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidVersion", theId.getVersionIdPart(), theId.toUnqualifiedVersionless())); throw new ResourceNotFoundException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "invalidVersion", theId.getVersionIdPart(), theId.toUnqualifiedVersionless()));
} }
if (entity.getVersion() != theId.getVersionIdPartAsLong().longValue()) { if (entity.getVersion() != theId.getVersionIdPartAsLong()) {
entity = null; entity = null;
} }
} }
@ -925,7 +929,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Override @Override
public void reindex(T theResource, ResourceTable theEntity) { public void reindex(T theResource, ResourceTable theEntity) {
ourLog.debug("Indexing resource {} - PID {}", theResource.getIdElement().getValue(), theEntity.getId()); ourLog.debug("Indexing resource {} - PID {}", theResource.getIdElement().getValue(), theEntity.getId());
CURRENTLY_REINDEXING.put(theResource, Boolean.TRUE);
updateEntity(theResource, theEntity, null, true, false, theEntity.getUpdatedDate(), true, false); updateEntity(theResource, theEntity, null, true, false, theEntity.getUpdatedDate(), true, false);
CURRENTLY_REINDEXING.put(theResource, null);
} }
@Override @Override
@ -962,7 +968,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
myEntityManager.merge(entity); myEntityManager.merge(entity);
ourLog.info("Processed remove tag {}/{} on {} in {}ms", new Object[]{theScheme, theTerm, theId.getValue(), w.getMillisAndRestart()}); ourLog.debug("Processed remove tag {}/{} on {} in {}ms", theScheme, theTerm, theId.getValue(), w.getMillisAndRestart());
} }
@Transactional(propagation = Propagation.SUPPORTS) @Transactional(propagation = Propagation.SUPPORTS)
@ -1276,7 +1282,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart()); String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulCreate", outcome.getId(), w.getMillisAndRestart());
outcome.setOperationOutcome(createInfoOperationOutcome(msg)); outcome.setOperationOutcome(createInfoOperationOutcome(msg));
ourLog.info(msg); ourLog.debug(msg);
return outcome; return outcome;
} }
@ -1333,7 +1339,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
} }
if (myDaoConfig.isEnforceReferentialIntegrityOnDelete() == false && !theForValidate) { if (myDaoConfig.isEnforceReferentialIntegrityOnDelete() == false && !theForValidate) {
ourLog.info("Deleting {} resource dependencies which can no longer be satisfied", resultList.size()); ourLog.debug("Deleting {} resource dependencies which can no longer be satisfied", resultList.size());
myResourceLinkDao.delete(resultList); myResourceLinkDao.delete(resultList);
return; return;
} }

View File

@ -6,7 +6,7 @@ import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
import ca.uhn.fhir.jpa.entity.ForcedId; import ca.uhn.fhir.jpa.entity.ForcedId;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.util.ReindexFailureException; import ca.uhn.fhir.jpa.util.ReindexFailureException;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -66,13 +66,21 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Autowired @Autowired
private ITermConceptDao myTermConceptDao; private ITermConceptDao myTermConceptDao;
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
@Autowired @Autowired
private PlatformTransactionManager myTxManager; private PlatformTransactionManager myTxManager;
@Autowired @Autowired
private IResourceTableDao myResourceTableDao; private IResourceTableDao myResourceTableDao;
private int doPerformReindexingPass(final Integer theCount) { private int doPerformReindexingPass(final Integer theCount) {
/*
* If any search parameters have been recently added or changed,
* this makes sure that the cache has been reloaded to reflect
* them.
*/
mySearchParamRegistry.refreshCacheIfNecessary();
TransactionTemplate txTemplate = new TransactionTemplate(myTxManager); TransactionTemplate txTemplate = new TransactionTemplate(myTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
return doPerformReindexingPassForResources(theCount, txTemplate); return doPerformReindexingPassForResources(theCount, txTemplate);

View File

@ -24,17 +24,25 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.util.SearchParameterUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.*; import java.util.*;
public abstract class BaseSearchParamRegistry implements ISearchParamRegistry { import static org.apache.commons.lang3.StringUtils.isBlank;
public abstract class BaseSearchParamRegistry<SP extends IBaseResource> implements ISearchParamRegistry {
private static final int MAX_MANAGED_PARAM_COUNT = 10000;
private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class); private static final Logger ourLog = LoggerFactory.getLogger(BaseSearchParamRegistry.class);
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams; private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams = Collections.emptyMap(); private volatile Map<String, List<JpaRuntimeSearchParam>> myActiveUniqueSearchParams = Collections.emptyMap();
@ -43,14 +51,26 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
private FhirContext myCtx; private FhirContext myCtx;
@Autowired @Autowired
private Collection<IFhirResourceDao<?>> myDaos; private Collection<IFhirResourceDao<?>> myDaos;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
@Autowired
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
public BaseSearchParamRegistry() { public BaseSearchParamRegistry() {
super(); super();
} }
@Override
public void requestRefresh() {
synchronized (this) {
myLastRefresh = 0;
}
}
@Override @Override
public void forceRefresh() { public void forceRefresh() {
// nothing by default requestRefresh();
refreshCacheIfNecessary();
} }
@Override @Override
@ -63,21 +83,19 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
return retVal; return retVal;
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
return myBuiltInSearchParams; return myActiveSearchParams;
} }
@Override @Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) { public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
Validate.notBlank(theResourceName, "theResourceName must not be blank or null"); return myActiveSearchParams.get(theResourceName);
return myBuiltInSearchParams.get(theResourceName);
} }
@Override @Override
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) { public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName) {
refreshCacheIfNecessary();
List<JpaRuntimeSearchParam> retVal = myActiveUniqueSearchParams.get(theResourceName); List<JpaRuntimeSearchParam> retVal = myActiveUniqueSearchParams.get(theResourceName);
if (retVal == null) { if (retVal == null) {
retVal = Collections.emptyList(); retVal = Collections.emptyList();
@ -87,7 +105,6 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
@Override @Override
public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) { public List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames) {
refreshCacheIfNecessary();
Map<Set<String>, List<JpaRuntimeSearchParam>> paramNamesToParams = myActiveParamNamesToUniqueSearchParams.get(theResourceName); Map<Set<String>, List<JpaRuntimeSearchParam>> paramNamesToParams = myActiveParamNamesToUniqueSearchParams.get(theResourceName);
if (paramNamesToParams == null) { if (paramNamesToParams == null) {
@ -105,7 +122,18 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
return myBuiltInSearchParams; return myBuiltInSearchParams;
} }
public void populateActiveSearchParams(Map<String, Map<String, RuntimeSearchParam>> theActiveSearchParams) { private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
public abstract IFhirResourceDao<SP> getSearchParameterDao();
private void populateActiveSearchParams(Map<String, Map<String, RuntimeSearchParam>> theActiveSearchParams) {
Map<String, List<JpaRuntimeSearchParam>> activeUniqueSearchParams = new HashMap<>(); Map<String, List<JpaRuntimeSearchParam>> activeUniqueSearchParams = new HashMap<>();
Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> activeParamNamesToUniqueSearchParams = new HashMap<>(); Map<String, Map<Set<String>, List<JpaRuntimeSearchParam>>> activeParamNamesToUniqueSearchParams = new HashMap<>();
@ -196,8 +224,100 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
} }
myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams); myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams);
refreshCacheIfNecessary();
} }
protected abstract void refreshCacheIfNecessary(); @Override
public void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = getSearchParameterDao().search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size >= MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SP nextSp = (SP) nextResource;
RuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
for (String nextBaseName : SearchParameterUtil.getBaseAsStrings(myCtx, nextSp)) {
if (isBlank(nextBaseName)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, nextBaseName);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND)
public void refreshCacheOnSchedule() {
refreshCacheIfNecessary();
}
protected abstract RuntimeSearchParam toRuntimeSp(SP theNextSp);
} }

View File

@ -45,7 +45,9 @@ public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2<Se
private IFhirSystemDao<Bundle, MetaDt> mySystemDao; private IFhirSystemDao<Bundle, MetaDt> mySystemDao;
protected void markAffectedResources(SearchParameter theResource) { protected void markAffectedResources(SearchParameter theResource) {
markResourcesMatchingExpressionAsNeedingReindexing(theResource != null ? theResource.getXpath() : null); Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
String expression = theResource != null ? theResource.getXpath() : null;
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
} }
/** /**

View File

@ -25,6 +25,7 @@ import java.util.*;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import ca.uhn.fhir.model.primitive.UriDt;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
@ -453,6 +454,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
continue; continue;
} }
// References
List<BaseResourceReferenceDt> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class); List<BaseResourceReferenceDt> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, BaseResourceReferenceDt.class);
for (BaseResourceReferenceDt nextRef : allRefs) { for (BaseResourceReferenceDt nextRef : allRefs) {
IdDt nextId = nextRef.getReference(); IdDt nextId = nextRef.getReference();
@ -461,13 +463,30 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
} }
if (idSubstitutions.containsKey(nextId)) { if (idSubstitutions.containsKey(nextId)) {
IdDt newId = idSubstitutions.get(nextId); IdDt newId = idSubstitutions.get(nextId);
ourLog.info(" * Replacing resource ref {} with {}", nextId, newId); ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
nextRef.setReference(newId); nextRef.setReference(newId);
} else { } else {
ourLog.debug(" * Reference [{}] does not exist in bundle", nextId); ourLog.debug(" * Reference [{}] does not exist in bundle", nextId);
} }
} }
// URIs
List<UriDt> allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriDt.class);
for (UriDt nextRef : allUris) {
if (nextRef instanceof IIdType) {
continue; // No substitution on the resource ID itself!
}
IdDt nextUriString = new IdDt(nextRef.getValueAsString());
if (idSubstitutions.containsKey(nextUriString)) {
IdDt newId = idSubstitutions.get(nextUriString);
ourLog.debug(" * Replacing resource ref {} with {}", nextUriString, newId);
nextRef.setValue(newId.getValue());
} else {
ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString);
}
}
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource); InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity());
@ -503,7 +522,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
if (replacement.equals(next)) { if (replacement.equals(next)) {
continue; continue;
} }
ourLog.info("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement); ourLog.debug("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
} }
/* /*
@ -532,7 +551,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
} }
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) { for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue); requestDetails.addParameter(nextParamEntry.getKey(), nextValue);
} }
url = url.substring(0, qIndex); url = url.substring(0, qIndex);
} }

View File

@ -1,17 +1,15 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import java.util.Collection;
import java.util.Set;
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider; import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.Collection;
import java.util.Set;
/* /*
* #%L * #%L
@ -35,7 +33,9 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupporti
public interface IDao { public interface IDao {
ResourceMetadataKeySupportingAnyResource<Long, Long> RESOURCE_PID = new MetadataKeyResourcePid("RESOURCE_PID"); MetadataKeyResourcePid RESOURCE_PID = new MetadataKeyResourcePid("RESOURCE_PID");
MetadataKeyCurrentlyReindexing CURRENTLY_REINDEXING = new MetadataKeyCurrentlyReindexing("CURRENTLY_REINDEXING");
FhirContext getContext(); FhirContext getContext();

View File

@ -29,6 +29,9 @@ import java.util.Set;
public interface ISearchParamRegistry { public interface ISearchParamRegistry {
/**
* Request that the cache be refreshed now, in the current thread
*/
void forceRefresh(); void forceRefresh();
/** /**
@ -36,11 +39,18 @@ public interface ISearchParamRegistry {
*/ */
RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName); RuntimeSearchParam getActiveSearchParam(String theResourceName, String theParamName);
Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams();
Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName); Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName);
Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams(); List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames);
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName); List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName);
List<JpaRuntimeSearchParam> getActiveUniqueSearchParams(String theResourceName, Set<String> theParamNames); void refreshCacheIfNecessary();
/**
* Request that the cache be refreshed at the next convenient time (in a different thread)
*/
void requestRefresh();
} }

View File

@ -0,0 +1,70 @@
package ca.uhn.fhir.jpa.dao;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* 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 ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
public final class MetadataKeyCurrentlyReindexing extends ResourceMetadataKeySupportingAnyResource<Boolean, Boolean> {
private static final long serialVersionUID = 1L;
MetadataKeyCurrentlyReindexing(String theValue) {
super(theValue);
}
@Override
public Boolean get(IAnyResource theResource) {
return (Boolean) theResource.getUserData(IDao.CURRENTLY_REINDEXING.name());
}
@Override
public Boolean get(IResource theResource) {
return (Boolean) theResource.getResourceMetadata().get(IDao.CURRENTLY_REINDEXING);
}
public Boolean get(IBaseResource theResource) {
if (theResource instanceof IAnyResource) {
return get((IAnyResource) theResource);
} else {
return get((IResource) theResource);
}
}
@Override
public void put(IAnyResource theResource, Boolean theObject) {
theResource.setUserData(IDao.CURRENTLY_REINDEXING.name(), theObject);
}
public void put(IBaseResource theResource, Boolean theValue) {
if (theResource instanceof IAnyResource) {
put((IAnyResource) theResource, theValue);
} else {
put((IResource) theResource, theValue);
}
}
@Override
public void put(IResource theResource, Boolean theObject) {
theResource.getResourceMetadata().put(IDao.CURRENTLY_REINDEXING, theObject);
}
}

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept; import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.util.BaseIterator; import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.model.api.*; import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
@ -83,7 +83,7 @@ public class SearchBuilder implements ISearchBuilder {
private static final List<Long> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<Long>()); private static final List<Long> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<Long>());
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
private static Long NO_MORE = Long.valueOf(-1); private static Long NO_MORE = -1L;
private static HandlerTypeEnum ourLastHandlerMechanismForUnitTest; private static HandlerTypeEnum ourLastHandlerMechanismForUnitTest;
private List<Long> myAlsoIncludePids; private List<Long> myAlsoIncludePids;
private CriteriaBuilder myBuilder; private CriteriaBuilder myBuilder;
@ -331,10 +331,9 @@ public class SearchBuilder implements ISearchBuilder {
List<Predicate> codePredicates = new ArrayList<Predicate>(); List<Predicate> codePredicates = new ArrayList<Predicate>();
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (params instanceof ReferenceParam) { if (nextOr instanceof ReferenceParam) {
ReferenceParam ref = (ReferenceParam) params; ReferenceParam ref = (ReferenceParam) nextOr;
if (isBlank(ref.getChain())) { if (isBlank(ref.getChain())) {
IIdType dt = new IdDt(ref.getBaseUrl(), ref.getResourceType(), ref.getIdPart(), null); IIdType dt = new IdDt(ref.getBaseUrl(), ref.getResourceType(), ref.getIdPart(), null);
@ -471,7 +470,7 @@ public class SearchBuilder implements ISearchBuilder {
IQueryParameterType chainValue; IQueryParameterType chainValue;
if (remainingChain != null) { if (remainingChain != null) {
if (param == null || param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) { if (param == null || param.getParamType() != RestSearchParameterTypeEnum.REFERENCE) {
ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", new Object[]{nextType.getSimpleName(), chain, remainingChain}); ourLog.debug("Type {} parameter {} is not a reference, can not chain {}", new Object[] {nextType.getSimpleName(), chain, remainingChain});
continue; continue;
} }
@ -533,7 +532,7 @@ public class SearchBuilder implements ISearchBuilder {
} }
} else { } else {
throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + params.getClass()); throw new IllegalArgumentException("Invalid token type (expecting ReferenceParam): " + nextOr.getClass());
} }
} }
@ -771,12 +770,11 @@ public class SearchBuilder implements ISearchBuilder {
return; return;
} }
List<Predicate> codePredicates = new ArrayList<Predicate>(); List<Predicate> codePredicates = new ArrayList<>();
for (IQueryParameterType nextOr : theList) { for (IQueryParameterType nextOr : theList) {
IQueryParameterType params = nextOr;
if (params instanceof UriParam) { if (nextOr instanceof UriParam) {
UriParam param = (UriParam) params; UriParam param = (UriParam) nextOr;
String value = param.getValue(); String value = param.getValue();
if (value == null) { if (value == null) {
@ -821,7 +819,7 @@ public class SearchBuilder implements ISearchBuilder {
} }
codePredicates.add(predicate); codePredicates.add(predicate);
} else { } else {
throw new IllegalArgumentException("Invalid URI type: " + params.getClass()); throw new IllegalArgumentException("Invalid URI type: " + nextOr.getClass());
} }
} }
@ -838,9 +836,7 @@ public class SearchBuilder implements ISearchBuilder {
Predicate orPredicate = myBuilder.or(toArray(codePredicates)); Predicate orPredicate = myBuilder.or(toArray(codePredicates));
Predicate paramNamePredicate = myBuilder.equal(join.get("myParamName"), theParamName); Predicate outerPredicate = combineParamIndexPredicateWithParamNamePredicate(theResourceName, theParamName, join, orPredicate);
Predicate outerPredicate = myBuilder.and(paramNamePredicate, orPredicate);
myPredicates.add(outerPredicate); myPredicates.add(outerPredicate);
} }
@ -1196,7 +1192,7 @@ public class SearchBuilder implements ISearchBuilder {
for (VersionIndependentConcept nextCode : codes) { for (VersionIndependentConcept nextCode : codes) {
List<VersionIndependentConcept> systemCodes = map.get(nextCode.getSystem()); List<VersionIndependentConcept> systemCodes = map.get(nextCode.getSystem());
if (null == systemCodes) { if (null == systemCodes) {
systemCodes = new ArrayList<VersionIndependentConcept>(); systemCodes = new ArrayList<>();
map.put(nextCode.getSystem(), systemCodes); map.put(nextCode.getSystem(), systemCodes);
} }
systemCodes.add(nextCode); systemCodes.add(nextCode);
@ -1343,7 +1339,7 @@ public class SearchBuilder implements ISearchBuilder {
} }
myPredicates = new ArrayList<Predicate>(); myPredicates = new ArrayList<>();
if (myParams.getEverythingMode() != null) { if (myParams.getEverythingMode() != null) {
Join<ResourceTable, ResourceLink> join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT); Join<ResourceTable, ResourceLink> join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
@ -1352,7 +1348,7 @@ public class SearchBuilder implements ISearchBuilder {
StringParam idParm = (StringParam) myParams.get(BaseResource.SP_RES_ID).get(0).get(0); StringParam idParm = (StringParam) myParams.get(BaseResource.SP_RES_ID).get(0).get(0);
Long pid = BaseHapiFhirDao.translateForcedIdToPid(myResourceName, idParm.getValue(), myForcedIdDao); Long pid = BaseHapiFhirDao.translateForcedIdToPid(myResourceName, idParm.getValue(), myForcedIdDao);
if (myAlsoIncludePids == null) { if (myAlsoIncludePids == null) {
myAlsoIncludePids = new ArrayList<Long>(1); myAlsoIncludePids = new ArrayList<>(1);
} }
myAlsoIncludePids.add(pid); myAlsoIncludePids.add(pid);
myPredicates.add(myBuilder.equal(join.get("myTargetResourcePid").as(Long.class), pid)); myPredicates.add(myBuilder.equal(join.get("myTargetResourcePid").as(Long.class), pid));
@ -1475,37 +1471,37 @@ public class SearchBuilder implements ISearchBuilder {
switch (param.getParamType()) { switch (param.getParamType()) {
case STRING: case STRING:
joinAttrName = "myParamsString"; joinAttrName = "myParamsString";
sortAttrName = new String[]{"myValueExact"}; sortAttrName = new String[] {"myValueExact"};
joinType = JoinEnum.STRING; joinType = JoinEnum.STRING;
break; break;
case DATE: case DATE:
joinAttrName = "myParamsDate"; joinAttrName = "myParamsDate";
sortAttrName = new String[]{"myValueLow"}; sortAttrName = new String[] {"myValueLow"};
joinType = JoinEnum.DATE; joinType = JoinEnum.DATE;
break; break;
case REFERENCE: case REFERENCE:
joinAttrName = "myResourceLinks"; joinAttrName = "myResourceLinks";
sortAttrName = new String[]{"myTargetResourcePid"}; sortAttrName = new String[] {"myTargetResourcePid"};
joinType = JoinEnum.REFERENCE; joinType = JoinEnum.REFERENCE;
break; break;
case TOKEN: case TOKEN:
joinAttrName = "myParamsToken"; joinAttrName = "myParamsToken";
sortAttrName = new String[]{"mySystem", "myValue"}; sortAttrName = new String[] {"mySystem", "myValue"};
joinType = JoinEnum.TOKEN; joinType = JoinEnum.TOKEN;
break; break;
case NUMBER: case NUMBER:
joinAttrName = "myParamsNumber"; joinAttrName = "myParamsNumber";
sortAttrName = new String[]{"myValue"}; sortAttrName = new String[] {"myValue"};
joinType = JoinEnum.NUMBER; joinType = JoinEnum.NUMBER;
break; break;
case URI: case URI:
joinAttrName = "myParamsUri"; joinAttrName = "myParamsUri";
sortAttrName = new String[]{"myUri"}; sortAttrName = new String[] {"myUri"};
joinType = JoinEnum.URI; joinType = JoinEnum.URI;
break; break;
case QUANTITY: case QUANTITY:
joinAttrName = "myParamsQuantity"; joinAttrName = "myParamsQuantity";
sortAttrName = new String[]{"myValue"}; sortAttrName = new String[] {"myValue"};
joinType = JoinEnum.QUANTITY; joinType = JoinEnum.QUANTITY;
break; break;
default: default:
@ -1780,7 +1776,7 @@ public class SearchBuilder implements ISearchBuilder {
nextRoundMatches = pidsToInclude; nextRoundMatches = pidsToInclude;
} while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound); } while (includes.size() > 0 && nextRoundMatches.size() > 0 && addedSomeThisRound);
ourLog.info("Loaded {} {} in {} rounds and {} ms", new Object[]{allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart()}); ourLog.info("Loaded {} {} in {} rounds and {} ms", new Object[] {allAdded.size(), theReverseMode ? "_revincludes" : "_includes", roundCounts, w.getMillisAndRestart()});
return allAdded; return allAdded;
} }
@ -2006,8 +2002,17 @@ public class SearchBuilder implements ISearchBuilder {
RuntimeResourceDefinition resourceDef = theContext.getResourceDefinition(theResourceType); RuntimeResourceDefinition resourceDef = theContext.getResourceDefinition(theResourceType);
RuntimeSearchParam param = theCallingDao.getSearchParamByName(resourceDef, theParamName); RuntimeSearchParam param = theCallingDao.getSearchParamByName(resourceDef, theParamName);
List<String> path = param.getPathsSplit(); List<String> path = param.getPathsSplit();
Predicate type = theFrom.get("mySourcePath").in(path);
return type; /*
* SearchParameters can declare paths on multiple resources
* types. Here we only want the ones that actually apply.
*/
for (Iterator<String> iter = path.iterator(); iter.hasNext(); ) {
if (!iter.next().startsWith(theResourceType + ".")) {
iter.remove();
}
}
return theFrom.get("mySourcePath").in(path);
} }
private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) { private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) {

View File

@ -21,17 +21,12 @@ package ca.uhn.fhir.jpa.dao;
*/ */
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.dstu2.resource.SearchParameter; import ca.uhn.fhir.model.dstu2.resource.SearchParameter;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.util.DatatypeUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -39,134 +34,19 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.util.*; import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry { public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry<SearchParameter> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class);
public static final int MAX_MANAGED_PARAM_COUNT = 10000;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
@Autowired
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
@Autowired @Autowired
private IFhirResourceDao<SearchParameter> mySpDao; private IFhirResourceDao<SearchParameter> mySpDao;
@Override @Override
public void forceRefresh() { public IFhirResourceDao<SearchParameter> getSearchParameterDao() {
synchronized (this) { return mySpDao;
myLastRefresh = 0;
}
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
refreshCacheIfNecessary();
return myActiveSearchParams;
}
@Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
refreshCacheIfNecessary();
return myActiveSearchParams.get(theResourceName);
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
protected void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = mySpDao.search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size > MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SearchParameter nextSp = (SearchParameter) nextResource;
JpaRuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
CodeDt nextBaseName = nextSp.getBaseElement();
String resourceType = nextBaseName.getValue();
if (isBlank(resourceType)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Map.Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParam.RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
super.populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
private JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode(); String name = theNextSp.getCode();
String description = theNextSp.getDescription(); String description = theNextSp.getDescription();
String path = theNextSp.getXpath(); String path = theNextSp.getXpath();
@ -212,9 +92,9 @@ public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry {
} }
} }
Set<String> providesMembershipInCompartments = Collections.emptySet(); Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = toStrings(theNextSp.getTarget()); Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) { if (isBlank(name) || isBlank(path)) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) { if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
return null; return null;
} }
@ -235,19 +115,8 @@ public class SearchParamRegistryDstu2 extends BaseSearchParamRegistry {
} }
List<JpaRuntimeSearchParam.Component> components = Collections.emptyList(); List<JpaRuntimeSearchParam.Component> components = Collections.emptyList();
Collection<? extends IPrimitiveType<String>> base = Arrays.asList(theNextSp.getBaseElement()); Collection<? extends IPrimitiveType<String>> base = Collections.singletonList(theNextSp.getBaseElement());
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base); return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, base);
return retVal;
}
private Set<String> toStrings(List<? extends CodeDt> theTarget) {
HashSet<String> retVal = new HashSet<String>();
for (CodeDt next : theTarget) {
if (isNotBlank(next.getValue())) {
retVal.add(next.getValue());
}
}
return retVal;
} }
} }

View File

@ -50,7 +50,9 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
private IFhirSystemDao<Bundle, Meta> mySystemDao; private IFhirSystemDao<Bundle, Meta> mySystemDao;
protected void markAffectedResources(SearchParameter theResource) { protected void markAffectedResources(SearchParameter theResource) {
markResourcesMatchingExpressionAsNeedingReindexing(theResource != null ? theResource.getExpression() : null); Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
String expression = theResource != null ? theResource.getExpression() : null;
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
} }
/** /**

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.lang3.ObjectUtils;
import org.hl7.fhir.dstu3.model.Subscription; import org.hl7.fhir.dstu3.model.Subscription;
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType; import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus; import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
@ -108,6 +109,16 @@ public class FhirResourceDaoSubscriptionDstu3 extends FhirResourceDaoDstu3<Subsc
} }
public RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) { public RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
switch (ObjectUtils.defaultIfNull(theResource.getStatus(), SubscriptionStatus.OFF)) {
case REQUESTED:
case ACTIVE:
break;
case ERROR:
case OFF:
case NULL:
return null;
}
String query = theResource.getCriteria(); String query = theResource.getCriteria();
if (isBlank(query)) { if (isBlank(query)) {
throw new UnprocessableEntityException("Subscription.criteria must be populated"); throw new UnprocessableEntityException("Subscription.criteria must be populated");
@ -144,6 +155,9 @@ public class FhirResourceDaoSubscriptionDstu3 extends FhirResourceDaoDstu3<Subsc
super.validateResourceForStorage(theResource, theEntityToSave); super.validateResourceForStorage(theResource, theEntityToSave);
RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource); RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
if (resDef == null) {
return;
}
IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass()); IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
if (dao == null) { if (dao == null) {

View File

@ -28,7 +28,7 @@ import java.util.Map.Entry;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.param.ParameterUtil;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.http.NameValuePair; import org.apache.http.NameValuePair;
@ -260,7 +260,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) { for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]);
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue); requestDetails.addParameter(nextParamEntry.getKey(), nextValue);
} }
url = url.substring(0, qIndex); url = url.substring(0, qIndex);
} }
@ -311,7 +311,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Map<BundleEntryComponent, ResourceTable> doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set<IdType> allIds, private Map<BundleEntryComponent, ResourceTable> doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set<IdType> allIds,
Map<IdType, IdType> idSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, Bundle response, IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder, List<BundleEntryComponent> theEntries) { Map<IdType, IdType> theIdSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, Bundle response, IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder, List<BundleEntryComponent> theEntries) {
Set<String> deletedResources = new HashSet<String>(); Set<String> deletedResources = new HashSet<String>();
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>(); List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>();
Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>(); Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>();
@ -379,10 +379,10 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
res.setId((String) null); res.setId((String) null);
DaoMethodOutcome outcome; DaoMethodOutcome outcome;
String matchUrl = nextReqEntry.getRequest().getIfNoneExist(); String matchUrl = nextReqEntry.getRequest().getIfNoneExist();
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
outcome = resourceDao.create(res, matchUrl, false, theRequestDetails); outcome = resourceDao.create(res, matchUrl, false, theRequestDetails);
if (nextResourceId != null) { if (nextResourceId != null) {
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
} }
entriesToProcess.put(nextRespEntry, outcome.getEntity()); entriesToProcess.put(nextRespEntry, outcome.getEntity());
if (outcome.getCreated() == false) { if (outcome.getCreated() == false) {
@ -412,7 +412,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
} else { } else {
String matchUrl = parts.getResourceType() + '?' + parts.getParams(); String matchUrl = parts.getResourceType() + '?' + parts.getParams();
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails); DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails);
List<ResourceTable> allDeleted = deleteOutcome.getDeletedEntities(); List<ResourceTable> allDeleted = deleteOutcome.getDeletedEntities();
for (ResourceTable deleted : allDeleted) { for (ResourceTable deleted : allDeleted) {
@ -453,14 +453,14 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} else { } else {
matchUrl = parts.getResourceType(); matchUrl = parts.getResourceType();
} }
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
outcome = resourceDao.update(res, matchUrl, false, theRequestDetails); outcome = resourceDao.update(res, matchUrl, false, theRequestDetails);
if (Boolean.TRUE.equals(outcome.getCreated())) { if (Boolean.TRUE.equals(outcome.getCreated())) {
conditionalRequestUrls.put(matchUrl, res.getClass()); conditionalRequestUrls.put(matchUrl, res.getClass());
} }
} }
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
entriesToProcess.put(nextRespEntry, outcome.getEntity()); entriesToProcess.put(nextRespEntry, outcome.getEntity());
break; break;
} }
@ -496,15 +496,16 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
continue; continue;
} }
// Refererences
List<IBaseReference> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class); List<IBaseReference> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class);
for (IBaseReference nextRef : allRefs) { for (IBaseReference nextRef : allRefs) {
IIdType nextId = nextRef.getReferenceElement(); IIdType nextId = nextRef.getReferenceElement();
if (!nextId.hasIdPart()) { if (!nextId.hasIdPart()) {
continue; continue;
} }
if (idSubstitutions.containsKey(nextId)) { if (theIdSubstitutions.containsKey(nextId)) {
IdType newId = idSubstitutions.get(nextId); IdType newId = theIdSubstitutions.get(nextId);
ourLog.info(" * Replacing resource ref {} with {}", nextId, newId); ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
nextRef.setReference(newId.getValue()); nextRef.setReference(newId.getValue());
} else if (nextId.getValue().startsWith("urn:")) { } else if (nextId.getValue().startsWith("urn:")) {
throw new InvalidRequestException("Unable to satisfy placeholder ID: " + nextId.getValue()); throw new InvalidRequestException("Unable to satisfy placeholder ID: " + nextId.getValue());
@ -513,6 +514,22 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
} }
// URIs
List<UriType> allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriType.class);
for (UriType nextRef : allUris) {
if (nextRef instanceof IIdType) {
continue; // No substitution on the resource ID itself!
}
IdType nextUriString = new IdType(nextRef.getValueAsString());
if (theIdSubstitutions.containsKey(nextUriString)) {
IdType newId = theIdSubstitutions.get(nextUriString);
ourLog.debug(" * Replacing resource ref {} with {}", nextUriString, newId);
nextRef.setValue(newId.getValue());
} else {
ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString);
}
}
IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity());
@ -521,13 +538,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
} }
SessionImpl session = (SessionImpl) myEntityManager.unwrap(Session.class); flushJpaSession();
int insertionCount = session.getActionQueue().numberOfInsertions();
int updateCount = session.getActionQueue().numberOfUpdates();
StopWatch sw = new StopWatch();
myEntityManager.flush();
ourLog.info("Session flush took {}ms for {} inserts and {} updates", sw.getMillis(), insertionCount, updateCount);
/* /*
* Double check we didn't allow any duplicates we shouldn't have * Double check we didn't allow any duplicates we shouldn't have
@ -546,14 +557,14 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
for (IdType next : allIds) { for (IdType next : allIds) {
IdType replacement = idSubstitutions.get(next); IdType replacement = theIdSubstitutions.get(next);
if (replacement == null) { if (replacement == null) {
continue; continue;
} }
if (replacement.equals(next)) { if (replacement.equals(next)) {
continue; continue;
} }
ourLog.info("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement); ourLog.debug("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
} }
return entriesToProcess; return entriesToProcess;
} }
@ -603,6 +614,23 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return retVal; return retVal;
} }
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {
String matchUrl = theMatchUrl;
if (isNotBlank(matchUrl)) {
for (Entry<IdType, IdType> nextSubstitutionEntry : theIdSubstitutions.entrySet()) {
IdType nextTemporaryId = nextSubstitutionEntry.getKey();
IdType nextReplacementId = nextSubstitutionEntry.getValue();
String nextTemporaryIdPart = nextTemporaryId.getIdPart();
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart);
}
}
}
return matchUrl;
}
private void populateEntryWithOperationOutcome(BaseServerResponseException caughtEx, BundleEntryComponent nextEntry) { private void populateEntryWithOperationOutcome(BaseServerResponseException caughtEx, BundleEntryComponent nextEntry) {
OperationOutcome oo = new OperationOutcome(); OperationOutcome oo = new OperationOutcome();
oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage()); oo.addIssue().setSeverity(IssueSeverity.ERROR).setDiagnostics(caughtEx.getMessage());
@ -664,27 +692,6 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName); return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName);
} }
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {
String matchUrl = theMatchUrl;
if (isNotBlank(matchUrl)) {
for (Entry<IdType, IdType> nextSubstitutionEntry : theIdSubstitutions.entrySet()) {
IdType nextTemporaryId = nextSubstitutionEntry.getKey();
IdType nextReplacementId = nextSubstitutionEntry.getValue();
String nextTemporaryIdPart = nextTemporaryId.getIdPart();
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart);
}
}
}
return matchUrl;
}
private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) { private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
super.markRequestAsProcessingSubRequest(theRequestDetails); super.markRequestAsProcessingSubRequest(theRequestDetails);
try { try {
@ -742,23 +749,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode)); return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
} }
private static class BaseServerResponseExceptionHolder
{
private BaseServerResponseException myException;
public BaseServerResponseException getException() {
return myException;
}
public void setException(BaseServerResponseException myException) {
this.myException = myException;
}
}
//@formatter:off
/** /**
* Transaction Order, per the spec: * Transaction Order, per the spec:
* *
* Process any DELETE interactions * Process any DELETE interactions
* Process any POST interactions * Process any POST interactions
* Process any PUT interactions * Process any PUT interactions
@ -859,4 +852,19 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
//@formatter:off
private static class BaseServerResponseExceptionHolder
{
private BaseServerResponseException myException;
public BaseServerResponseException getException() {
return myException;
}
public void setException(BaseServerResponseException myException) {
this.myException = myException;
}
}
} }

View File

@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
*/ */
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.trim;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.*; import java.util.*;
@ -478,8 +479,8 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
multiType = true; multiType = true;
} }
List<String> systems = new ArrayList<String>(); List<String> systems = new ArrayList<>();
List<String> codes = new ArrayList<String>(); List<String> codes = new ArrayList<>();
// String needContactPointSystem = null; // String needContactPointSystem = null;
// if (nextPath.contains(".where(system='phone')")) { // if (nextPath.contains(".where(system='phone')")) {
@ -693,11 +694,11 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
IWorkerContext worker = new org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport); IWorkerContext worker = new org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext(getContext(), myValidationSupport);
FHIRPathEngine fp = new FHIRPathEngine(worker); FHIRPathEngine fp = new FHIRPathEngine(worker);
List<Object> values = new ArrayList<Object>(); List<Object> values = new ArrayList<>();
try { try {
String[] nextPathsSplit = SPLIT.split(thePaths); String[] nextPathsSplit = SPLIT.split(thePaths);
for (String nextPath : nextPathsSplit) { for (String nextPath : nextPathsSplit) {
List<Base> allValues = fp.evaluate((Base) theResource, nextPath); List<Base> allValues = fp.evaluate((Base) theResource, trim(nextPath));
if (allValues.isEmpty() == false) { if (allValues.isEmpty() == false) {
values.addAll(allValues); values.addAll(allValues);
} }

View File

@ -20,210 +20,91 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isBlank; import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import ca.uhn.fhir.jpa.dao.BaseSearchParamRegistry;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import java.util.*;
import java.util.Map.Entry;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam; import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.jpa.util.JpaConstants;
import org.apache.commons.lang3.time.DateUtils; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import org.hl7.fhir.dstu3.model.CodeType; import ca.uhn.fhir.util.DatatypeUtil;
import org.hl7.fhir.dstu3.model.Extension; import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.SearchParameter; import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.context.RuntimeSearchParam; import java.util.ArrayList;
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum; import java.util.Collections;
import ca.uhn.fhir.jpa.dao.*; import java.util.List;
import ca.uhn.fhir.jpa.util.StopWatch; import java.util.Set;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry { import static org.apache.commons.lang3.StringUtils.isBlank;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class);
public static final int MAX_MANAGED_PARAM_COUNT = 10000;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams; public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry<SearchParameter> {
@Autowired
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
@Autowired @Autowired
private IFhirResourceDao<SearchParameter> mySpDao; private IFhirResourceDao<SearchParameter> mySpDao;
@Override @Override
public void forceRefresh() { public IFhirResourceDao<SearchParameter> getSearchParameterDao() {
synchronized (this) { return mySpDao;
myLastRefresh = 0;
}
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { protected JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
refreshCacheIfNecessary();
return myActiveSearchParams;
}
@Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
refreshCacheIfNecessary();
return myActiveSearchParams.get(theResourceName);
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
protected void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = mySpDao.search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size > MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SearchParameter nextSp = (SearchParameter) nextResource;
JpaRuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
for (org.hl7.fhir.dstu3.model.CodeType nextBaseName : nextSp.getBase()) {
String resourceType = nextBaseName.getValue();
if (isBlank(resourceType)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
super.populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
private JpaRuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode(); String name = theNextSp.getCode();
String description = theNextSp.getDescription(); String description = theNextSp.getDescription();
String path = theNextSp.getExpression(); String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null; RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParamStatusEnum status = null; RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) { switch (theNextSp.getType()) {
case COMPOSITE: case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE; paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParamStatusEnum.ACTIVE;
break; break;
case DRAFT: case DATE:
status = RuntimeSearchParamStatusEnum.DRAFT; paramType = RestSearchParameterTypeEnum.DATE;
break; break;
case RETIRED: case NUMBER:
status = RuntimeSearchParamStatusEnum.RETIRED; paramType = RestSearchParameterTypeEnum.NUMBER;
break; break;
case UNKNOWN: case QUANTITY:
status = RuntimeSearchParamStatusEnum.UNKNOWN; paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break; break;
case NULL: case NULL:
break; break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
} }
} }
Set<String> providesMembershipInCompartments = Collections.emptySet(); Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = toStrings(theNextSp.getTarget()); Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) { if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) { if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
@ -250,18 +131,7 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition())); components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition()));
} }
JpaRuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase()); return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
return retVal;
}
private Set<String> toStrings(List<CodeType> theTarget) {
HashSet<String> retVal = new HashSet<String>();
for (CodeType next : theTarget) {
if (isNotBlank(next.getValue())) {
retVal.add(next.getValue());
}
}
return retVal;
} }
} }

View File

@ -40,14 +40,13 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> { public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterR4.class);
@Autowired @Autowired
private IFhirSystemDao<Bundle, Meta> mySystemDao; private IFhirSystemDao<Bundle, Meta> mySystemDao;
protected void markAffectedResources(SearchParameter theResource) { protected void markAffectedResources(SearchParameter theResource) {
markResourcesMatchingExpressionAsNeedingReindexing(theResource != null ? theResource.getExpression() : null); Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null;
String expression = theResource != null ? theResource.getExpression() : null;
markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression);
} }
/** /**
@ -128,7 +127,6 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
theExpression = theExpression.trim(); theExpression = theExpression.trim();
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(theExpression); String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(theExpression);
String allResourceName = null;
for (String nextPath : expressionSplit) { for (String nextPath : expressionSplit) {
nextPath = nextPath.trim(); nextPath = nextPath.trim();
@ -144,14 +142,6 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4<SearchPa
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage()); throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
} }
if (allResourceName == null) {
allResourceName = resourceName;
} else {
if (!allResourceName.equals(resourceName)) {
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
}
}
} }
} // if have expression } // if have expression

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.lang3.ObjectUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.r4.model.Subscription;
@ -37,20 +38,16 @@ import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import javax.annotation.Nullable;
import java.util.Date; import java.util.Date;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4<Subscription> implements IFhirResourceDaoSubscription<Subscription> { public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4<Subscription> implements IFhirResourceDaoSubscription<Subscription> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSubscriptionR4.class);
@Autowired @Autowired
private ISubscriptionTableDao mySubscriptionTableDao; private ISubscriptionTableDao mySubscriptionTableDao;
@Autowired
private PlatformTransactionManager myTxManager;
private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) { private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) {
SubscriptionTable subscriptionEntity = new SubscriptionTable(); SubscriptionTable subscriptionEntity = new SubscriptionTable();
subscriptionEntity.setCreated(new Date()); subscriptionEntity.setCreated(new Date());
@ -108,7 +105,18 @@ public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4<Subscriptio
} }
} }
@Nullable
public RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) { public RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
switch (ObjectUtils.defaultIfNull(theResource.getStatus(), Subscription.SubscriptionStatus.OFF)) {
case REQUESTED:
case ACTIVE:
break;
case ERROR:
case OFF:
case NULL:
return null;
}
String query = theResource.getCriteria(); String query = theResource.getCriteria();
if (isBlank(query)) { if (isBlank(query)) {
throw new UnprocessableEntityException("Subscription.criteria must be populated"); throw new UnprocessableEntityException("Subscription.criteria must be populated");
@ -145,6 +153,9 @@ public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4<Subscriptio
super.validateResourceForStorage(theResource, theEntityToSave); super.validateResourceForStorage(theResource, theEntityToSave);
RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource); RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
if (resDef == null) {
return;
}
IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass()); IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
if (dao == null) { if (dao == null) {

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TagDefinition; import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails; import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
import ca.uhn.fhir.jpa.util.DeleteConflict; import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
@ -144,7 +145,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
long delay = System.currentTimeMillis() - start; long delay = System.currentTimeMillis() - start;
ourLog.info("Batch completed in {}ms", new Object[]{delay}); ourLog.info("Batch completed in {}ms", new Object[] {delay});
return resp; return resp;
} }
@ -169,9 +170,9 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
final Date updateTime = new Date(); final Date updateTime = new Date();
final Set<IdType> allIds = new LinkedHashSet<IdType>(); final Set<IdType> allIds = new LinkedHashSet<>();
final Map<IdType, IdType> idSubstitutions = new HashMap<IdType, IdType>(); final Map<IdType, IdType> idSubstitutions = new HashMap<>();
final Map<IdType, DaoMethodOutcome> idToPersistedOutcome = new HashMap<IdType, DaoMethodOutcome>(); final Map<IdType, DaoMethodOutcome> idToPersistedOutcome = new HashMap<>();
// Do all entries have a verb? // Do all entries have a verb?
for (int i = 0; i < theRequest.getEntry().size(); i++) { for (int i = 0; i < theRequest.getEntry().size(); i++) {
@ -193,8 +194,8 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
* we want the GET processing to use the final indexing state * we want the GET processing to use the final indexing state
*/ */
final Bundle response = new Bundle(); final Bundle response = new Bundle();
List<BundleEntryComponent> getEntries = new ArrayList<BundleEntryComponent>(); List<BundleEntryComponent> getEntries = new ArrayList<>();
final IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder = new IdentityHashMap<Bundle.BundleEntryComponent, Integer>(); final IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder = new IdentityHashMap<>();
for (int i = 0; i < theRequest.getEntry().size(); i++) { for (int i = 0; i < theRequest.getEntry().size(); i++) {
originalRequestOrder.put(theRequest.getEntry().get(i), i); originalRequestOrder.put(theRequest.getEntry().get(i), i);
response.addEntry(); response.addEntry();
@ -265,8 +266,8 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
paramValues.put(next.getName(), next.getValue()); paramValues.put(next.getName(), next.getValue());
} }
for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) { for (java.util.Map.Entry<String, Collection<String>> nextParamEntry : paramValues.asMap().entrySet()) {
String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); String[] nextValue = nextParamEntry.getValue().toArray(new String[ nextParamEntry.getValue().size() ]);
requestDetails.getParameters().put(nextParamEntry.getKey(), nextValue); requestDetails.addParameter(nextParamEntry.getKey(), nextValue);
} }
url = url.substring(0, qIndex); url = url.substring(0, qIndex);
} }
@ -290,7 +291,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch()); requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch());
} }
Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {}", url); Validate.isTrue(method instanceof BaseResourceReturningMethodBinding, "Unable to handle GET {0}", url);
try { try {
IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails); IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails);
if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) { if (paramValues.containsKey(Constants.PARAM_SUMMARY) || paramValues.containsKey(Constants.PARAM_CONTENT)) {
@ -309,20 +310,20 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
long delay = System.currentTimeMillis() - start; long delay = System.currentTimeMillis() - start;
ourLog.info(theActionName + " completed in {}ms", new Object[]{delay}); ourLog.info(theActionName + " completed in {}ms", new Object[] {delay});
response.setType(BundleType.TRANSACTIONRESPONSE); response.setType(BundleType.TRANSACTIONRESPONSE);
return response; return response;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Map<BundleEntryComponent, ResourceTable> doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set<IdType> allIds, private Map<BundleEntryComponent, ResourceTable> doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date theUpdateTime, Set<IdType> theAllIds,
Map<IdType, IdType> idSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, Bundle response, IdentityHashMap<BundleEntryComponent, Integer> originalRequestOrder, List<BundleEntryComponent> theEntries) { Map<IdType, IdType> theIdSubstitutions, Map<IdType, DaoMethodOutcome> theIdToPersistedOutcome, Bundle theResponse, IdentityHashMap<BundleEntryComponent, Integer> theOriginalRequestOrder, List<BundleEntryComponent> theEntries) {
Set<String> deletedResources = new HashSet<String>(); Set<String> deletedResources = new HashSet<>();
List<DeleteConflict> deleteConflicts = new ArrayList<DeleteConflict>(); List<DeleteConflict> deleteConflicts = new ArrayList<>();
Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<BundleEntryComponent, ResourceTable>(); Map<BundleEntryComponent, ResourceTable> entriesToProcess = new IdentityHashMap<>();
Set<ResourceTable> nonUpdatedEntities = new HashSet<ResourceTable>(); Set<ResourceTable> nonUpdatedEntities = new HashSet<>();
Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<String, Class<? extends IBaseResource>>(); Map<String, Class<? extends IBaseResource>> conditionalRequestUrls = new HashMap<>();
/* /*
* Loop through the request and process any entries of type * Loop through the request and process any entries of type
@ -341,7 +342,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
nextResourceId = res.getIdElement(); nextResourceId = res.getIdElement();
if (nextResourceId.hasIdPart() == false) { if (!nextResourceId.hasIdPart()) {
if (isNotBlank(nextReqEntry.getFullUrl())) { if (isNotBlank(nextReqEntry.getFullUrl())) {
nextResourceId = new IdType(nextReqEntry.getFullUrl()); nextResourceId = new IdType(nextReqEntry.getFullUrl());
} }
@ -360,12 +361,12 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
* Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness
*/ */
if (isPlaceholder(nextResourceId)) { if (isPlaceholder(nextResourceId)) {
if (!allIds.add(nextResourceId)) { if (!theAllIds.add(nextResourceId)) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId)); throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId));
} }
} else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) { } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) {
IdType nextId = nextResourceId.toUnqualifiedVersionless(); IdType nextId = nextResourceId.toUnqualifiedVersionless();
if (!allIds.add(nextId)) { if (!theAllIds.add(nextId)) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId)); throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId));
} }
} }
@ -375,7 +376,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
HTTPVerb verb = nextReqEntry.getRequest().getMethodElement().getValue(); HTTPVerb verb = nextReqEntry.getRequest().getMethodElement().getValue();
String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null; String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null;
BundleEntryComponent nextRespEntry = response.getEntry().get(originalRequestOrder.get(nextReqEntry)); BundleEntryComponent nextRespEntry = theResponse.getEntry().get(theOriginalRequestOrder.get(nextReqEntry));
switch (verb) { switch (verb) {
case POST: { case POST: {
@ -385,10 +386,10 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
res.setId((String) null); res.setId((String) null);
DaoMethodOutcome outcome; DaoMethodOutcome outcome;
String matchUrl = nextReqEntry.getRequest().getIfNoneExist(); String matchUrl = nextReqEntry.getRequest().getIfNoneExist();
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
outcome = resourceDao.create(res, matchUrl, false, theRequestDetails); outcome = resourceDao.create(res, matchUrl, false, theRequestDetails);
if (nextResourceId != null) { if (nextResourceId != null) {
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
} }
entriesToProcess.put(nextRespEntry, outcome.getEntity()); entriesToProcess.put(nextRespEntry, outcome.getEntity());
if (outcome.getCreated() == false) { if (outcome.getCreated() == false) {
@ -418,7 +419,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
} else { } else {
String matchUrl = parts.getResourceType() + '?' + parts.getParams(); String matchUrl = parts.getResourceType() + '?' + parts.getParams();
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails); DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails);
List<ResourceTable> allDeleted = deleteOutcome.getDeletedEntities(); List<ResourceTable> allDeleted = deleteOutcome.getDeletedEntities();
for (ResourceTable deleted : allDeleted) { for (ResourceTable deleted : allDeleted) {
@ -459,14 +460,14 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} else { } else {
matchUrl = parts.getResourceType(); matchUrl = parts.getResourceType();
} }
matchUrl = performIdSubstitutionsInMatchUrl(idSubstitutions, matchUrl); matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl);
outcome = resourceDao.update(res, matchUrl, false, theRequestDetails); outcome = resourceDao.update(res, matchUrl, false, theRequestDetails);
if (Boolean.TRUE.equals(outcome.getCreated())) { if (Boolean.TRUE.equals(outcome.getCreated())) {
conditionalRequestUrls.put(matchUrl, res.getClass()); conditionalRequestUrls.put(matchUrl, res.getClass());
} }
} }
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
entriesToProcess.put(nextRespEntry, outcome.getEntity()); entriesToProcess.put(nextRespEntry, outcome.getEntity());
break; break;
} }
@ -499,21 +500,22 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
*/ */
FhirTerser terser = getContext().newTerser(); FhirTerser terser = getContext().newTerser();
for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) { for (DaoMethodOutcome nextOutcome : theIdToPersistedOutcome.values()) {
IBaseResource nextResource = nextOutcome.getResource(); IBaseResource nextResource = nextOutcome.getResource();
if (nextResource == null) { if (nextResource == null) {
continue; continue;
} }
// References
List<IBaseReference> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class); List<IBaseReference> allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class);
for (IBaseReference nextRef : allRefs) { for (IBaseReference nextRef : allRefs) {
IIdType nextId = nextRef.getReferenceElement(); IIdType nextId = nextRef.getReferenceElement();
if (!nextId.hasIdPart()) { if (!nextId.hasIdPart()) {
continue; continue;
} }
if (idSubstitutions.containsKey(nextId)) { if (theIdSubstitutions.containsKey(nextId)) {
IdType newId = idSubstitutions.get(nextId); IdType newId = theIdSubstitutions.get(nextId);
ourLog.info(" * Replacing resource ref {} with {}", nextId, newId); ourLog.debug(" * Replacing resource ref {} with {}", nextId, newId);
nextRef.setReference(newId.getValue()); nextRef.setReference(newId.getValue());
} else if (nextId.getValue().startsWith("urn:")) { } else if (nextId.getValue().startsWith("urn:")) {
throw new InvalidRequestException("Unable to satisfy placeholder ID: " + nextId.getValue()); throw new InvalidRequestException("Unable to satisfy placeholder ID: " + nextId.getValue());
@ -522,15 +524,31 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
} }
// URIs
List<UriType> allUris = terser.getAllPopulatedChildElementsOfType(nextResource, UriType.class);
for (UriType nextRef : allUris) {
if (nextRef instanceof IIdType) {
continue; // No substitution on the resource ID itself!
}
IdType nextUriString = new IdType(nextRef.getValueAsString());
if (theIdSubstitutions.containsKey(nextUriString)) {
IdType newId = theIdSubstitutions.get(nextUriString);
ourLog.debug(" * Replacing resource ref {} with {}", nextUriString, newId);
nextRef.setValue(newId.getValue());
} else {
ourLog.debug(" * Reference [{}] does not exist in bundle", nextUriString);
}
}
IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity());
if (shouldUpdate) { if (shouldUpdate) {
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, false, updateTime, false, true); updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, false, theUpdateTime, false, true);
} }
} }
myEntityManager.flush(); flushJpaSession();
/* /*
* Double check we didn't allow any duplicates we shouldn't have * Double check we didn't allow any duplicates we shouldn't have
@ -548,15 +566,15 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
} }
} }
for (IdType next : allIds) { for (IdType next : theAllIds) {
IdType replacement = idSubstitutions.get(next); IdType replacement = theIdSubstitutions.get(next);
if (replacement == null) { if (replacement == null) {
continue; continue;
} }
if (replacement.equals(next)) { if (replacement.equals(next)) {
continue; continue;
} }
ourLog.info("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement); ourLog.debug("Placeholder resource ID \"{}\" was replaced with permanent ID \"{}\"", next, replacement);
} }
return entriesToProcess; return entriesToProcess;
} }
@ -601,9 +619,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class); TypedQuery<TagDefinition> q = myEntityManager.createQuery(sql, TagDefinition.class);
List<TagDefinition> tagDefinitions = q.getResultList(); List<TagDefinition> tagDefinitions = q.getResultList();
Meta retVal = toMeta(tagDefinitions); return toMeta(tagDefinitions);
return retVal;
} }
private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) { private String performIdSubstitutionsInMatchUrl(Map<IdType, IdType> theIdSubstitutions, String theMatchUrl) {

View File

@ -20,211 +20,93 @@ package ca.uhn.fhir.jpa.dao.r4;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.*;
import java.util.Map.Entry;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.SearchParameter;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum; import ca.uhn.fhir.context.RuntimeSearchParam.RuntimeSearchParamStatusEnum;
import ca.uhn.fhir.jpa.dao.*; import ca.uhn.fhir.jpa.dao.BaseSearchParamRegistry;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.util.DatatypeUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.SearchParameter;
import org.springframework.beans.factory.annotation.Autowired;
public class SearchParamRegistryR4 extends BaseSearchParamRegistry { import java.util.ArrayList;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryR4.class); import java.util.Collections;
public static final int MAX_MANAGED_PARAM_COUNT = 10000; import java.util.List;
import java.util.Set;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams; import static org.apache.commons.lang3.StringUtils.isBlank;
@Autowired public class SearchParamRegistryR4 extends BaseSearchParamRegistry<SearchParameter> {
private DaoConfig myDaoConfig;
private volatile long myLastRefresh;
@Autowired @Autowired
private IFhirResourceDao<SearchParameter> mySpDao; private IFhirResourceDao<SearchParameter> mySpDao;
@Override @Override
public void forceRefresh() { public IFhirResourceDao<SearchParameter> getSearchParameterDao() {
synchronized (this) { return mySpDao;
myLastRefresh = 0;
}
} }
@Override @Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() { protected RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
refreshCacheIfNecessary();
return myActiveSearchParams;
}
@Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
refreshCacheIfNecessary();
return myActiveSearchParams.get(theResourceName);
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
protected void refreshCacheIfNecessary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
synchronized (this) {
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
Map<String, Map<String, RuntimeSearchParam>> searchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextBuiltInEntry : getBuiltInSearchParams().entrySet()) {
for (RuntimeSearchParam nextParam : nextBuiltInEntry.getValue().values()) {
String nextResourceName = nextBuiltInEntry.getKey();
getSearchParamMap(searchParams, nextResourceName).put(nextParam.getName(), nextParam);
}
}
SearchParameterMap params = new SearchParameterMap();
params.setLoadSynchronousUpTo(MAX_MANAGED_PARAM_COUNT);
IBundleProvider allSearchParamsBp = mySpDao.search(params);
int size = allSearchParamsBp.size();
// Just in case..
if (size >= MAX_MANAGED_PARAM_COUNT) {
ourLog.warn("Unable to support >" + MAX_MANAGED_PARAM_COUNT + " search params!");
size = MAX_MANAGED_PARAM_COUNT;
}
List<IBaseResource> allSearchParams = allSearchParamsBp.getResources(0, size);
for (IBaseResource nextResource : allSearchParams) {
SearchParameter nextSp = (SearchParameter) nextResource;
RuntimeSearchParam runtimeSp = toRuntimeSp(nextSp);
if (runtimeSp == null) {
continue;
}
for (CodeType nextBaseName : nextSp.getBase()) {
String resourceType = nextBaseName.getValue();
if (isBlank(resourceType)) {
continue;
}
Map<String, RuntimeSearchParam> searchParamMap = getSearchParamMap(searchParams, resourceType);
String name = runtimeSp.getName();
if (myDaoConfig.isDefaultSearchParamsCanBeOverridden() || !searchParamMap.containsKey(name)) {
searchParamMap.put(name, runtimeSp);
}
}
}
Map<String, Map<String, RuntimeSearchParam>> activeSearchParams = new HashMap<>();
for (Entry<String, Map<String, RuntimeSearchParam>> nextEntry : searchParams.entrySet()) {
for (RuntimeSearchParam nextSp : nextEntry.getValue().values()) {
String nextName = nextSp.getName();
if (nextSp.getStatus() != RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
activeSearchParams.get(nextEntry.getKey()).remove(nextName);
}
}
}
myActiveSearchParams = activeSearchParams;
super.populateActiveSearchParams(activeSearchParams);
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
}
}
}
private RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode(); String name = theNextSp.getCode();
String description = theNextSp.getDescription(); String description = theNextSp.getDescription();
String path = theNextSp.getExpression(); String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null; RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParamStatusEnum status = null; RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) { switch (theNextSp.getType()) {
case COMPOSITE: case COMPOSITE:
paramType = RestSearchParameterTypeEnum.COMPOSITE; paramType = RestSearchParameterTypeEnum.COMPOSITE;
break;
case DATE:
paramType = RestSearchParameterTypeEnum.DATE;
break;
case NUMBER:
paramType = RestSearchParameterTypeEnum.NUMBER;
break;
case QUANTITY:
paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break;
case NULL:
break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParamStatusEnum.ACTIVE;
break; break;
case DRAFT: case DATE:
status = RuntimeSearchParamStatusEnum.DRAFT; paramType = RestSearchParameterTypeEnum.DATE;
break; break;
case RETIRED: case NUMBER:
status = RuntimeSearchParamStatusEnum.RETIRED; paramType = RestSearchParameterTypeEnum.NUMBER;
break; break;
case UNKNOWN: case QUANTITY:
status = RuntimeSearchParamStatusEnum.UNKNOWN; paramType = RestSearchParameterTypeEnum.QUANTITY;
break;
case REFERENCE:
paramType = RestSearchParameterTypeEnum.REFERENCE;
break;
case STRING:
paramType = RestSearchParameterTypeEnum.STRING;
break;
case TOKEN:
paramType = RestSearchParameterTypeEnum.TOKEN;
break;
case URI:
paramType = RestSearchParameterTypeEnum.URI;
break; break;
case NULL: case NULL:
break; break;
}
if (theNextSp.getStatus() != null) {
switch (theNextSp.getStatus()) {
case ACTIVE:
status = RuntimeSearchParamStatusEnum.ACTIVE;
break;
case DRAFT:
status = RuntimeSearchParamStatusEnum.DRAFT;
break;
case RETIRED:
status = RuntimeSearchParamStatusEnum.RETIRED;
break;
case UNKNOWN:
status = RuntimeSearchParamStatusEnum.UNKNOWN;
break;
case NULL:
break;
} }
} }
Set<String> providesMembershipInCompartments = Collections.emptySet(); Set<String> providesMembershipInCompartments = Collections.emptySet();
Set<String> targets = toStrings(theNextSp.getTarget()); Set<String> targets = DatatypeUtil.toStringSet(theNextSp.getTarget());
if (isBlank(name) || isBlank(path) || paramType == null) { if (isBlank(name) || isBlank(path) || paramType == null) {
if (paramType != RestSearchParameterTypeEnum.COMPOSITE) { if (paramType != RestSearchParameterTypeEnum.COMPOSITE) {
@ -248,21 +130,11 @@ public class SearchParamRegistryR4 extends BaseSearchParamRegistry {
List<JpaRuntimeSearchParam.Component> components = new ArrayList<>(); List<JpaRuntimeSearchParam.Component> components = new ArrayList<>();
for (org.hl7.fhir.r4.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) { for (org.hl7.fhir.r4.model.SearchParameter.SearchParameterComponentComponent next : theNextSp.getComponent()) {
components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), next.getDefinition())); components.add(new JpaRuntimeSearchParam.Component(next.getExpression(), new Reference(next.getDefinition())));
} }
RuntimeSearchParam retVal = new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase()); return new JpaRuntimeSearchParam(id, uri, name, description, path, paramType, providesMembershipInCompartments, targets, status, unique, components, theNextSp.getBase());
return retVal;
} }
private Set<String> toStrings(List<CodeType> theTarget) {
HashSet<String> retVal = new HashSet<>();
for (CodeType next : theTarget) {
if (isNotBlank(next.getValue())) {
retVal.add(next.getValue());
}
}
return retVal;
}
} }

View File

@ -36,6 +36,7 @@ public abstract class BaseHasResource {
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
private Date myDeleted; private Date myDeleted;
// TODO: move to resource history table
@Column(name = "RES_VERSION", nullable = true, length = 7) @Column(name = "RES_VERSION", nullable = true, length = 7)
@Enumerated(EnumType.STRING) @Enumerated(EnumType.STRING)
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)

View File

@ -20,24 +20,15 @@ package ca.uhn.fhir.jpa.entity;
* #L% * #L%
*/ */
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import com.sun.prism.image.Coords;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.search.annotations.Field; import org.hibernate.search.annotations.Field;
import javax.persistence.*;
//@formatter:off //@formatter:off
@Embeddable @Embeddable
@Entity @Entity

View File

@ -29,13 +29,13 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
public class ServletSubRequestDetails extends ServletRequestDetails { public class ServletSubRequestDetails extends ServletRequestDetails {
private Map<String, ArrayList<String>> myHeaders = new HashMap<String, ArrayList<String>>(); private Map<String, ArrayList<String>> myHeaders = new HashMap<>();
public void addHeader(String theName, String theValue) { public void addHeader(String theName, String theValue) {
String lowerCase = theName.toLowerCase(); String lowerCase = theName.toLowerCase();
ArrayList<String> list = myHeaders.get(lowerCase); ArrayList<String> list = myHeaders.get(lowerCase);
if (list == null) { if (list == null) {
list = new ArrayList<String>(); list = new ArrayList<>();
myHeaders.put(lowerCase, list); myHeaders.put(lowerCase, list);
} }
list.add(theValue); list.add(theValue);

View File

@ -29,7 +29,7 @@ import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao; import ca.uhn.fhir.jpa.dao.data.ISearchIncludeDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao; import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;

View File

@ -37,6 +37,7 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import java.util.Date; import java.util.Date;
@ -95,17 +96,24 @@ public class StaleSearchDeletingSvcImpl implements IStaleSearchDeletingSvc {
ourLog.debug("Searching for searches which are before {}", cutoff); ourLog.debug("Searching for searches which are before {}", cutoff);
TransactionTemplate tt = new TransactionTemplate(myTransactionManager); TransactionTemplate tt = new TransactionTemplate(myTransactionManager);
int count = tt.execute(new TransactionCallback<Integer>() { final Slice<Long> toDelete = tt.execute(new TransactionCallback<Slice<Long>>() {
@Override @Override
public Integer doInTransaction(TransactionStatus theStatus) { public Slice<Long> doInTransaction(TransactionStatus theStatus) {
Slice<Long> toDelete = mySearchDao.findWhereLastReturnedBefore(cutoff, new PageRequest(0, 1000)); return mySearchDao.findWhereLastReturnedBefore(cutoff, new PageRequest(0, 1000));
for (final Long next : toDelete) {
deleteSearch(next);
}
return toDelete.getContent().size();
} }
}); });
for (final Long nextSearchToDelete : toDelete) {
ourLog.debug("Deleting search with PID {}", nextSearchToDelete);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
deleteSearch(nextSearchToDelete);
}
});
}
int count = toDelete.getContent().size();
if (count > 0) { if (count > 0) {
long total = tt.execute(new TransactionCallback<Long>() { long total = tt.execute(new TransactionCallback<Long>() {
@Override @Override

View File

@ -21,6 +21,9 @@ package ca.uhn.fhir.jpa.subscription;
*/ */
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.r4.model.Subscription;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -29,6 +32,7 @@ import org.springframework.messaging.MessagingException;
public abstract class BaseSubscriptionDeliverySubscriber extends BaseSubscriptionSubscriber { public abstract class BaseSubscriptionDeliverySubscriber extends BaseSubscriptionSubscriber {
private static final Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionDeliverySubscriber.class); private static final Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionDeliverySubscriber.class);
private boolean myReloadResourceBeforeDelivery = true;
public BaseSubscriptionDeliverySubscriber(IFhirResourceDao<?> theSubscriptionDao, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor) { public BaseSubscriptionDeliverySubscriber(IFhirResourceDao<?> theSubscriptionDao, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor) {
super(theSubscriptionDao, theChannelType, theSubscriptionInterceptor); super(theSubscriptionDao, theChannelType, theSubscriptionInterceptor);
@ -40,24 +44,54 @@ public abstract class BaseSubscriptionDeliverySubscriber extends BaseSubscriptio
ourLog.warn("Unexpected payload type: {}", theMessage.getPayload()); ourLog.warn("Unexpected payload type: {}", theMessage.getPayload());
return; return;
} }
String subscriptionId = "(unknown?)";
try { try {
ResourceDeliveryMessage msg = (ResourceDeliveryMessage) theMessage.getPayload(); ResourceDeliveryMessage msg = (ResourceDeliveryMessage) theMessage.getPayload();
subscriptionId = msg.getPayload(getContext()).getIdElement().getValue();
if (!subscriptionTypeApplies(getContext(), msg.getSubscription().getBackingSubscription(getContext()))) { if (!subscriptionTypeApplies(getContext(), msg.getSubscription().getBackingSubscription(getContext()))) {
return; return;
} }
CanonicalSubscription updatedSubscription = (CanonicalSubscription)getSubscriptionInterceptor().getIdToSubscription().get(msg.getSubscription().getIdElement(getContext()).getIdPart()); CanonicalSubscription updatedSubscription = (CanonicalSubscription) getSubscriptionInterceptor().getIdToSubscription().get(msg.getSubscription().getIdElement(getContext()).getIdPart());
if (updatedSubscription != null) { if (updatedSubscription != null) {
msg.setSubscription(updatedSubscription); msg.setSubscription(updatedSubscription);
} }
if (myReloadResourceBeforeDelivery) {
// Reload the payload just in case any interceptors modified
// it before it was saved to the database. This is also
// useful for resources created in a transaction, since they
// can have placeholder IDs in them.
IIdType payloadId = msg.getPayloadId(getContext());
Class type = getContext().getResourceDefinition(payloadId.getResourceType()).getImplementingClass();
IFhirResourceDao dao = getSubscriptionDao().getDao(type);
IBaseResource loadedPayload;
try {
loadedPayload = dao.read(payloadId);
} catch (ResourceNotFoundException e) {
// This can happen if a last minute failure happens when saving a resource,
// eg a constraint causes the transaction to roll back on commit
ourLog.warn("Unable to find resource {} - Aborting delivery", payloadId.getValue());
return;
}
msg.setPayload(getContext(), loadedPayload);
}
handleMessage(msg); handleMessage(msg);
} catch (Exception e) { } catch (Exception e) {
ourLog.error("Failure handling subscription payload", e); String msg = "Failure handling subscription payload for subscription: " + subscriptionId;
throw new MessagingException(theMessage, "Failure handling subscription payload", e); ourLog.error(msg, e);
throw new MessagingException(theMessage, msg, e);
} }
} }
public abstract void handleMessage(ResourceDeliveryMessage theMessage) throws Exception; public abstract void handleMessage(ResourceDeliveryMessage theMessage) throws Exception;
public void setReloadResourceBeforeDelivery(boolean theReloadResourceBeforeDelivery) {
myReloadResourceBeforeDelivery = theReloadResourceBeforeDelivery;
}
} }

View File

@ -23,11 +23,12 @@ package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.config.BaseConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails; import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails;
import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -48,6 +49,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.SubscribableChannel; import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.support.ExecutorSubscribableChannel; import org.springframework.messaging.support.ExecutorSubscribableChannel;
@ -92,8 +94,11 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
@Autowired(required = false) @Autowired(required = false)
@Qualifier("myEventDefinitionDaoR4") @Qualifier("myEventDefinitionDaoR4")
private IFhirResourceDao<org.hl7.fhir.r4.model.EventDefinition> myEventDefinitionDaoR4; private IFhirResourceDao<org.hl7.fhir.r4.model.EventDefinition> myEventDefinitionDaoR4;
@Autowired @Autowired()
private PlatformTransactionManager myTxManager; private PlatformTransactionManager myTxManager;
@Autowired
@Qualifier(BaseConfig.TASK_EXECUTOR_NAME)
private AsyncTaskExecutor myAsyncTaskExecutor;
/** /**
* Constructor * Constructor
@ -364,6 +369,11 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
}); });
} }
@VisibleForTesting
public void setAsyncTaskExecutorForUnitTest(AsyncTaskExecutor theAsyncTaskExecutor) {
myAsyncTaskExecutor = theAsyncTaskExecutor;
}
public void setFhirContext(FhirContext theCtx) { public void setFhirContext(FhirContext theCtx) {
myCtx = theCtx; myCtx = theCtx;
} }
@ -455,7 +465,7 @@ public abstract class BaseSubscriptionInterceptor<S extends IBaseResource> exten
} }
if (mySubscriptionActivatingSubscriber == null) { if (mySubscriptionActivatingSubscriber == null) {
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), getChannelType(), this, myTxManager); mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), getChannelType(), this, myTxManager, myAsyncTaskExecutor);
} }
registerSubscriptionCheckingSubscriber(); registerSubscriptionCheckingSubscriber();

View File

@ -26,7 +26,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.r4.model.Subscription;
import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
public abstract class BaseSubscriptionSubscriber implements MessageHandler { public abstract class BaseSubscriptionSubscriber implements MessageHandler {
@ -60,14 +59,6 @@ public abstract class BaseSubscriptionSubscriber implements MessageHandler {
} }
/**
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
*/
protected boolean subscriptionTypeApplies(IBaseResource theSubscription) {
FhirContext ctx = mySubscriptionDao.getContext();
return subscriptionTypeApplies(ctx, theSubscription);
}
/** /**
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor? * Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
*/ */
@ -80,10 +71,12 @@ public abstract class BaseSubscriptionSubscriber implements MessageHandler {
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor? * Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
*/ */
static boolean subscriptionTypeApplies(FhirContext theCtx, IBaseResource theSubscription, Subscription.SubscriptionChannelType theChannelType) { static boolean subscriptionTypeApplies(FhirContext theCtx, IBaseResource theSubscription, Subscription.SubscriptionChannelType theChannelType) {
IPrimitiveType<?> status = theCtx.newTerser().getSingleValueOrNull(theSubscription, BaseSubscriptionInterceptor.SUBSCRIPTION_TYPE, IPrimitiveType.class); IPrimitiveType<?> subscriptionType = theCtx.newTerser().getSingleValueOrNull(theSubscription, BaseSubscriptionInterceptor.SUBSCRIPTION_TYPE, IPrimitiveType.class);
boolean subscriptionTypeApplies = false; boolean subscriptionTypeApplies = false;
if (theChannelType.toCode().equals(status.getValueAsString())) { if (subscriptionType != null) {
subscriptionTypeApplies = true; if (theChannelType.toCode().equals(subscriptionType.getValueAsString())) {
subscriptionTypeApplies = true;
}
} }
return subscriptionTypeApplies; return subscriptionTypeApplies;
} }

View File

@ -23,13 +23,19 @@ package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.SubscriptionUtil;
import com.google.common.annotations.VisibleForTesting;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.r4.model.Subscription;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.messaging.MessagingException; import org.springframework.messaging.MessagingException;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionCallbackWithoutResult;
@ -37,24 +43,33 @@ import org.springframework.transaction.support.TransactionSynchronizationAdapter
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
import java.util.Date;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class SubscriptionActivatingSubscriber { public class SubscriptionActivatingSubscriber {
private static boolean ourWaitForSubscriptionActivationSynchronouslyForUnitTest;
private final IFhirResourceDao mySubscriptionDao; private final IFhirResourceDao mySubscriptionDao;
private final BaseSubscriptionInterceptor mySubscriptionInterceptor; private final BaseSubscriptionInterceptor mySubscriptionInterceptor;
private final PlatformTransactionManager myTransactionManager; private final PlatformTransactionManager myTransactionManager;
private final AsyncTaskExecutor myTaskExecutor;
private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class); private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class);
private FhirContext myCtx; private FhirContext myCtx;
private Subscription.SubscriptionChannelType myChannelType; private Subscription.SubscriptionChannelType myChannelType;
/** /**
* Constructor * Constructor
*/ */
public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor, PlatformTransactionManager theTransactionManager) { public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor, PlatformTransactionManager theTransactionManager, AsyncTaskExecutor theTaskExecutor) {
mySubscriptionDao = theSubscriptionDao; mySubscriptionDao = theSubscriptionDao;
mySubscriptionInterceptor = theSubscriptionInterceptor; mySubscriptionInterceptor = theSubscriptionInterceptor;
myChannelType = theChannelType; myChannelType = theChannelType;
myCtx = theSubscriptionDao.getContext(); myCtx = theSubscriptionDao.getContext();
myTransactionManager = theTransactionManager; myTransactionManager = theTransactionManager;
myTaskExecutor = theTaskExecutor;
Validate.notNull(theTaskExecutor);
} }
public void activateAndRegisterSubscriptionIfRequired(final IBaseResource theSubscription) { public void activateAndRegisterSubscriptionIfRequired(final IBaseResource theSubscription) {
@ -70,14 +85,40 @@ public class SubscriptionActivatingSubscriber {
final String activeStatus = Subscription.SubscriptionStatus.ACTIVE.toCode(); final String activeStatus = Subscription.SubscriptionStatus.ACTIVE.toCode();
if (requestedStatus.equals(statusString)) { if (requestedStatus.equals(statusString)) {
if (TransactionSynchronizationManager.isSynchronizationActive()) { if (TransactionSynchronizationManager.isSynchronizationActive()) {
/*
* If we're in a transaction, we don't want to try and change the status from
* requested to active within the same transaction because it's too late by
* the time we get here to make modifications to the payload.
*
* So, we register a synchronization, meaning that when the transaction is
* finished, we'll schedule a task to do this in a separate worker thread
* to avoid any possibility of conflict.
*/
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override @Override
public void afterCommit() { public void afterCommit() {
activateSubscription(status, activeStatus, theSubscription, requestedStatus); Future<?> activationFuture = myTaskExecutor.submit(new Runnable() {
@Override
public void run() {
activateSubscription(activeStatus, theSubscription, requestedStatus);
}
});
/*
* If we're running in a unit test, it's nice to be predictable in
* terms of order... In the real world it's a recipe for deadlocks
*/
if (ourWaitForSubscriptionActivationSynchronouslyForUnitTest) {
try {
activationFuture.get(5, TimeUnit.SECONDS);
} catch (Exception e) {
ourLog.error("Failed to activate subscription", e);
}
}
} }
}); });
} else { } else {
activateSubscription(status, activeStatus, theSubscription, requestedStatus); activateSubscription(activeStatus, theSubscription, requestedStatus);
} }
} else if (activeStatus.equals(statusString)) { } else if (activeStatus.equals(statusString)) {
if (!mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement())) { if (!mySubscriptionInterceptor.hasSubscription(theSubscription.getIdElement())) {
@ -92,13 +133,21 @@ public class SubscriptionActivatingSubscriber {
} }
} }
private void activateSubscription(IPrimitiveType<?> theStatus, String theActiveStatus, IBaseResource theSubscription, String theRequestedStatus) { private void activateSubscription(String theActiveStatus, final IBaseResource theSubscription, String theRequestedStatus) {
theStatus.setValueAsString(theActiveStatus); IBaseResource subscription = mySubscriptionDao.read(theSubscription.getIdElement());
ourLog.info("Activating and registering subscription {} from status {} to {}", theSubscription.getIdElement().toUnqualified().getValue(), theRequestedStatus, theActiveStatus);
mySubscriptionDao.update(theSubscription);
mySubscriptionInterceptor.registerSubscription(theSubscription.getIdElement(), theSubscription);
}
ourLog.info("Activating and registering subscription {} from status {} to {}", subscription.getIdElement().toUnqualified().getValue(), theRequestedStatus, theActiveStatus);
try {
SubscriptionUtil.setStatus(myCtx, subscription, theActiveStatus);
mySubscriptionDao.update(subscription);
mySubscriptionInterceptor.registerSubscription(subscription.getIdElement(), subscription);
} catch (final UnprocessableEntityException e) {
ourLog.info("Changing status of {} to ERROR", subscription.getIdElement());
SubscriptionUtil.setStatus(myCtx, subscription, "error");
SubscriptionUtil.setReason(myCtx, subscription, e.getMessage());
mySubscriptionDao.update(subscription);
}
}
public void handleMessage(RestOperationTypeEnum theOperationType, IIdType theId, final IBaseResource theSubscription) throws MessagingException { public void handleMessage(RestOperationTypeEnum theOperationType, IIdType theId, final IBaseResource theSubscription) throws MessagingException {
@ -125,4 +174,9 @@ public class SubscriptionActivatingSubscriber {
} }
@VisibleForTesting
public static void setWaitForSubscriptionActivationSynchronouslyForUnitTest(boolean theWaitForSubscriptionActivationSynchronouslyForUnitTest) {
ourWaitForSubscriptionActivationSynchronouslyForUnitTest = theWaitForSubscriptionActivationSynchronouslyForUnitTest;
}
} }

View File

@ -102,25 +102,22 @@ public class SubscriptionCheckingSubscriber extends BaseSubscriptionSubscriber {
IBundleProvider results = performSearch(criteria); IBundleProvider results = performSearch(criteria);
ourLog.info("Subscription check found {} results for query: {}", results.size(), criteria); ourLog.debug("Subscription check found {} results for query: {}", results.size(), criteria);
if (results.size() == 0) { if (results.size() == 0) {
continue; continue;
} }
// should just be one resource as it was filtered by the id ourLog.debug("Found match: queueing rest-hook notification for resource: {}", id.toUnqualifiedVersionless().getValue());
for (IBaseResource nextBase : results.getResources(0, results.size())) {
ourLog.info("Found match: queueing rest-hook notification for resource: {}", nextBase.getIdElement());
ResourceDeliveryMessage deliveryMsg = new ResourceDeliveryMessage(); ResourceDeliveryMessage deliveryMsg = new ResourceDeliveryMessage();
deliveryMsg.setPayload(getContext(), nextBase); deliveryMsg.setPayload(getContext(), msg.getNewPayload(getContext()));
deliveryMsg.setSubscription(nextSubscription); deliveryMsg.setSubscription(nextSubscription);
deliveryMsg.setOperationType(msg.getOperationType()); deliveryMsg.setOperationType(msg.getOperationType());
deliveryMsg.setPayloadId(msg.getId(getContext())); deliveryMsg.setPayloadId(msg.getId(getContext()));
ResourceDeliveryJsonMessage wrappedMsg = new ResourceDeliveryJsonMessage(deliveryMsg); ResourceDeliveryJsonMessage wrappedMsg = new ResourceDeliveryJsonMessage(deliveryMsg);
getSubscriptionInterceptor().getDeliveryChannel().send(wrappedMsg); getSubscriptionInterceptor().getDeliveryChannel().send(wrappedMsg);
}
} }

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.subscription.email;
* #L% * #L%
*/ */
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -95,7 +95,6 @@ public class JavaMailEmailSender implements IEmailSender {
@Override @Override
public void send(EmailDetails theDetails) { public void send(EmailDetails theDetails) {
String subscriptionId = theDetails.getSubscription().toUnqualifiedVersionless().getValue(); String subscriptionId = theDetails.getSubscription().toUnqualifiedVersionless().getValue();
ourLog.info("Sending email for subscription {} to recipients: {}", subscriptionId, theDetails.getTo());
StopWatch sw = new StopWatch(); StopWatch sw = new StopWatch();
StringTemplateResolver templateResolver = new StringTemplateResolver(); StringTemplateResolver templateResolver = new StringTemplateResolver();
@ -116,15 +115,18 @@ public class JavaMailEmailSender implements IEmailSender {
MimeMessage email = mySender.createMimeMessage(); MimeMessage email = mySender.createMimeMessage();
String from = trim(theDetails.getFrom());
ourLog.info("Sending email for subscription {} from [{}] to recipients: [{}]", subscriptionId, from, theDetails.getTo());
try { try {
email.setFrom(trim(theDetails.getFrom())); email.setFrom(from);
email.setRecipients(Message.RecipientType.TO, toTrimmedCommaSeparatedString(theDetails.getTo())); email.setRecipients(Message.RecipientType.TO, toTrimmedCommaSeparatedString(theDetails.getTo()));
email.setSubject(subject); email.setSubject(subject);
email.setText(body); email.setText(body);
email.setSentDate(new Date()); email.setSentDate(new Date());
email.addHeader("X-FHIR-Subscription", subscriptionId); email.addHeader("X-FHIR-Subscription", subscriptionId);
} catch (MessagingException e) { } catch (MessagingException e) {
throw new InternalErrorException("Failed to create email messaage", e); throw new InternalErrorException("Failed to create email message", e);
} }
mySender.send(email); mySender.send(email);

View File

@ -47,7 +47,7 @@ import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.*; import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*; import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.ObjectUtil; import ca.uhn.fhir.util.ObjectUtil;

View File

@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;

View File

@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;

View File

@ -1,9 +1,5 @@
package ca.uhn.fhir.jpa.util; package ca.uhn.fhir.jpa.util;
import org.apache.commons.lang3.time.DateUtils;
import java.util.Date;
/* /*
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR JPA Server
@ -24,91 +20,10 @@ import java.util.Date;
* #L% * #L%
*/ */
public class StopWatch { /**
* @deprecated Use {@link ca.uhn.fhir.util.StopWatch} instead
private long myStarted = System.currentTimeMillis(); */
@Deprecated
/** public class StopWatch extends ca.uhn.fhir.util.StopWatch {
* Constructor // this just exists since existing code may depend on it
*/
public StopWatch() {
super();
}
/**
* Constructor
*/
public StopWatch(Date theNow) {
myStarted = theNow.getTime();
}
public long getMillisAndRestart() {
long now = System.currentTimeMillis();
long retVal = now - myStarted;
myStarted = now;
return retVal;
}
public long getMillis() {
long now = System.currentTimeMillis();
long retVal = now - myStarted;
return retVal;
}
public long getMillis(Date theNow) {
long retVal = theNow.getTime() - myStarted;
return retVal;
}
public Date getStartedDate() {
return new Date(myStarted);
}
/**
* Formats value in the format [DD d ]HH:mm:ss.SSSS
*/
@Override
public String toString() {
return formatMillis(getMillis());
}
static public String formatMillis(long val) {
StringBuilder buf = new StringBuilder(20);
if (val >= DateUtils.MILLIS_PER_DAY) {
long days = val / DateUtils.MILLIS_PER_DAY;
append(buf, "", 1, days);
if (days > 1) {
buf.append(" days ");
} else if (days == 1) {
buf.append(" day ");
}
append(buf, "", 2, ((val % DateUtils.MILLIS_PER_DAY) / DateUtils.MILLIS_PER_HOUR));
} else {
append(buf, "", 2, ((val % DateUtils.MILLIS_PER_DAY) / DateUtils.MILLIS_PER_HOUR));
}
append(buf, ":", 2, ((val % DateUtils.MILLIS_PER_HOUR) / DateUtils.MILLIS_PER_MINUTE));
append(buf, ":", 2, ((val % DateUtils.MILLIS_PER_MINUTE) / DateUtils.MILLIS_PER_SECOND));
append(buf, ".", 3, (val % DateUtils.MILLIS_PER_SECOND));
return buf.toString();
}
/** Append a right-aligned and zero-padded numeric value to a `StringBuilder`. */
static private void append(StringBuilder tgt, String pfx, int dgt, long val) {
tgt.append(pfx);
if (dgt > 1) {
int pad = (dgt - 1);
for (long xa = val; xa > 9 && pad > 0; xa /= 10) {
pad--;
}
for (int xa = 0; xa < pad; xa++) {
tgt.append('0');
}
}
tgt.append(val);
}
public int getMillisPerOperation(int theNumOperations) {
return (int)(((double) getMillis()) / Math.max(1.0, theNumOperations));
}
} }

View File

@ -74,7 +74,7 @@ public class SubscriptionsRequireManualActivationInterceptorDstu2 extends Server
if (newStatus == null) { if (newStatus == null) {
String actualCode = subscription.getStatusElement().getValueAsString(); String actualCode = subscription.getStatusElement().getValueAsString();
throw new UnprocessableEntityException("Can not " + theOperation.getCode() + " resource: Subscription.status must be populated" + ((isNotBlank(actualCode)) ? " (invalid value " + actualCode + ")" : "")); throw new UnprocessableEntityException("Can not " + theOperation.getCode() + " resource: Subscription.status must be populated on this server" + ((isNotBlank(actualCode)) ? " (invalid value " + actualCode + ")" : ""));
} }
if (theOldResourceOrNull != null) { if (theOldResourceOrNull != null) {
@ -108,7 +108,7 @@ public class SubscriptionsRequireManualActivationInterceptorDstu2 extends Server
} }
if (theSubscription.getStatus() == null) { if (theSubscription.getStatus() == null) {
throw new UnprocessableEntityException("Can not " + theOperation.getCode().toLowerCase() + " resource: Subscription.status must be populated"); throw new UnprocessableEntityException("Can not " + theOperation.getCode().toLowerCase() + " resource: Subscription.status must be populated on this server");
} }
throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatusEnum.OFF.getCode() + "' or '" + SubscriptionStatusEnum.REQUESTED.getCode() + "' on a newly created subscription"); throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatusEnum.OFF.getCode() + "' or '" + SubscriptionStatusEnum.REQUESTED.getCode() + "' on a newly created subscription");

View File

@ -74,7 +74,7 @@ public class SubscriptionsRequireManualActivationInterceptorDstu3 extends Server
if (newStatus == null) { if (newStatus == null) {
String actualCode = subscription.getStatusElement().getValueAsString(); String actualCode = subscription.getStatusElement().getValueAsString();
throw new UnprocessableEntityException("Can not " + theOperation.getCode() + " resource: Subscription.status must be populated" + ((isNotBlank(actualCode)) ? " (invalid value " + actualCode + ")" : "")); throw new UnprocessableEntityException("Can not " + theOperation.getCode() + " resource: Subscription.status must be populated on this server" + ((isNotBlank(actualCode)) ? " (invalid value " + actualCode + ")" : ""));
} }
if (theOldResourceOrNull != null) { if (theOldResourceOrNull != null) {
@ -108,7 +108,7 @@ public class SubscriptionsRequireManualActivationInterceptorDstu3 extends Server
} }
if (theSubscription.getStatus() == null) { if (theSubscription.getStatus() == null) {
throw new UnprocessableEntityException("Can not " + theOperation.getCode().toLowerCase() + " resource: Subscription.status must be populated"); throw new UnprocessableEntityException("Can not " + theOperation.getCode().toLowerCase() + " resource: Subscription.status must be populated on this server");
} }
throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatus.OFF.toCode() + "' or '" + SubscriptionStatus.REQUESTED.toCode() + "' on a newly created subscription"); throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatus.OFF.toCode() + "' or '" + SubscriptionStatus.REQUESTED.toCode() + "' on a newly created subscription");

View File

@ -74,7 +74,7 @@ public class SubscriptionsRequireManualActivationInterceptorR4 extends ServerOpe
if (newStatus == null) { if (newStatus == null) {
String actualCode = subscription.getStatusElement().getValueAsString(); String actualCode = subscription.getStatusElement().getValueAsString();
throw new UnprocessableEntityException("Can not " + theOperation.getCode() + " resource: Subscription.status must be populated" + ((isNotBlank(actualCode)) ? " (invalid value " + actualCode + ")" : "")); throw new UnprocessableEntityException("Can not " + theOperation.getCode() + " resource: Subscription.status must be populated on this server" + ((isNotBlank(actualCode)) ? " (invalid value " + actualCode + ")" : ""));
} }
if (theOldResourceOrNull != null) { if (theOldResourceOrNull != null) {
@ -108,7 +108,7 @@ public class SubscriptionsRequireManualActivationInterceptorR4 extends ServerOpe
} }
if (theSubscription.getStatus() == null) { if (theSubscription.getStatus() == null) {
throw new UnprocessableEntityException("Can not " + theOperation.getCode().toLowerCase() + " resource: Subscription.status must be populated"); throw new UnprocessableEntityException("Can not " + theOperation.getCode().toLowerCase() + " resource: Subscription.status must be populated on this server");
} }
throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatus.OFF.toCode() + "' or '" + SubscriptionStatus.REQUESTED.toCode() + "' on a newly created subscription"); throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatus.OFF.toCode() + "' or '" + SubscriptionStatus.REQUESTED.toCode() + "' on a newly created subscription");

View File

@ -47,7 +47,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
@Override @Override
public Connection getConnection() throws SQLException { public Connection getConnection() {
ConnectionWrapper retVal; ConnectionWrapper retVal;
try { try {
retVal = new ConnectionWrapper(super.getConnection()); retVal = new ConnectionWrapper(super.getConnection());

View File

@ -6,7 +6,7 @@ import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc; import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept; import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.util.StopWatch; import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -34,7 +34,6 @@ import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.sql.SQLException;
import java.util.*; import java.util.*;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -158,7 +157,7 @@ public abstract class BaseJpaTest {
} }
protected List<IIdType> toUnqualifiedVersionlessIds(IBundleProvider theFound) { protected List<IIdType> toUnqualifiedVersionlessIds(IBundleProvider theFound) {
List<IIdType> retVal = new ArrayList<IIdType>(); List<IIdType> retVal = new ArrayList<>();
Integer size = theFound.size(); Integer size = theFound.size();
StopWatch sw = new StopWatch(); StopWatch sw = new StopWatch();
while (size == null) { while (size == null) {
@ -171,6 +170,7 @@ public abstract class BaseJpaTest {
} catch (InterruptedException theE) { } catch (InterruptedException theE) {
//ignore //ignore
} }
size = theFound.size();
} }
ourLog.info("Found {} results", size); ourLog.info("Found {} results", size);

View File

@ -75,6 +75,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Qualifier("myDiagnosticReportDaoDstu2") @Qualifier("myDiagnosticReportDaoDstu2")
protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao; protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao;
@Autowired @Autowired
@Qualifier("myBinaryDaoDstu2")
protected IFhirResourceDao<Binary> myBinaryDao;
@Autowired
@Qualifier("myEncounterDaoDstu2") @Qualifier("myEncounterDaoDstu2")
protected IFhirResourceDao<Encounter> myEncounterDao; protected IFhirResourceDao<Encounter> myEncounterDao;
// @PersistenceContext() // @PersistenceContext()

View File

@ -19,9 +19,9 @@ import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.mockito.internal.util.collections.ListUtil; import org.mockito.internal.util.collections.ListUtil;
import org.thymeleaf.util.ListUtils;
import java.util.List; import java.util.List;
@ -53,90 +53,6 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
} }
} }
@Test
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingEnabled() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
SearchParameter memberSp = new SearchParameter();
memberSp.setCode("member");
memberSp.setBase(ResourceTypeEnum.GROUP);
memberSp.setType(SearchParamTypeEnum.REFERENCE);
memberSp.setXpath("Group.member.entity");
memberSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
memberSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(memberSp, mySrd);
SearchParameter identifierSp = new SearchParameter();
identifierSp.setCode("identifier");
identifierSp.setBase(ResourceTypeEnum.GROUP);
identifierSp.setType(SearchParamTypeEnum.TOKEN);
identifierSp.setXpath("Group.identifier");
identifierSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
identifierSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(identifierSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p = new Patient();
p.addName().addGiven("G");
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
Group g = new Group();
g.addIdentifier().setSystem("urn:foo").setValue("bar");
g.addMember().getEntity().setReference(pid);
myGroupDao.create(g);
assertThat(myResourceLinkDao.findAll(), empty());
assertThat(ListUtil.filter(myResourceIndexedSearchParamTokenDao.findAll(), new ListUtil.Filter<ResourceIndexedSearchParamToken>() {
@Override
public boolean isOut(ResourceIndexedSearchParamToken object) {
return !object.getResourceType().equals("Group") || object.isMissing();
}
}), empty());
}
@Test
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingDisabled() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
SearchParameter memberSp = new SearchParameter();
memberSp.setCode("member");
memberSp.setBase(ResourceTypeEnum.GROUP);
memberSp.setType(SearchParamTypeEnum.REFERENCE);
memberSp.setXpath("Group.member.entity");
memberSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
memberSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(memberSp, mySrd);
SearchParameter identifierSp = new SearchParameter();
identifierSp.setCode("identifier");
identifierSp.setBase(ResourceTypeEnum.GROUP);
identifierSp.setType(SearchParamTypeEnum.TOKEN);
identifierSp.setXpath("Group.identifier");
identifierSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
identifierSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(identifierSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p = new Patient();
p.addName().addGiven("G");
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
Group g = new Group();
g.addIdentifier().setSystem("urn:foo").setValue("bar");
g.addMember().getEntity().setReference(pid);
myGroupDao.create(g);
assertThat(myResourceLinkDao.findAll(), not(empty()));
assertThat(ListUtil.filter(myResourceIndexedSearchParamTokenDao.findAll(), new ListUtil.Filter<ResourceIndexedSearchParamToken>() {
@Override
public boolean isOut(ResourceIndexedSearchParamToken object) {
return !object.getResourceType().equals("Group") || object.isMissing();
}
}), not(empty()));
}
@Test @Test
public void testCreateInvalidParamInvalidResourceName() { public void testCreateInvalidParamInvalidResourceName() {
SearchParameter fooSp = new SearchParameter(); SearchParameter fooSp = new SearchParameter();
@ -154,23 +70,6 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
} }
} }
@Test
public void testCreateInvalidParamMismatchedResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.setBase(ResourceTypeEnum.PATIENT);
fooSp.setCode("foo");
fooSp.setType(SearchParamTypeEnum.TOKEN);
fooSp.setXpath("Patient.gender or Observation.code");
fooSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
fooSp.setStatus(ConformanceResourceStatusEnum.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid SearchParameter.expression value \"Observation.code\". All paths in a single SearchParameter must match the same resource type", e.getMessage());
}
}
@Test @Test
public void testCreateInvalidParamNoPath() { public void testCreateInvalidParamNoPath() {
SearchParameter fooSp = new SearchParameter(); SearchParameter fooSp = new SearchParameter();
@ -224,7 +123,7 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
} }
@Test @Test
public void testCustomReferenceParameter() throws Exception { public void testCustomReferenceParameter() {
SearchParameter sp = new SearchParameter(); SearchParameter sp = new SearchParameter();
sp.setBase(ResourceTypeEnum.PATIENT); sp.setBase(ResourceTypeEnum.PATIENT);
sp.setCode("myDoctor"); sp.setCode("myDoctor");
@ -234,6 +133,8 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
sp.setStatus(ConformanceResourceStatusEnum.ACTIVE); sp.setStatus(ConformanceResourceStatusEnum.ACTIVE);
mySearchParameterDao.create(sp); mySearchParameterDao.create(sp);
mySearchParamRegsitry.forceRefresh();
Practitioner pract = new Practitioner(); Practitioner pract = new Practitioner();
pract.setId("A"); pract.setId("A");
pract.getName().addFamily("PRACT"); pract.getName().addFamily("PRACT");
@ -312,6 +213,90 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
} }
@Test
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingDisabled() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(false);
SearchParameter memberSp = new SearchParameter();
memberSp.setCode("member");
memberSp.setBase(ResourceTypeEnum.GROUP);
memberSp.setType(SearchParamTypeEnum.REFERENCE);
memberSp.setXpath("Group.member.entity");
memberSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
memberSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(memberSp, mySrd);
SearchParameter identifierSp = new SearchParameter();
identifierSp.setCode("identifier");
identifierSp.setBase(ResourceTypeEnum.GROUP);
identifierSp.setType(SearchParamTypeEnum.TOKEN);
identifierSp.setXpath("Group.identifier");
identifierSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
identifierSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(identifierSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p = new Patient();
p.addName().addGiven("G");
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
Group g = new Group();
g.addIdentifier().setSystem("urn:foo").setValue("bar");
g.addMember().getEntity().setReference(pid);
myGroupDao.create(g);
assertThat(myResourceLinkDao.findAll(), not(empty()));
assertThat(ListUtil.filter(myResourceIndexedSearchParamTokenDao.findAll(), new ListUtil.Filter<ResourceIndexedSearchParamToken>() {
@Override
public boolean isOut(ResourceIndexedSearchParamToken object) {
return !object.getResourceType().equals("Group") || object.isMissing();
}
}), not(empty()));
}
@Test
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingEnabled() {
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
SearchParameter memberSp = new SearchParameter();
memberSp.setCode("member");
memberSp.setBase(ResourceTypeEnum.GROUP);
memberSp.setType(SearchParamTypeEnum.REFERENCE);
memberSp.setXpath("Group.member.entity");
memberSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
memberSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(memberSp, mySrd);
SearchParameter identifierSp = new SearchParameter();
identifierSp.setCode("identifier");
identifierSp.setBase(ResourceTypeEnum.GROUP);
identifierSp.setType(SearchParamTypeEnum.TOKEN);
identifierSp.setXpath("Group.identifier");
identifierSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
identifierSp.setStatus(ConformanceResourceStatusEnum.RETIRED);
mySearchParameterDao.create(identifierSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p = new Patient();
p.addName().addGiven("G");
IIdType pid = myPatientDao.create(p).getId().toUnqualifiedVersionless();
Group g = new Group();
g.addIdentifier().setSystem("urn:foo").setValue("bar");
g.addMember().getEntity().setReference(pid);
myGroupDao.create(g);
assertThat(myResourceLinkDao.findAll(), empty());
assertThat(ListUtil.filter(myResourceIndexedSearchParamTokenDao.findAll(), new ListUtil.Filter<ResourceIndexedSearchParamToken>() {
@Override
public boolean isOut(ResourceIndexedSearchParamToken object) {
return !object.getResourceType().equals("Group") || object.isMissing();
}
}), empty());
}
@Test @Test
public void testSearchForExtensionReferenceWithNonMatchingTarget() { public void testSearchForExtensionReferenceWithNonMatchingTarget() {
SearchParameter siblingSp = new SearchParameter(); SearchParameter siblingSp = new SearchParameter();
@ -883,6 +868,53 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
} }
@Test
@Ignore
public void testSearchForStringOnIdentifierWithSpecificSystem() {
SearchParameter fooSp = new SearchParameter();
fooSp.setBase(ResourceTypeEnum.PATIENT);
fooSp.setCode("foo");
fooSp.setType(SearchParamTypeEnum.STRING);
fooSp.setXpath("Patient.identifier.where(system = 'http://AAA').value");
fooSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
fooSp.setStatus(ConformanceResourceStatusEnum.ACTIVE);
IIdType spId = mySearchParameterDao.create(fooSp, mySrd).getId().toUnqualifiedVersionless();
mySearchParamRegsitry.forceRefresh();
Patient pat = new Patient();
pat.addIdentifier().setSystem("http://AAA").setValue("BAR678");
pat.setGender(AdministrativeGenderEnum.MALE);
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
Patient pat2 = new Patient();
pat2.addIdentifier().setSystem("http://BBB").setValue("BAR678");
pat2.setGender(AdministrativeGenderEnum.FEMALE);
myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map;
IBundleProvider results;
List<String> foundResources;
// Partial match
map = new SearchParameterMap();
map.add("foo", new StringParam("bar"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(patId.getValue()));
// Non match
map = new SearchParameterMap();
map.add("foo", new StringParam("zzz"));
results = myPatientDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, empty());
}
@Test @Test
public void testSearchWithCustomParam() { public void testSearchWithCustomParam() {

Some files were not shown because too many files have changed in this diff Show More