mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-09 14:33:32 +00:00
Merge branch 'master' into ja_20190928_rationalize_search_param_extractor
This commit is contained in:
commit
817f9ae36e
@ -28,7 +28,12 @@ jobs:
|
|||||||
mavenOptions: '-Xmx2048m $(MAVEN_OPTS) -Dorg.slf4j.simpleLogger.showDateTime=true -Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss,SSS -Duser.timezone=America/Toronto'
|
mavenOptions: '-Xmx2048m $(MAVEN_OPTS) -Dorg.slf4j.simpleLogger.showDateTime=true -Dorg.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss,SSS -Duser.timezone=America/Toronto'
|
||||||
- script: bash <(curl https://codecov.io/bash) -t $(CODECOV_TOKEN)
|
- script: bash <(curl https://codecov.io/bash) -t $(CODECOV_TOKEN)
|
||||||
displayName: 'codecov'
|
displayName: 'codecov'
|
||||||
|
- task: PublishCodeCoverageResults@1
|
||||||
|
inputs:
|
||||||
|
codeCoverageTool: 'JaCoCo'
|
||||||
|
summaryFileLocation: $(System.DefaultWorkingDirectory)/hapi-fhir-jacoco/target/site/jacoco-report/jacoco.xml
|
||||||
|
reportDirectory: $(System.DefaultWorkingDirectory)/hapi-fhir-jacoco/target/site/jacoco-report/
|
||||||
|
failIfCoverageEmpty: false
|
||||||
|
|
||||||
# Potential Additional Maven3 Options:
|
# Potential Additional Maven3 Options:
|
||||||
#publishJUnitResults: true
|
#publishJUnitResults: true
|
||||||
@ -53,3 +58,5 @@ jobs:
|
|||||||
#checkStyleRunAnalysis: false # Optional
|
#checkStyleRunAnalysis: false # Optional
|
||||||
#pmdRunAnalysis: false # Optional
|
#pmdRunAnalysis: false # Optional
|
||||||
#findBugsRunAnalysis: false # Optional
|
#findBugsRunAnalysis: false # Optional
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package example;
|
package example;
|
||||||
|
|
||||||
|
import org.hl7.fhir.converter.NullVersionConverterAdvisor30;
|
||||||
import org.hl7.fhir.convertors.*;
|
import org.hl7.fhir.convertors.*;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
package example;
|
package example;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
|
import org.hl7.fhir.r4.model.InstantType;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
|
||||||
import ca.uhn.fhir.model.api.Tag;
|
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
|
||||||
|
|
||||||
public class ServerMetadataExamples {
|
public class ServerMetadataExamples {
|
||||||
|
|
||||||
// START SNIPPET: serverMethod
|
// START SNIPPET: serverMethod
|
||||||
@ -21,33 +19,20 @@ public class ServerMetadataExamples {
|
|||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
retVal.add(patient);
|
retVal.add(patient);
|
||||||
patient.setId("Patient/123");
|
patient.setId("Patient/123");
|
||||||
patient.addName().addFamily("Smith").addGiven("John");
|
patient.addName().setFamily("Smith").addGiven("John");
|
||||||
|
|
||||||
// Create a tag list and add it to the resource
|
// Add tags
|
||||||
TagList tags = new TagList();
|
patient.getMeta().addTag()
|
||||||
tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag1.html", "Some tag");
|
.setSystem(Tag.HL7_ORG_FHIR_TAG)
|
||||||
tags.addTag(Tag.HL7_ORG_FHIR_TAG, "http://foo/tag2.html", "Another tag");
|
.setCode("some_tag")
|
||||||
ResourceMetadataKeyEnum.TAG_LIST.put(patient, tags);
|
.setDisplay("Some tag");
|
||||||
|
patient.getMeta().addTag()
|
||||||
|
.setSystem(Tag.HL7_ORG_FHIR_TAG)
|
||||||
|
.setCode("another_tag")
|
||||||
|
.setDisplay("Another tag");
|
||||||
|
|
||||||
// Set some links (these can be provided as relative links or absolute)
|
// Set the last updated date
|
||||||
// and the server will convert to absolute as appropriate
|
patient.getMeta().setLastUpdatedElement(new InstantType("2011-02-22T11:22:00.0122Z"));
|
||||||
String linkAlternate = "Patient/7736";
|
|
||||||
ResourceMetadataKeyEnum.LINK_ALTERNATE.put(patient, linkAlternate);
|
|
||||||
String linkSearch = "Patient?name=smith&name=john";
|
|
||||||
ResourceMetadataKeyEnum.LINK_SEARCH.put(patient, linkSearch);
|
|
||||||
|
|
||||||
// Set the published and updated dates
|
|
||||||
InstantDt pubDate = new InstantDt("2011-02-22");
|
|
||||||
ResourceMetadataKeyEnum.PUBLISHED.put(patient, pubDate);
|
|
||||||
InstantDt updatedDate = new InstantDt("2014-07-12T11:22:27Z");
|
|
||||||
ResourceMetadataKeyEnum.UPDATED.put(patient, updatedDate);
|
|
||||||
|
|
||||||
// Set the resource title (note that if you are using HAPI's narrative
|
|
||||||
// generation capability, the narrative generator will often create
|
|
||||||
// useful titles automatically, and the server will create a default
|
|
||||||
// title if none is provided)
|
|
||||||
String title = "Patient John SMITH";
|
|
||||||
ResourceMetadataKeyEnum.TITLE.put(patient, title);
|
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,12 @@ public class ServerOperations {
|
|||||||
public void manualInputAndOutput(HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws IOException {
|
public void manualInputAndOutput(HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws IOException {
|
||||||
String contentType = theServletRequest.getContentType();
|
String contentType = theServletRequest.getContentType();
|
||||||
byte[] bytes = IOUtils.toByteArray(theServletRequest.getInputStream());
|
byte[] bytes = IOUtils.toByteArray(theServletRequest.getInputStream());
|
||||||
|
|
||||||
ourLog.info("Received call with content type {} and {} bytes", contentType, bytes.length);
|
ourLog.info("Received call with content type {} and {} bytes", contentType, bytes.length);
|
||||||
|
|
||||||
|
// In a real example we might do something more interesting with the received bytes,
|
||||||
|
// here we'll just replace them with hardcoded ones
|
||||||
|
bytes = new byte[] { 0, 1, 2, 3 };
|
||||||
|
|
||||||
theServletResponse.setContentType(contentType);
|
theServletResponse.setContentType(contentType);
|
||||||
theServletResponse.getOutputStream().write(bytes);
|
theServletResponse.getOutputStream().write(bytes);
|
||||||
theServletResponse.getOutputStream().close();
|
theServletResponse.getOutputStream().close();
|
||||||
|
@ -265,7 +265,7 @@ public class ValidatorExamples {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSet) {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -100,10 +100,6 @@
|
|||||||
<groupId>com.google.guava</groupId>
|
<groupId>com.google.guava</groupId>
|
||||||
<artifactId>guava</artifactId>
|
<artifactId>guava</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
|
||||||
<artifactId>jetty-server</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- Logging -->
|
<!-- Logging -->
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -120,6 +116,11 @@
|
|||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
@ -20,25 +20,25 @@ package ca.uhn.fhir.context;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.util.*;
|
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
||||||
|
|
||||||
private static final Class<Void> VOID_CLASS = Void.class;
|
private static final Class<Void> VOID_CLASS = Void.class;
|
||||||
|
private final Class<? extends T> myImplementingClass;
|
||||||
|
private final String myName;
|
||||||
|
private final boolean myStandardType;
|
||||||
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<Class<?>, Constructor<T>>());
|
private Map<Class<?>, Constructor<T>> myConstructors = Collections.synchronizedMap(new HashMap<Class<?>, Constructor<T>>());
|
||||||
private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
private List<RuntimeChildDeclaredExtensionDefinition> myExtensions = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
||||||
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
||||||
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
private List<RuntimeChildDeclaredExtensionDefinition> myExtensionsNonModifier = new ArrayList<RuntimeChildDeclaredExtensionDefinition>();
|
||||||
private final Class<? extends T> myImplementingClass;
|
|
||||||
private final String myName;
|
|
||||||
private final boolean myStandardType;
|
|
||||||
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
|
private Map<String, RuntimeChildDeclaredExtensionDefinition> myUrlToExtension = new HashMap<String, RuntimeChildDeclaredExtensionDefinition>();
|
||||||
|
private BaseRuntimeElementDefinition<?> myRootParentDefinition;
|
||||||
|
|
||||||
public BaseRuntimeElementDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) {
|
public BaseRuntimeElementDefinition(String theName, Class<? extends T> theImplementingClass, boolean theStandardType) {
|
||||||
assert StringUtils.isNotBlank(theName);
|
assert StringUtils.isNotBlank(theName);
|
||||||
@ -168,9 +168,14 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BaseRuntimeElementDefinition<?> getRootParentDefinition() {
|
||||||
|
return myRootParentDefinition;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked prior to use to perform any initialization and make object
|
* Invoked prior to use to perform any initialization and make object
|
||||||
* mutable.
|
* mutable.
|
||||||
|
*
|
||||||
* @param theContext TODO
|
* @param theContext TODO
|
||||||
*/
|
*/
|
||||||
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
void sealAndInitialize(FhirContext theContext, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theClassToElementDefinitions) {
|
||||||
@ -193,6 +198,16 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
myExtensions = Collections.unmodifiableList(myExtensions);
|
myExtensions = Collections.unmodifiableList(myExtensions);
|
||||||
|
|
||||||
|
Class parent = myImplementingClass;
|
||||||
|
do {
|
||||||
|
BaseRuntimeElementDefinition<?> parentDefinition = theClassToElementDefinitions.get(parent);
|
||||||
|
if (parentDefinition != null) {
|
||||||
|
myRootParentDefinition = parentDefinition;
|
||||||
|
}
|
||||||
|
parent = parent.getSuperclass();
|
||||||
|
} while (!parent.equals(Object.class));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -210,15 +225,18 @@ public abstract class BaseRuntimeElementDefinition<T extends IBase> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum ChildTypeEnum {
|
public enum ChildTypeEnum {
|
||||||
COMPOSITE_DATATYPE, /**
|
COMPOSITE_DATATYPE,
|
||||||
|
/**
|
||||||
* HL7.org structure style.
|
* HL7.org structure style.
|
||||||
*/
|
*/
|
||||||
CONTAINED_RESOURCE_LIST, /**
|
CONTAINED_RESOURCE_LIST,
|
||||||
|
/**
|
||||||
* HAPI structure style.
|
* HAPI structure style.
|
||||||
*/
|
*/
|
||||||
CONTAINED_RESOURCES, EXTENSION_DECLARED,
|
CONTAINED_RESOURCES, EXTENSION_DECLARED,
|
||||||
ID_DATATYPE,
|
ID_DATATYPE,
|
||||||
PRIMITIVE_DATATYPE, /**
|
PRIMITIVE_DATATYPE,
|
||||||
|
/**
|
||||||
* HAPI style.
|
* HAPI style.
|
||||||
*/
|
*/
|
||||||
PRIMITIVE_XHTML,
|
PRIMITIVE_XHTML,
|
||||||
|
@ -20,6 +20,7 @@ import ca.uhn.fhir.util.ReflectionUtil;
|
|||||||
import ca.uhn.fhir.util.VersionUtil;
|
import ca.uhn.fhir.util.VersionUtil;
|
||||||
import ca.uhn.fhir.validation.FhirValidator;
|
import ca.uhn.fhir.validation.FhirValidator;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.jena.riot.Lang;
|
import org.apache.jena.riot.Lang;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
@ -898,6 +899,11 @@ public class FhirContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "FhirContext[" + myVersion.getVersion().name() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2}
|
* Creates and returns a new FhirContext with version {@link FhirVersionEnum#DSTU2 DSTU2}
|
||||||
*/
|
*/
|
||||||
|
@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
|||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
import org.hl7.fhir.instance.model.api.*;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
@ -44,56 +45,47 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||||||
class ModelScanner {
|
class ModelScanner {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ModelScanner.class);
|
||||||
|
|
||||||
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions = new HashMap<Class<? extends IBase>, BaseRuntimeElementDefinition<?>>();
|
private Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> myClassToElementDefinitions = new HashMap<>();
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
private Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = new HashMap<String, RuntimeResourceDefinition>();
|
private Map<String, RuntimeResourceDefinition> myIdToResourceDefinition = new HashMap<>();
|
||||||
private Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinitions = new HashMap<String, BaseRuntimeElementDefinition<?>>();
|
private Map<String, BaseRuntimeElementDefinition<?>> myNameToElementDefinitions = new HashMap<>();
|
||||||
private Map<String, RuntimeResourceDefinition> myNameToResourceDefinitions = new HashMap<String, RuntimeResourceDefinition>();
|
private Map<String, RuntimeResourceDefinition> myNameToResourceDefinitions = new HashMap<>();
|
||||||
private Map<String, Class<? extends IBaseResource>> myNameToResourceType = new HashMap<String, Class<? extends IBaseResource>>();
|
private Map<String, Class<? extends IBaseResource>> myNameToResourceType = new HashMap<>();
|
||||||
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
private RuntimeChildUndeclaredExtensionDefinition myRuntimeChildUndeclaredExtensionDefinition;
|
||||||
private Set<Class<? extends IBase>> myScanAlso = new HashSet<Class<? extends IBase>>();
|
private Set<Class<? extends IBase>> myScanAlso = new HashSet<>();
|
||||||
private FhirVersionEnum myVersion;
|
private FhirVersionEnum myVersion;
|
||||||
|
|
||||||
private Set<Class<? extends IBase>> myVersionTypes;
|
private Set<Class<? extends IBase>> myVersionTypes;
|
||||||
|
|
||||||
ModelScanner(FhirContext theContext, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions,
|
ModelScanner(FhirContext theContext, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingDefinitions,
|
||||||
Collection<Class<? extends IBase>> theResourceTypes) throws ConfigurationException {
|
@Nonnull Collection<Class<? extends IBase>> theResourceTypes) throws ConfigurationException {
|
||||||
myContext = theContext;
|
myContext = theContext;
|
||||||
myVersion = theVersion;
|
myVersion = theVersion;
|
||||||
Set<Class<? extends IBase>> toScan;
|
Set<Class<? extends IBase>> toScan = new HashSet<>(theResourceTypes);
|
||||||
if (theResourceTypes != null) {
|
|
||||||
toScan = new HashSet<Class<? extends IBase>>(theResourceTypes);
|
|
||||||
} else {
|
|
||||||
toScan = new HashSet<Class<? extends IBase>>();
|
|
||||||
}
|
|
||||||
init(theExistingDefinitions, toScan);
|
init(theExistingDefinitions, toScan);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
|
Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> getClassToElementDefinitions() {
|
||||||
return myClassToElementDefinitions;
|
return myClassToElementDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {
|
Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {
|
||||||
return myIdToResourceDefinition;
|
return myIdToResourceDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, BaseRuntimeElementDefinition<?>> getNameToElementDefinitions() {
|
Map<String, BaseRuntimeElementDefinition<?>> getNameToElementDefinitions() {
|
||||||
return myNameToElementDefinitions;
|
return myNameToElementDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinition() {
|
Map<String, RuntimeResourceDefinition> getNameToResourceDefinition() {
|
||||||
return myNameToResourceDefinitions;
|
return myNameToResourceDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, RuntimeResourceDefinition> getNameToResourceDefinitions() {
|
Map<String, Class<? extends IBaseResource>> getNameToResourceType() {
|
||||||
return (myNameToResourceDefinitions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Class<? extends IBaseResource>> getNameToResourceType() {
|
|
||||||
return myNameToResourceType;
|
return myNameToResourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
|
RuntimeChildUndeclaredExtensionDefinition getRuntimeChildUndeclaredExtensionDefinition() {
|
||||||
return myRuntimeChildUndeclaredExtensionDefinition;
|
return myRuntimeChildUndeclaredExtensionDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,11 +137,10 @@ class ModelScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isStandardType(Class<? extends IBase> theClass) {
|
private boolean isStandardType(Class<? extends IBase> theClass) {
|
||||||
boolean retVal = myVersionTypes.contains(theClass);
|
return myVersionTypes.contains(theClass);
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scan(Class<? extends IBase> theClass) throws ConfigurationException {
|
void scan(Class<? extends IBase> theClass) throws ConfigurationException {
|
||||||
BaseRuntimeElementDefinition<?> existingDef = myClassToElementDefinitions.get(theClass);
|
BaseRuntimeElementDefinition<?> existingDef = myClassToElementDefinitions.get(theClass);
|
||||||
if (existingDef != null) {
|
if (existingDef != null) {
|
||||||
return;
|
return;
|
||||||
@ -204,9 +195,6 @@ class ModelScanner {
|
|||||||
ourLog.debug("Scanning resource block class: {}", theClass.getName());
|
ourLog.debug("Scanning resource block class: {}", theClass.getName());
|
||||||
|
|
||||||
String resourceName = theClass.getCanonicalName();
|
String resourceName = theClass.getCanonicalName();
|
||||||
if (isBlank(resourceName)) {
|
|
||||||
throw new ConfigurationException("Block type @" + Block.class.getSimpleName() + " annotation contains no name: " + theClass.getCanonicalName());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just in case someone messes up when upgrading from DSTU2
|
// Just in case someone messes up when upgrading from DSTU2
|
||||||
if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
if (myContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||||
@ -341,14 +329,14 @@ class ModelScanner {
|
|||||||
|
|
||||||
private void scanResourceForSearchParams(Class<? extends IBaseResource> theClass, RuntimeResourceDefinition theResourceDef) {
|
private void scanResourceForSearchParams(Class<? extends IBaseResource> theClass, RuntimeResourceDefinition theResourceDef) {
|
||||||
|
|
||||||
Map<String, RuntimeSearchParam> nameToParam = new HashMap<String, RuntimeSearchParam>();
|
Map<String, RuntimeSearchParam> nameToParam = new HashMap<>();
|
||||||
Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<Field, SearchParamDefinition>();
|
Map<Field, SearchParamDefinition> compositeFields = new LinkedHashMap<>();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure we pick up fields in interfaces too.. This ensures that we
|
* Make sure we pick up fields in interfaces too.. This ensures that we
|
||||||
* grab the _id field which generally gets picked up via interface
|
* grab the _id field which generally gets picked up via interface
|
||||||
*/
|
*/
|
||||||
Set<Field> fields = new HashSet<Field>(Arrays.asList(theClass.getFields()));
|
Set<Field> fields = new HashSet<>(Arrays.asList(theClass.getFields()));
|
||||||
Class<?> nextClass = theClass;
|
Class<?> nextClass = theClass;
|
||||||
do {
|
do {
|
||||||
for (Class<?> nextInterface : nextClass.getInterfaces()) {
|
for (Class<?> nextInterface : nextClass.getInterfaces()) {
|
||||||
@ -400,12 +388,12 @@ class ModelScanner {
|
|||||||
for (Entry<Field, SearchParamDefinition> nextEntry : compositeFields.entrySet()) {
|
for (Entry<Field, SearchParamDefinition> nextEntry : compositeFields.entrySet()) {
|
||||||
SearchParamDefinition searchParam = nextEntry.getValue();
|
SearchParamDefinition searchParam = nextEntry.getValue();
|
||||||
|
|
||||||
List<RuntimeSearchParam> compositeOf = new ArrayList<RuntimeSearchParam>();
|
List<RuntimeSearchParam> compositeOf = new ArrayList<>();
|
||||||
for (String nextName : searchParam.compositeOf()) {
|
for (String nextName : searchParam.compositeOf()) {
|
||||||
RuntimeSearchParam param = nameToParam.get(nextName);
|
RuntimeSearchParam param = nameToParam.get(nextName);
|
||||||
if (param == null) {
|
if (param == null) {
|
||||||
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parametr name itself. Valid values are: {}",
|
ourLog.warn("Search parameter {}.{} declares that it is a composite with compositeOf value '{}' but that is not a valid parametr name itself. Valid values are: {}",
|
||||||
new Object[]{theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet()});
|
theResourceDef.getName(), searchParam.name(), nextName, nameToParam.keySet());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
compositeOf.add(param);
|
compositeOf.add(param);
|
||||||
@ -417,7 +405,7 @@ class ModelScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> toTargetList(Class<? extends IBaseResource>[] theTarget) {
|
private Set<String> toTargetList(Class<? extends IBaseResource>[] theTarget) {
|
||||||
HashSet<String> retVal = new HashSet<String>();
|
HashSet<String> retVal = new HashSet<>();
|
||||||
|
|
||||||
for (Class<? extends IBaseResource> nextType : theTarget) {
|
for (Class<? extends IBaseResource> nextType : theTarget) {
|
||||||
ResourceDef resourceDef = nextType.getAnnotation(ResourceDef.class);
|
ResourceDef resourceDef = nextType.getAnnotation(ResourceDef.class);
|
||||||
@ -486,7 +474,7 @@ class ModelScanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Set<Class<? extends IBase>> scanVersionPropertyFile(Set<Class<? extends IBase>> theDatatypes, Map<String, Class<? extends IBaseResource>> theResourceTypes, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingElementDefinitions) {
|
static Set<Class<? extends IBase>> scanVersionPropertyFile(Set<Class<? extends IBase>> theDatatypes, Map<String, Class<? extends IBaseResource>> theResourceTypes, FhirVersionEnum theVersion, Map<Class<? extends IBase>, BaseRuntimeElementDefinition<?>> theExistingElementDefinitions) {
|
||||||
Set<Class<? extends IBase>> retVal = new HashSet<Class<? extends IBase>>();
|
Set<Class<? extends IBase>> retVal = new HashSet<>();
|
||||||
|
|
||||||
try (InputStream str = theVersion.getVersionImplementation().getFhirVersionPropertiesFile()) {
|
try (InputStream str = theVersion.getVersionImplementation().getFhirVersionPropertiesFile()) {
|
||||||
Properties prop = new Properties();
|
Properties prop = new Properties();
|
||||||
|
@ -1,100 +0,0 @@
|
|||||||
package ca.uhn.fhir.context;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2019 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.instance.model.api.IBaseResource;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.annotation.ProvidesResources;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scans a class tagged with {@code ProvidesResources} and adds any resources listed to its FhirContext's resource
|
|
||||||
* definition list. This makes the profile generator find the classes.
|
|
||||||
*
|
|
||||||
* @see ca.uhn.fhir.model.api.annotation.ProvidesResources
|
|
||||||
*/
|
|
||||||
public class ProvidedResourceScanner {
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ProvidedResourceScanner.class);
|
|
||||||
private FhirContext myContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param theContext
|
|
||||||
* - context whose resource definition list is to be updated by the scanner
|
|
||||||
*/
|
|
||||||
public ProvidedResourceScanner(FhirContext theContext) {
|
|
||||||
myContext = theContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If {@code theProvider} is tagged with the {@code ProvidesResources} annotation, this method will add every
|
|
||||||
* resource listed by the {@code resources} method.
|
|
||||||
* <p>
|
|
||||||
* Notes:
|
|
||||||
* </p>
|
|
||||||
* <ul>
|
|
||||||
* <li>if {@code theProvider} isn't annotated with {@code resources} nothing is done; it's expected that most
|
|
||||||
* RestfulServers and ResourceProviders won't be annotated.</li>
|
|
||||||
* <li>any object listed in {@code resources} that doesn't implement {@code IResource} will generate a warning in the
|
|
||||||
* log.</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param theProvider
|
|
||||||
* - Normally, either a {@link ca.uhn.fhir.rest.server.RestfulServer} or a
|
|
||||||
* {@link ca.uhn.fhir.rest.server.IResourceProvider} that might be annotated with
|
|
||||||
* {@link ca.uhn.fhir.model.api.annotation.ProvidesResources}
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public void scanForProvidedResources(Object theProvider) {
|
|
||||||
ProvidesResources annotation = theProvider.getClass().getAnnotation(ProvidesResources.class);
|
|
||||||
if (annotation == null)
|
|
||||||
return;
|
|
||||||
for (Class<?> clazz : annotation.resources()) {
|
|
||||||
if (IBaseResource.class.isAssignableFrom(clazz)) {
|
|
||||||
myContext.getResourceDefinition((Class<? extends IBaseResource>) clazz);
|
|
||||||
} else {
|
|
||||||
ourLog.warn(clazz.getSimpleName() + "is not assignable from IResource");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove any metadata that was added by any {@code ProvidesResources} annotation
|
|
||||||
* present in {@code theProvider}. This method is callled from {@code RestfulService}
|
|
||||||
* when it is unregistering a Resource Provider.
|
|
||||||
*
|
|
||||||
* @param theProvider
|
|
||||||
* - Normally a {@link ca.uhn.fhir.rest.server.IResourceProvider} that might
|
|
||||||
* be annotated with {@link ca.uhn.fhir.model.api.annotation.ProvidesResources}
|
|
||||||
*/
|
|
||||||
public void removeProvidedResources(Object theProvider) {
|
|
||||||
ProvidesResources annotation = theProvider.getClass().getAnnotation(ProvidesResources.class);
|
|
||||||
if (annotation == null)
|
|
||||||
return;
|
|
||||||
for (Class<?> clazz : annotation.resources()) {
|
|
||||||
if (IBaseResource.class.isAssignableFrom(clazz)) {
|
|
||||||
// TODO -- not currently used but should be finished for completeness
|
|
||||||
} else {
|
|
||||||
ourLog.warn(clazz.getSimpleName() + "is not assignable from IResource");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -102,6 +102,11 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||||||
*/
|
*/
|
||||||
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
boolean isCodeSystemSupported(FhirContext theContext, String theSystem);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the given ValueSet by URL
|
||||||
|
*/
|
||||||
|
IBaseResource fetchValueSet(FhirContext theContext, String theValueSetUrl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates that the given code exists and if possible returns a display
|
* Validates that the given code exists and if possible returns a display
|
||||||
* name. This method is called to check codes which are found in "example"
|
* name. This method is called to check codes which are found in "example"
|
||||||
@ -112,7 +117,7 @@ public interface IContextValidationSupport<EVS_IN, EVS_OUT, SDT, CST, CDCT, IST>
|
|||||||
* @param theDisplay The display name, if it should also be validated
|
* @param theDisplay The display name, if it should also be validated
|
||||||
* @return Returns a validation result object
|
* @return Returns a validation result object
|
||||||
*/
|
*/
|
||||||
CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay);
|
CodeValidationResult<CDCT, IST> validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look up a code using the system and code value
|
* Look up a code using the system and code value
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
package ca.uhn.fhir.model.api;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2019 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.primitive.IdDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
|
||||||
import ca.uhn.fhir.util.ElementUtil;
|
|
||||||
|
|
||||||
public class BaseBundle extends BaseElement /*implements IElement*/ {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 3349586533271409727L;
|
|
||||||
private StringDt myAuthorName;
|
|
||||||
private StringDt myAuthorUri;
|
|
||||||
private IdDt myId;
|
|
||||||
|
|
||||||
public StringDt getAuthorName() {
|
|
||||||
if (myAuthorName == null) {
|
|
||||||
myAuthorName = new StringDt();
|
|
||||||
}
|
|
||||||
return myAuthorName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringDt getAuthorUri() {
|
|
||||||
if (myAuthorUri == null) {
|
|
||||||
myAuthorUri = new StringDt();
|
|
||||||
}
|
|
||||||
return myAuthorUri;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IdDt getId() {
|
|
||||||
if (myId==null) {
|
|
||||||
myId=new IdDt();
|
|
||||||
}
|
|
||||||
return myId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return ElementUtil.isEmpty(myAuthorName, myAuthorUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(IdDt theId) {
|
|
||||||
myId = theId;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -40,7 +40,7 @@ public interface IQueryParameterAnd<T extends IQueryParameterOr<?>> extends Seri
|
|||||||
* @param theContext TODO
|
* @param theContext TODO
|
||||||
* @param theParamName TODO
|
* @param theParamName TODO
|
||||||
*/
|
*/
|
||||||
public void setValuesAsQueryTokens(FhirContext theContext, String theParamName, List<QualifiedParamList> theParameters) throws InvalidRequestException;
|
void setValuesAsQueryTokens(FhirContext theContext, String theParamName, List<QualifiedParamList> theParameters) throws InvalidRequestException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -50,7 +50,7 @@ public interface IQueryParameterAnd<T extends IQueryParameterOr<?>> extends Seri
|
|||||||
* for information on the <b>token</b> format
|
* for information on the <b>token</b> format
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public List<T> getValuesAsQueryTokens();
|
List<T> getValuesAsQueryTokens();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ package 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.primitive.DecimalDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
|
||||||
@ -29,7 +28,6 @@ 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.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
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 java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -92,29 +90,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
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
|
|
||||||
* <code>Bundle.entry.score</code> in a Bundle resource.
|
|
||||||
* <p>
|
|
||||||
* Note that search URL is only used in FHIR DSTU2 and later.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* Values for this key are of type <b>{@link DecimalDt}</b>
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final ResourceMetadataKeyEnum<DecimalDt> ENTRY_SCORE = new ResourceMetadataKeyEnum<DecimalDt>("ENTRY_SCORE") {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DecimalDt get(IResource theResource) {
|
|
||||||
return getDecimalFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SCORE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(IResource theResource, DecimalDt 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
|
||||||
@ -187,67 +163,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* an absolute URL at runtime.
|
|
||||||
* <p>
|
|
||||||
* Values for this key are of type <b>{@link String}</b>
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final ResourceMetadataKeyEnum<String> LINK_ALTERNATE = new ResourceMetadataKeyEnum<String>("LINK_ALTERNATE") {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String get(IResource theResource) {
|
|
||||||
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_ALTERNATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(IResource theResource, String 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
|
|
||||||
* 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
|
|
||||||
* this to an absolute URL at runtime.
|
|
||||||
* <p>
|
|
||||||
* Values for this key are of type <b>{@link String}</b>
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final ResourceMetadataKeyEnum<String> LINK_SEARCH = new ResourceMetadataKeyEnum<String>("LINK_SEARCH") {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String get(IResource theResource) {
|
|
||||||
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_SEARCH);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(IResource theResource, String 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.
|
|
||||||
* <p>
|
|
||||||
* Values for this key are of type <b>{@link IdDt}</b>
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final ResourceMetadataKeyEnum<IdDt> PREVIOUS_ID = new ResourceMetadataKeyEnum<IdDt>("PREVIOUS_ID") {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IdDt get(IResource theResource) {
|
|
||||||
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(IResource theResource, IdDt 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>
|
||||||
@ -301,18 +217,8 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
if (obj == null) {
|
if (obj == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
//noinspection unchecked
|
||||||
@SuppressWarnings("unchecked")
|
return (List<BaseCodingDt>) obj;
|
||||||
List<BaseCodingDt> securityLabels = (List<BaseCodingDt>) obj;
|
|
||||||
if (securityLabels.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return securityLabels;
|
|
||||||
} catch (ClassCastException e) {
|
|
||||||
throw new InternalErrorException("Found an object of type '" + obj.getClass().getCanonicalName() + "' in resource metadata for key SECURITY_LABELS - Expected "
|
|
||||||
+ BaseCodingDt.class.getCanonicalName());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -337,14 +243,9 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
Object retValObj = theResource.getResourceMetadata().get(TAG_LIST);
|
Object retValObj = theResource.getResourceMetadata().get(TAG_LIST);
|
||||||
if (retValObj == null) {
|
if (retValObj == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (retValObj instanceof TagList) {
|
} else {
|
||||||
if (((TagList) retValObj).isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
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 "
|
|
||||||
+ TagList.class.getCanonicalName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -352,25 +253,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.
|
|
||||||
* <p>
|
|
||||||
* Values for this key are of type <b>{@link String}</b>
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final ResourceMetadataKeyEnum<String> TITLE = new ResourceMetadataKeyEnum<String>("TITLE") {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String get(IResource theResource) {
|
|
||||||
return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(IResource theResource, String 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.)
|
||||||
@ -398,7 +280,10 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
* <p>
|
* <p>
|
||||||
* Values for this key are of type <b>{@link String}</b>
|
* Values for this key are of type <b>{@link String}</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
|
||||||
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;
|
||||||
|
|
||||||
@ -426,7 +311,7 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IdDt get(IResource theResource) {
|
public IdDt get(IResource theResource) {
|
||||||
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
|
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -474,32 +359,45 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
|
|
||||||
public abstract void put(IResource theResource, T theObject);
|
public abstract void put(IResource theResource, T theObject);
|
||||||
|
|
||||||
@Override
|
public static abstract class ResourceMetadataKeySupportingAnyResource<T, T2> extends ResourceMetadataKeyEnum<T> {
|
||||||
public String toString() {
|
|
||||||
return myValue;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public ResourceMetadataKeySupportingAnyResource(String theValue) {
|
||||||
|
super(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DecimalDt getDecimalFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<DecimalDt> theKey) {
|
public abstract T2 get(IAnyResource theResource);
|
||||||
Object retValObj = theResourceMetadata.get(theKey);
|
|
||||||
|
public abstract void put(IAnyResource theResource, T2 theObject);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class ExtensionResourceMetadataKey extends ResourceMetadataKeyEnum<ExtensionDt> {
|
||||||
|
public ExtensionResourceMetadataKey(String theUrl) {
|
||||||
|
super(theUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExtensionDt get(IResource theResource) {
|
||||||
|
Object retValObj = theResource.getResourceMetadata().get(this);
|
||||||
if (retValObj == null) {
|
if (retValObj == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (retValObj instanceof DecimalDt) {
|
} else if (retValObj instanceof ExtensionDt) {
|
||||||
if (((DecimalDt) retValObj).isEmpty()) {
|
return (ExtensionDt) retValObj;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return (DecimalDt) retValObj;
|
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName()
|
||||||
} else if (retValObj instanceof String) {
|
+ "' in resource metadata for key " + this.name() + " - Expected "
|
||||||
if (StringUtils.isBlank((String) retValObj)) {
|
+ ExtensionDt.class.getCanonicalName());
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
return new DecimalDt((String) retValObj);
|
|
||||||
} else if (retValObj instanceof Double) {
|
@Override
|
||||||
return new DecimalDt((Double) retValObj);
|
public void put(IResource theResource, ExtensionDt theObject) {
|
||||||
|
theResource.getResourceMetadata().put(this, theObject);
|
||||||
}
|
}
|
||||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
|
|
||||||
+ 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) {
|
||||||
@ -515,8 +413,8 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
+ InstantDt.class.getCanonicalName());
|
+ InstantDt.class.getCanonicalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
|
private static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata) {
|
||||||
return toId(theKey, theResourceMetadata.get(theKey));
|
return toId(ResourceMetadataKeyEnum.VERSION_ID, theResourceMetadata.get(ResourceMetadataKeyEnum.VERSION_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<IdDt> getIdListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
|
private static List<IdDt> getIdListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum<?>, Object> theResourceMetadata, ResourceMetadataKeyEnum<?> theKey) {
|
||||||
@ -586,49 +484,11 @@ public abstract class ResourceMetadataKeyEnum<T> implements Serializable {
|
|||||||
}
|
}
|
||||||
return (IdDt) retValObj;
|
return (IdDt) retValObj;
|
||||||
} else if (retValObj instanceof Number) {
|
} else if (retValObj instanceof Number) {
|
||||||
return new IdDt(((Number) retValObj).toString());
|
return new IdDt(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> {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public ResourceMetadataKeySupportingAnyResource(String theValue) {
|
|
||||||
super(theValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract T2 get(IAnyResource theResource);
|
|
||||||
|
|
||||||
public abstract void put(IAnyResource theResource, T2 theObject);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class ExtensionResourceMetadataKey extends ResourceMetadataKeyEnum<ExtensionDt> {
|
|
||||||
public ExtensionResourceMetadataKey(String url) {
|
|
||||||
super(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ExtensionDt get(IResource theResource) {
|
|
||||||
Object retValObj = theResource.getResourceMetadata().get(this);
|
|
||||||
if (retValObj == null) {
|
|
||||||
return null;
|
|
||||||
} else if (retValObj instanceof ExtensionDt) {
|
|
||||||
return (ExtensionDt) retValObj;
|
|
||||||
}
|
|
||||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName()
|
|
||||||
+ "' in resource metadata for key " + this.name() + " - Expected "
|
|
||||||
+ ExtensionDt.class.getCanonicalName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void put(IResource theResource, ExtensionDt theObject) {
|
|
||||||
theResource.getResourceMetadata().put(this, theObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -168,18 +168,6 @@ public class Tag extends BaseElement implements IElement, IBaseCoding {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toHeaderValue() {
|
|
||||||
StringBuilder b = new StringBuilder();
|
|
||||||
b.append(this.getTerm());
|
|
||||||
if (isNotBlank(this.getLabel())) {
|
|
||||||
b.append("; label=\"").append(this.getLabel()).append('"');
|
|
||||||
}
|
|
||||||
if (isNotBlank(this.getScheme())) {
|
|
||||||
b.append("; scheme=\"").append(this.getScheme()).append('"');
|
|
||||||
}
|
|
||||||
return b.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
package ca.uhn.fhir.model.api.annotation;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR - Core Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2019 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.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* IResourceProvider and RestfulServer subclasses can use this annotation to designate which custom resources they can provide.
|
|
||||||
* These resources will automatically be added to the resource list used for profile generation.
|
|
||||||
* <pre>
|
|
||||||
* Examples:
|
|
||||||
* {@literal @}ProvidesResources(resource=CustomObservation.class)
|
|
||||||
* class CustomObservationResourceProvider implements IResourceProvider{...}
|
|
||||||
*
|
|
||||||
* {@literal @}ProvidesResources(resource={CustomPatient.class,CustomObservation.class}){...}
|
|
||||||
* class FhirServer extends RestfulServer
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
* Note that you needn't annotate both the IResourceProvider and the RestfulServer for a given resource; either one will suffice.
|
|
||||||
*/
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
|
||||||
public @interface ProvidesResources {
|
|
||||||
Class<?>[] resources();
|
|
||||||
}
|
|
@ -36,5 +36,7 @@ import java.lang.annotation.Target;
|
|||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
@Target(value=ElementType.METHOD)
|
@Target(value=ElementType.METHOD)
|
||||||
public @interface Metadata {
|
public @interface Metadata {
|
||||||
// nothing for now
|
|
||||||
|
long cacheMillis() default 60 * 1000L;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,9 @@ import java.util.*;
|
|||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
|
|
||||||
|
public static final String CT_TEXT_CSV = "text/csv";
|
||||||
public static final String HEADER_REQUEST_ID = "X-Request-ID";
|
public static final String HEADER_REQUEST_ID = "X-Request-ID";
|
||||||
|
public static final String HEADER_REQUEST_SOURCE = "X-Request-Source";
|
||||||
public static final String CACHE_CONTROL_MAX_RESULTS = "max-results";
|
public static final String CACHE_CONTROL_MAX_RESULTS = "max-results";
|
||||||
public static final String CACHE_CONTROL_NO_CACHE = "no-cache";
|
public static final String CACHE_CONTROL_NO_CACHE = "no-cache";
|
||||||
public static final String CACHE_CONTROL_NO_STORE = "no-store";
|
public static final String CACHE_CONTROL_NO_STORE = "no-store";
|
||||||
@ -241,6 +243,15 @@ public class Constants {
|
|||||||
* Operation name for the $lastn operation
|
* Operation name for the $lastn operation
|
||||||
*/
|
*/
|
||||||
public static final String OPERATION_LASTN = "$lastn";
|
public static final String OPERATION_LASTN = "$lastn";
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* This extension represents the equivalent of the
|
||||||
|
* <code>Resource.meta.source</code> field within R4+ resources, and is for
|
||||||
|
* use in DSTU3 resources. It should contain a value of type <code>uri</code>
|
||||||
|
* and will be located on the Resource.meta
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public static final String EXT_META_SOURCE = "http://hapifhir.io/fhir/StructureDefinition/resource-meta-source";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
package ca.uhn.fhir.rest.api;
|
package ca.uhn.fhir.rest.api;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
||||||
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -22,13 +29,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterOr;
|
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
|
||||||
|
|
||||||
public class QualifiedParamList extends ArrayList<String> {
|
public class QualifiedParamList extends ArrayList<String> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@ -108,6 +108,13 @@ public class QualifiedParamList extends ArrayList<String> {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no value was found, at least add that empty string as a value. It should get ignored later, but at
|
||||||
|
// least this lets us give a sensible error message if the parameter name was bad. See
|
||||||
|
// ResourceProviderR4Test#testParameterWithNoValueThrowsError_InvalidChainOnCustomSearch for an example
|
||||||
|
if (retVal.size() == 0) {
|
||||||
|
retVal.add("");
|
||||||
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,12 +33,6 @@ import java.util.Map;
|
|||||||
*/
|
*/
|
||||||
public interface IHttpResponse {
|
public interface IHttpResponse {
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This method was deprecated in HAPI FHIR 2.2 because its name has a typo. Use {@link #bufferEntity()} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
void bufferEntitity() throws IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffer the message entity data.
|
* Buffer the message entity data.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -248,26 +248,30 @@ public class TokenParam extends BaseParam /*implements IQueryParameterType*/ {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object theO) {
|
public boolean equals(Object theO) {
|
||||||
if (this == theO) return true;
|
if (this == theO) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (theO == null || getClass() != theO.getClass()) return false;
|
if (theO == null || getClass() != theO.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TokenParam that = (TokenParam) theO;
|
TokenParam that = (TokenParam) theO;
|
||||||
|
|
||||||
return new EqualsBuilder()
|
EqualsBuilder b = new EqualsBuilder();
|
||||||
.append(myModifier, that.myModifier)
|
b.append(myModifier, that.myModifier);
|
||||||
.append(mySystem, that.mySystem)
|
b.append(mySystem, that.mySystem);
|
||||||
.append(myValue, that.myValue)
|
b.append(myValue, that.myValue);
|
||||||
.isEquals();
|
return b.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return new HashCodeBuilder(17, 37)
|
HashCodeBuilder b = new HashCodeBuilder(17, 37);
|
||||||
.append(myModifier)
|
b.append(myModifier);
|
||||||
.append(mySystem)
|
b.append(mySystem);
|
||||||
.append(myValue)
|
b.append(myValue);
|
||||||
.toHashCode();
|
return b.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String toSystemValue(UriDt theSystem) {
|
private static String toSystemValue(UriDt theSystem) {
|
||||||
|
74
hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AsyncUtil.java
Normal file
74
hapi-fhir-base/src/main/java/ca/uhn/fhir/util/AsyncUtil.java
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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.server.exceptions.InternalErrorException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class AsyncUtil {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(AsyncUtil.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Non instantiable
|
||||||
|
*/
|
||||||
|
private AsyncUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls Thread.sleep and if an InterruptedException occurs, logs a warning but otherwise continues
|
||||||
|
*
|
||||||
|
* @param theMillis The number of millis to sleep
|
||||||
|
* @return Did we sleep the whole amount
|
||||||
|
*/
|
||||||
|
public static boolean sleep(long theMillis) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(theMillis);
|
||||||
|
return true;
|
||||||
|
} catch (InterruptedException theE) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
ourLog.warn("Sleep for {}ms was interrupted", theMillis);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean awaitLatchAndThrowInternalErrorExceptionOnInterrupt(CountDownLatch theInitialCollectionLatch, long theTime, TimeUnit theTimeUnit) {
|
||||||
|
try {
|
||||||
|
return theInitialCollectionLatch.await(theTime, theTimeUnit);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new InternalErrorException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean awaitLatchAndIgnoreInterrupt(CountDownLatch theInitialCollectionLatch, long theTime, TimeUnit theTimeUnit) {
|
||||||
|
try {
|
||||||
|
return theInitialCollectionLatch.await(theTime, theTimeUnit);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
ourLog.warn("Interrupted while waiting for latch");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,9 +20,8 @@ package ca.uhn.fhir.util;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import ca.uhn.fhir.context.*;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
import org.apache.commons.lang3.Validate;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
@ -35,12 +34,10 @@ public class AttachmentUtil {
|
|||||||
* Fetches the base64Binary value of Attachment.data, creating it if it does not
|
* Fetches the base64Binary value of Attachment.data, creating it if it does not
|
||||||
* already exist.
|
* already exist.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static IPrimitiveType<byte[]> getOrCreateData(FhirContext theContext, ICompositeType theAttachment) {
|
public static IPrimitiveType<byte[]> getOrCreateData(FhirContext theContext, ICompositeType theAttachment) {
|
||||||
return getOrCreateChild(theContext, theAttachment, "data", "base64Binary");
|
return getOrCreateChild(theContext, theAttachment, "data", "base64Binary");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static IPrimitiveType<String> getOrCreateContentType(FhirContext theContext, ICompositeType theAttachment) {
|
public static IPrimitiveType<String> getOrCreateContentType(FhirContext theContext, ICompositeType theAttachment) {
|
||||||
return getOrCreateChild(theContext, theAttachment, "contentType", "string");
|
return getOrCreateChild(theContext, theAttachment, "contentType", "string");
|
||||||
}
|
}
|
||||||
@ -64,6 +61,16 @@ public class AttachmentUtil {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setUrl(FhirContext theContext, ICompositeType theAttachment, String theUrl) {
|
||||||
|
BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "url");
|
||||||
|
assert entryChild != null : "Version " + theContext + " has no child " + "url";
|
||||||
|
String typeName = "uri";
|
||||||
|
if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
||||||
|
typeName = "url";
|
||||||
|
}
|
||||||
|
entryChild.getMutator().setValue(theAttachment, newPrimitive(theContext, typeName, theUrl));
|
||||||
|
}
|
||||||
|
|
||||||
public static void setContentType(FhirContext theContext, ICompositeType theAttachment, String theContentType) {
|
public static void setContentType(FhirContext theContext, ICompositeType theAttachment, String theContentType) {
|
||||||
BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "contentType");
|
BaseRuntimeChildDefinition entryChild = getChild(theContext, theAttachment, "contentType");
|
||||||
entryChild.getMutator().setValue(theAttachment, newPrimitive(theContext, "code", theContentType));
|
entryChild.getMutator().setValue(theAttachment, newPrimitive(theContext, "code", theContentType));
|
||||||
@ -88,7 +95,9 @@ public class AttachmentUtil {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
static <T> IPrimitiveType<T> newPrimitive(FhirContext theContext, String theType, T theValue) {
|
static <T> IPrimitiveType<T> newPrimitive(FhirContext theContext, String theType, T theValue) {
|
||||||
IPrimitiveType<T> primitive = (IPrimitiveType<T>) theContext.getElementDefinition(theType).newInstance();
|
BaseRuntimeElementDefinition<?> elementDefinition = theContext.getElementDefinition(theType);
|
||||||
|
Validate.notNull(elementDefinition, "Unknown type %s for %s", theType, theContext);
|
||||||
|
IPrimitiveType<T> primitive = (IPrimitiveType<T>) elementDefinition.newInstance();
|
||||||
primitive.setValue(theValue);
|
primitive.setValue(theValue);
|
||||||
return primitive;
|
return primitive;
|
||||||
}
|
}
|
||||||
@ -100,4 +109,8 @@ public class AttachmentUtil {
|
|||||||
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theElement.getClass());
|
BaseRuntimeElementCompositeDefinition<?> def = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theElement.getClass());
|
||||||
return def.getChildByName(theName);
|
return def.getChildByName(theName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ICompositeType newInstance(FhirContext theFhirCtx) {
|
||||||
|
return (ICompositeType) theFhirCtx.getElementDefinition("Attachment").newInstance();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
package ca.uhn.fhir.util;
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import ca.uhn.fhir.context.*;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|
||||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.util.bundle.BundleEntryMutator;
|
||||||
|
import ca.uhn.fhir.util.bundle.BundleEntryParts;
|
||||||
|
import ca.uhn.fhir.util.bundle.EntryListAccumulator;
|
||||||
|
import ca.uhn.fhir.util.bundle.ModifiableBundleEntry;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
@ -42,37 +43,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||||||
*/
|
*/
|
||||||
public class BundleUtil {
|
public class BundleUtil {
|
||||||
|
|
||||||
public static class BundleEntryParts {
|
|
||||||
private final RequestTypeEnum myRequestType;
|
|
||||||
private final IBaseResource myResource;
|
|
||||||
private final String myUrl;
|
|
||||||
private final String myConditionalUrl;
|
|
||||||
|
|
||||||
BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource, String theConditionalUrl) {
|
|
||||||
super();
|
|
||||||
myRequestType = theRequestType;
|
|
||||||
myUrl = theUrl;
|
|
||||||
myResource = theResource;
|
|
||||||
myConditionalUrl = theConditionalUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RequestTypeEnum getRequestType() {
|
|
||||||
return myRequestType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IBaseResource getResource() {
|
|
||||||
return myResource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getConditionalUrl() {
|
|
||||||
return myConditionalUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrl() {
|
|
||||||
return myUrl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Returns <code>null</code> if the link isn't found or has no value
|
* @return Returns <code>null</code> if the link isn't found or has no value
|
||||||
*/
|
*/
|
||||||
@ -185,20 +155,25 @@ 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<>();
|
EntryListAccumulator entryListAccumulator = new EntryListAccumulator();
|
||||||
|
processEntries(theContext, theBundle, entryListAccumulator);
|
||||||
|
return entryListAccumulator.getList();
|
||||||
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(theBundle);
|
|
||||||
BaseRuntimeChildDefinition entryChild = def.getChildByName("entry");
|
|
||||||
List<IBase> entries = entryChild.getAccessor().getValues(theBundle);
|
|
||||||
|
|
||||||
BaseRuntimeElementCompositeDefinition<?> entryChildElem = (BaseRuntimeElementCompositeDefinition<?>) entryChild.getChildByName("entry");
|
public static void processEntries(FhirContext theContext, IBaseBundle theBundle, Consumer<ModifiableBundleEntry> theProcessor) {
|
||||||
|
RuntimeResourceDefinition bundleDef = theContext.getResourceDefinition(theBundle);
|
||||||
|
BaseRuntimeChildDefinition entryChildDef = bundleDef.getChildByName("entry");
|
||||||
|
List<IBase> entries = entryChildDef.getAccessor().getValues(theBundle);
|
||||||
|
|
||||||
BaseRuntimeChildDefinition resourceChild = entryChildElem.getChildByName("resource");
|
BaseRuntimeElementCompositeDefinition<?> entryChildContentsDef = (BaseRuntimeElementCompositeDefinition<?>) entryChildDef.getChildByName("entry");
|
||||||
BaseRuntimeChildDefinition requestChild = entryChildElem.getChildByName("request");
|
|
||||||
BaseRuntimeElementCompositeDefinition<?> requestElem = (BaseRuntimeElementCompositeDefinition<?>) requestChild.getChildByName("request");
|
BaseRuntimeChildDefinition resourceChildDef = entryChildContentsDef.getChildByName("resource");
|
||||||
BaseRuntimeChildDefinition requestUrlChild = requestElem.getChildByName("url");
|
BaseRuntimeChildDefinition requestChildDef = entryChildContentsDef.getChildByName("request");
|
||||||
BaseRuntimeChildDefinition requestIfNoneExistChild = requestElem.getChildByName("ifNoneExist");
|
BaseRuntimeElementCompositeDefinition<?> requestChildContentsDef = (BaseRuntimeElementCompositeDefinition<?>) requestChildDef.getChildByName("request");
|
||||||
BaseRuntimeChildDefinition methodChild = requestElem.getChildByName("method");
|
BaseRuntimeChildDefinition requestUrlChildDef = requestChildContentsDef.getChildByName("url");
|
||||||
|
BaseRuntimeChildDefinition requestIfNoneExistChildDef = requestChildContentsDef.getChildByName("ifNoneExist");
|
||||||
|
BaseRuntimeChildDefinition methodChildDef = requestChildContentsDef.getChildByName("method");
|
||||||
|
|
||||||
for (IBase nextEntry : entries) {
|
for (IBase nextEntry : entries) {
|
||||||
IBaseResource resource = null;
|
IBaseResource resource = null;
|
||||||
@ -206,15 +181,15 @@ public class BundleUtil {
|
|||||||
RequestTypeEnum requestType = null;
|
RequestTypeEnum requestType = null;
|
||||||
String conditionalUrl = null;
|
String conditionalUrl = null;
|
||||||
|
|
||||||
for (IBase next : resourceChild.getAccessor().getValues(nextEntry)) {
|
for (IBase nextResource : resourceChildDef.getAccessor().getValues(nextEntry)) {
|
||||||
resource = (IBaseResource) next;
|
resource = (IBaseResource) nextResource;
|
||||||
}
|
}
|
||||||
for (IBase nextRequest : requestChild.getAccessor().getValues(nextEntry)) {
|
for (IBase nextRequest : requestChildDef.getAccessor().getValues(nextEntry)) {
|
||||||
for (IBase nextUrl : requestUrlChild.getAccessor().getValues(nextRequest)) {
|
for (IBase nextUrl : requestUrlChildDef.getAccessor().getValues(nextRequest)) {
|
||||||
url = ((IPrimitiveType<?>) nextUrl).getValueAsString();
|
url = ((IPrimitiveType<?>) nextUrl).getValueAsString();
|
||||||
}
|
}
|
||||||
for (IBase nextUrl : methodChild.getAccessor().getValues(nextRequest)) {
|
for (IBase nextMethod : methodChildDef.getAccessor().getValues(nextRequest)) {
|
||||||
String methodString = ((IPrimitiveType<?>) nextUrl).getValueAsString();
|
String methodString = ((IPrimitiveType<?>) nextMethod).getValueAsString();
|
||||||
if (isNotBlank(methodString)) {
|
if (isNotBlank(methodString)) {
|
||||||
requestType = RequestTypeEnum.valueOf(methodString);
|
requestType = RequestTypeEnum.valueOf(methodString);
|
||||||
}
|
}
|
||||||
@ -227,7 +202,7 @@ public class BundleUtil {
|
|||||||
conditionalUrl = url != null && url.contains("?") ? url : null;
|
conditionalUrl = url != null && url.contains("?") ? url : null;
|
||||||
break;
|
break;
|
||||||
case POST:
|
case POST:
|
||||||
List<IBase> ifNoneExistReps = requestIfNoneExistChild.getAccessor().getValues(nextRequest);
|
List<IBase> ifNoneExistReps = requestIfNoneExistChildDef.getAccessor().getValues(nextRequest);
|
||||||
if (ifNoneExistReps.size() > 0) {
|
if (ifNoneExistReps.size() > 0) {
|
||||||
IPrimitiveType<?> ifNoneExist = (IPrimitiveType<?>) ifNoneExistReps.get(0);
|
IPrimitiveType<?> ifNoneExist = (IPrimitiveType<?>) ifNoneExistReps.get(0);
|
||||||
conditionalUrl = ifNoneExist.getValueAsString();
|
conditionalUrl = ifNoneExist.getValueAsString();
|
||||||
@ -241,11 +216,10 @@ public class BundleUtil {
|
|||||||
* All 3 might be null - That's ok because we still want to know the
|
* All 3 might be null - That's ok because we still want to know the
|
||||||
* order in the original bundle.
|
* order in the original bundle.
|
||||||
*/
|
*/
|
||||||
retVal.add(new BundleEntryParts(requestType, url, resource, conditionalUrl));
|
BundleEntryMutator mutator = new BundleEntryMutator(nextEntry, requestChildDef, requestChildContentsDef);
|
||||||
|
ModifiableBundleEntry entry = new ModifiableBundleEntry(new BundleEntryParts(requestType, url, resource, conditionalUrl), mutator);
|
||||||
|
theProcessor.accept(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -278,4 +252,25 @@ public class BundleUtil {
|
|||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DSTU3 did not allow the PATCH verb for transaction bundles- so instead we infer that a bundle
|
||||||
|
* is a patch if the payload is a binary resource containing a patch. This method
|
||||||
|
* tests whether a resource (which should have come from
|
||||||
|
* <code>Bundle.entry.resource</code> is a Binary resource with a patch
|
||||||
|
* payload type.
|
||||||
|
*/
|
||||||
|
public static boolean isDstu3TransactionPatch(IBaseResource thePayloadResource) {
|
||||||
|
boolean isPatch = false;
|
||||||
|
if (thePayloadResource instanceof IBaseBinary) {
|
||||||
|
String contentType = ((IBaseBinary) thePayloadResource).getContentType();
|
||||||
|
try {
|
||||||
|
PatchTypeEnum.forContentTypeOrThrowInvalidRequestException(contentType);
|
||||||
|
isPatch = true;
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isPatch;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -735,25 +735,15 @@ public class FhirTerser {
|
|||||||
valueType = (Class<? extends IBase>) valueType.getSuperclass();
|
valueType = (Class<? extends IBase>) valueType.getSuperclass();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (childElementDef == null) {
|
Class<? extends IBase> typeClass = nextValue.getClass();
|
||||||
StringBuilder b = new StringBuilder();
|
while (childElementDef == null && IBase.class.isAssignableFrom(typeClass)) {
|
||||||
b.append("Found value of type[");
|
//noinspection unchecked
|
||||||
b.append(nextValue.getClass().getSimpleName());
|
typeClass = (Class<? extends IBase>) typeClass.getSuperclass();
|
||||||
b.append("] which is not valid for field[");
|
childElementDef = nextChild.getChildElementDefinitionByDatatype(typeClass);
|
||||||
b.append(nextChild.getElementName());
|
|
||||||
b.append("] in ");
|
|
||||||
b.append(childDef.getName());
|
|
||||||
b.append(" - Valid types: ");
|
|
||||||
for (Iterator<String> iter = new TreeSet<>(nextChild.getValidChildNames()).iterator(); iter.hasNext(); ) {
|
|
||||||
BaseRuntimeElementDefinition<?> childByName = nextChild.getChildByName(iter.next());
|
|
||||||
b.append(childByName.getImplementingClass().getSimpleName());
|
|
||||||
if (iter.hasNext()) {
|
|
||||||
b.append(", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new DataFormatException(b.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Validate.notNull(childElementDef, "Found value of type[%s] which is not valid for field[%s] in %s", nextValue.getClass(), nextChild.getElementName(), childDef.getName());
|
||||||
|
|
||||||
visit(nextValue, nextChild, childElementDef, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
|
visit(nextValue, nextChild, childElementDef, theCallback, theContainingElementPath, theChildDefinitionPath, theElementDefinitionPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FileUtil.java
Normal file
38
hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FileUtil.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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.text.DecimalFormat;
|
||||||
|
|
||||||
|
public class FileUtil {
|
||||||
|
// Use "bytes" instead of just "b" because it reads easier in logs
|
||||||
|
private static final String[] UNITS = new String[]{"Bytes", "kB", "MB", "GB", "TB"};
|
||||||
|
|
||||||
|
public static String formatFileSize(long theBytes) {
|
||||||
|
if (theBytes <= 0) {
|
||||||
|
return "0 " + UNITS[0];
|
||||||
|
}
|
||||||
|
int digitGroups = (int) (Math.log10(theBytes) / Math.log10(1024));
|
||||||
|
digitGroups = Math.min(digitGroups, UNITS.length - 1);
|
||||||
|
return new DecimalFormat("###0.#").format(theBytes / Math.pow(1024, digitGroups)) + " " + UNITS[digitGroups];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -45,7 +45,7 @@ public interface IModelVisitor2 {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
boolean acceptUndeclaredExtension(IBaseExtension<?, ?> theNextExt, List<IBase> theContainingElementPath, List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath);
|
default boolean acceptUndeclaredExtension(IBaseExtension<?, ?> theNextExt, List<IBase> theContainingElementPath, List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) { return true; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ package ca.uhn.fhir.util;
|
|||||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -46,6 +46,29 @@ public class MetaUtil {
|
|||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the value for <code>Resource.meta.source</code> for R4+ resources, and places the value in
|
||||||
|
* an extension on <code>Resource.meta</code>
|
||||||
|
* with the URL <code>http://hapifhir.io/fhir/StructureDefinition/resource-meta-source</code> for DSTU3.
|
||||||
|
*
|
||||||
|
* @param theContext The FhirContext object
|
||||||
|
* @param theResource The resource to modify
|
||||||
|
* @param theValue The source URI
|
||||||
|
* @see <a href="http://hl7.org/fhir/resource-definitions.html#Resource.meta">Meta.source</a>
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static void setSource(FhirContext theContext, IBaseResource theResource, String theValue) {
|
||||||
|
if (theContext.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.R4)) {
|
||||||
|
MetaUtil.setSource(theContext, theResource.getMeta(), theValue);
|
||||||
|
} else if (theContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
|
||||||
|
IBaseExtension<?, ?> sourceExtension = ((IBaseHasExtensions) theResource.getMeta()).addExtension();
|
||||||
|
sourceExtension.setUrl(Constants.EXT_META_SOURCE);
|
||||||
|
IPrimitiveType<String> value = (IPrimitiveType<String>) theContext.getElementDefinition("uri").newInstance();
|
||||||
|
value.setValue(theValue);
|
||||||
|
sourceExtension.setValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void setSource(FhirContext theContext, IBaseMetaType theMeta, String theValue) {
|
public static void setSource(FhirContext theContext, IBaseMetaType theMeta, String theValue) {
|
||||||
BaseRuntimeElementCompositeDefinition<?> elementDef = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theMeta.getClass());
|
BaseRuntimeElementCompositeDefinition<?> elementDef = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theMeta.getClass());
|
||||||
BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source");
|
BaseRuntimeChildDefinition sourceChild = elementDef.getChildByName("source");
|
||||||
|
@ -19,22 +19,17 @@ package ca.uhn.fhir.util;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.*;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
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 ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import java.util.List;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utilities for dealing with OperationOutcome resources across various model versions
|
* Utilities for dealing with OperationOutcome resources across various model versions
|
||||||
@ -43,20 +38,17 @@ public class OperationOutcomeUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an issue to an OperationOutcome
|
* Add an issue to an OperationOutcome
|
||||||
*
|
* @param theCtx The fhir context
|
||||||
* @param theCtx
|
* @param theOperationOutcome The OO resource to add to
|
||||||
* The fhir context
|
* @param theSeverity The severity (fatal | error | warning | information)
|
||||||
* @param theOperationOutcome
|
* @param theDetails The details string
|
||||||
* The OO resource to add to
|
|
||||||
* @param theSeverity
|
|
||||||
* The severity (fatal | error | warning | information)
|
|
||||||
* @param theDetails
|
|
||||||
* The details string
|
|
||||||
* @param theCode
|
* @param theCode
|
||||||
|
* @return Returns the newly added issue
|
||||||
*/
|
*/
|
||||||
public static void addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
|
public static IBase addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
|
||||||
IBase issue = createIssue(theCtx, theOperationOutcome);
|
IBase issue = createIssue(theCtx, theOperationOutcome);
|
||||||
populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode);
|
populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode);
|
||||||
|
return issue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IBase createIssue(FhirContext theCtx, IBaseResource theOutcome) {
|
private static IBase createIssue(FhirContext theCtx, IBaseResource theOutcome) {
|
||||||
@ -140,7 +132,6 @@ public class OperationOutcomeUtil {
|
|||||||
|
|
||||||
BaseRuntimeElementDefinition<?> stringDef = detailsChild.getChildByName(detailsChild.getElementName());
|
BaseRuntimeElementDefinition<?> stringDef = detailsChild.getChildByName(detailsChild.getElementName());
|
||||||
BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
|
BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
|
||||||
BaseRuntimeChildDefinition locationChild = issueElement.getChildByName("location");
|
|
||||||
|
|
||||||
IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments());
|
IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments());
|
||||||
severityElem.setValueAsString(theSeverity);
|
severityElem.setValueAsString(theSeverity);
|
||||||
@ -150,7 +141,13 @@ public class OperationOutcomeUtil {
|
|||||||
string.setValueAsString(theDetails);
|
string.setValueAsString(theDetails);
|
||||||
detailsChild.getMutator().setValue(theIssue, string);
|
detailsChild.getMutator().setValue(theIssue, string);
|
||||||
|
|
||||||
|
addLocationToIssue(theCtx, theIssue, theLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addLocationToIssue(FhirContext theContext, IBase theIssue, String theLocation) {
|
||||||
if (isNotBlank(theLocation)) {
|
if (isNotBlank(theLocation)) {
|
||||||
|
BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theIssue.getClass());
|
||||||
|
BaseRuntimeChildDefinition locationChild = issueElement.getChildByName("location");
|
||||||
IPrimitiveType<?> locationElem = (IPrimitiveType<?>) locationChild.getChildByName("location").newInstance(locationChild.getInstanceConstructorArguments());
|
IPrimitiveType<?> locationElem = (IPrimitiveType<?>) locationChild.getChildByName("location").newInstance(locationChild.getInstanceConstructorArguments());
|
||||||
locationElem.setValueAsString(theLocation);
|
locationElem.setValueAsString(theLocation);
|
||||||
locationChild.getMutator().addValue(theIssue, locationElem);
|
locationChild.getMutator().addValue(theIssue, locationElem);
|
||||||
|
@ -72,6 +72,8 @@ public class ParametersUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void addClientParameter(FhirContext theContext, Object theValue, IBaseResource theTargetResource, BaseRuntimeChildDefinition paramChild, BaseRuntimeElementCompositeDefinition<?> paramChildElem, String theName) {
|
private static void addClientParameter(FhirContext theContext, Object theValue, IBaseResource theTargetResource, BaseRuntimeChildDefinition paramChild, BaseRuntimeElementCompositeDefinition<?> paramChildElem, String theName) {
|
||||||
|
Validate.notNull(theValue, "theValue must not be null");
|
||||||
|
|
||||||
if (theValue instanceof IBaseResource) {
|
if (theValue instanceof IBaseResource) {
|
||||||
IBase parameter = createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
|
IBase parameter = createParameterRepetition(theContext, theTargetResource, paramChild, paramChildElem, theName);
|
||||||
paramChildElem.getChildByName("resource").getMutator().addValue(parameter, (IBaseResource) theValue);
|
paramChildElem.getChildByName("resource").getMutator().addValue(parameter, (IBaseResource) theValue);
|
||||||
@ -162,7 +164,6 @@ public class ParametersUtil {
|
|||||||
IPrimitiveType<Boolean> value = (IPrimitiveType<Boolean>) theCtx.getElementDefinition("boolean").newInstance();
|
IPrimitiveType<Boolean> value = (IPrimitiveType<Boolean>) theCtx.getElementDefinition("boolean").newInstance();
|
||||||
value.setValue(theValue);
|
value.setValue(theValue);
|
||||||
addParameterToParameters(theCtx, theParameters, theName, value);
|
addParameterToParameters(theCtx, theParameters, theName, value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -51,16 +51,8 @@ public class ReflectionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Class<?> getGenericCollectionTypeOfField(Field next) {
|
public static Class<?> getGenericCollectionTypeOfField(Field next) {
|
||||||
Class<?> type;
|
|
||||||
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
|
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
|
||||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
|
||||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
|
||||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
|
||||||
type = (Class<?>) pt.getRawType();
|
|
||||||
} else {
|
|
||||||
type = (Class<?>) firstArg;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,42 +76,37 @@ public class ReflectionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
|
public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
|
||||||
Class<?> type;
|
|
||||||
Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
|
Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
|
||||||
if (Class.class.equals(genericParameterType) || Class.class.equals(genericParameterType.getClass())) {
|
if (Class.class.equals(genericParameterType) || Class.class.equals(genericParameterType.getClass())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ParameterizedType collectionType = (ParameterizedType) genericParameterType;
|
ParameterizedType collectionType = (ParameterizedType) genericParameterType;
|
||||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
|
||||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
|
||||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
|
||||||
type = (Class<?>) pt.getRawType();
|
|
||||||
} else {
|
|
||||||
type = (Class<?>) firstArg;
|
|
||||||
}
|
|
||||||
return type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "rawtypes" })
|
|
||||||
public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
|
public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
|
||||||
Class<?> type;
|
|
||||||
Type genericReturnType = theMethod.getGenericReturnType();
|
Type genericReturnType = theMethod.getGenericReturnType();
|
||||||
if (!(genericReturnType instanceof ParameterizedType)) {
|
if (!(genericReturnType instanceof ParameterizedType)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ParameterizedType collectionType = (ParameterizedType) genericReturnType;
|
ParameterizedType collectionType = (ParameterizedType) genericReturnType;
|
||||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
|
||||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
}
|
||||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
private static Class<?> getGenericCollectionTypeOf(Type theType) {
|
||||||
|
Class<?> type;
|
||||||
|
if (ParameterizedType.class.isAssignableFrom(theType.getClass())) {
|
||||||
|
ParameterizedType pt = ((ParameterizedType) theType);
|
||||||
type = (Class<?>) pt.getRawType();
|
type = (Class<?>) pt.getRawType();
|
||||||
} else if (firstArg instanceof TypeVariable<?>) {
|
} else if (theType instanceof TypeVariable<?>) {
|
||||||
Type decl = ((TypeVariable) firstArg).getBounds()[0];
|
Type decl = ((TypeVariable) theType).getBounds()[0];
|
||||||
return (Class<?>) decl;
|
return (Class<?>) decl;
|
||||||
} else if (firstArg instanceof WildcardType) {
|
} else if (theType instanceof WildcardType) {
|
||||||
Type decl = ((WildcardType) firstArg).getUpperBounds()[0];
|
Type decl = ((WildcardType) theType).getUpperBounds()[0];
|
||||||
return (Class<?>) decl;
|
return (Class<?>) decl;
|
||||||
} else {
|
} else {
|
||||||
type = (Class<?>) firstArg;
|
type = (Class<?>) theType;
|
||||||
}
|
}
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
@ -154,8 +141,7 @@ public class ReflectionUtil {
|
|||||||
public static Object newInstanceOfFhirServerType(String theType) {
|
public static Object newInstanceOfFhirServerType(String theType) {
|
||||||
String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!";
|
String errorMessage = "Unable to instantiate server framework. Please make sure that hapi-fhir-server library is on your classpath!";
|
||||||
String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer";
|
String wantedType = "ca.uhn.fhir.rest.api.server.IFhirVersionServer";
|
||||||
Object fhirServerVersion = newInstanceOfType(theType, errorMessage, wantedType);
|
return newInstanceOfType(theType, errorMessage, wantedType);
|
||||||
return fhirServerVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package ca.uhn.fhir.util;
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
import java.text.DecimalFormat;
|
||||||
@ -9,8 +10,6 @@ import java.util.Date;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
@ -48,12 +47,14 @@ public class StopWatch {
|
|||||||
private long myStarted = now();
|
private long myStarted = now();
|
||||||
private TaskTiming myCurrentTask;
|
private TaskTiming myCurrentTask;
|
||||||
private LinkedList<TaskTiming> myTasks;
|
private LinkedList<TaskTiming> myTasks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public StopWatch() {
|
public StopWatch() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*
|
*
|
||||||
@ -63,7 +64,13 @@ public class StopWatch {
|
|||||||
myStarted = theStart.getTime();
|
myStarted = theStart.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public StopWatch(long theL) {
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param theStart The time that the stopwatch was started
|
||||||
|
*/
|
||||||
|
public StopWatch(long theStart) {
|
||||||
|
myStarted = theStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addNewlineIfContentExists(StringBuilder theB) {
|
private void addNewlineIfContentExists(StringBuilder theB) {
|
||||||
@ -120,6 +127,8 @@ public class StopWatch {
|
|||||||
b.append(": ");
|
b.append(": ");
|
||||||
b.append(formatMillis(delta));
|
b.append(formatMillis(delta));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
b.append("No tasks");
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskTiming last = null;
|
TaskTiming last = null;
|
||||||
@ -257,13 +266,12 @@ public class StopWatch {
|
|||||||
*/
|
*/
|
||||||
public void startTask(String theTaskName) {
|
public void startTask(String theTaskName) {
|
||||||
endCurrentTask();
|
endCurrentTask();
|
||||||
if (isNotBlank(theTaskName)) {
|
Validate.notBlank(theTaskName, "Task name must not be blank");
|
||||||
myCurrentTask = new TaskTiming()
|
myCurrentTask = new TaskTiming()
|
||||||
.setTaskName(theTaskName)
|
.setTaskName(theTaskName)
|
||||||
.setStart(now());
|
.setStart(now());
|
||||||
myTasks.add(myCurrentTask);
|
myTasks.add(myCurrentTask);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats value in an appropriate format. See {@link #formatMillis(long)}}
|
* Formats value in an appropriate format. See {@link #formatMillis(long)}}
|
||||||
@ -331,18 +339,18 @@ public class StopWatch {
|
|||||||
/**
|
/**
|
||||||
* Append a right-aligned and zero-padded numeric value to a `StringBuilder`.
|
* Append a right-aligned and zero-padded numeric value to a `StringBuilder`.
|
||||||
*/
|
*/
|
||||||
static private void append(StringBuilder tgt, String pfx, int dgt, long val) {
|
static void appendRightAlignedNumber(StringBuilder theStringBuilder, String thePrefix, int theNumberOfDigits, long theValueToAppend) {
|
||||||
tgt.append(pfx);
|
theStringBuilder.append(thePrefix);
|
||||||
if (dgt > 1) {
|
if (theNumberOfDigits > 1) {
|
||||||
int pad = (dgt - 1);
|
int pad = (theNumberOfDigits - 1);
|
||||||
for (long xa = val; xa > 9 && pad > 0; xa /= 10) {
|
for (long xa = theValueToAppend; xa > 9 && pad > 0; xa /= 10) {
|
||||||
pad--;
|
pad--;
|
||||||
}
|
}
|
||||||
for (int xa = 0; xa < pad; xa++) {
|
for (int xa = 0; xa < pad; xa++) {
|
||||||
tgt.append('0');
|
theStringBuilder.append('0');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tgt.append(val);
|
theStringBuilder.append(theValueToAppend);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -399,11 +407,11 @@ public class StopWatch {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
long millisAsLong = (long) theMillis;
|
long millisAsLong = (long) theMillis;
|
||||||
append(buf, "", 2, ((millisAsLong % DateUtils.MILLIS_PER_DAY) / DateUtils.MILLIS_PER_HOUR));
|
appendRightAlignedNumber(buf, "", 2, ((millisAsLong % DateUtils.MILLIS_PER_DAY) / DateUtils.MILLIS_PER_HOUR));
|
||||||
append(buf, ":", 2, ((millisAsLong % DateUtils.MILLIS_PER_HOUR) / DateUtils.MILLIS_PER_MINUTE));
|
appendRightAlignedNumber(buf, ":", 2, ((millisAsLong % DateUtils.MILLIS_PER_HOUR) / DateUtils.MILLIS_PER_MINUTE));
|
||||||
append(buf, ":", 2, ((millisAsLong % DateUtils.MILLIS_PER_MINUTE) / DateUtils.MILLIS_PER_SECOND));
|
appendRightAlignedNumber(buf, ":", 2, ((millisAsLong % DateUtils.MILLIS_PER_MINUTE) / DateUtils.MILLIS_PER_SECOND));
|
||||||
if (theMillis <= DateUtils.MILLIS_PER_MINUTE) {
|
if (theMillis <= DateUtils.MILLIS_PER_MINUTE) {
|
||||||
append(buf, ".", 3, (millisAsLong % DateUtils.MILLIS_PER_SECOND));
|
appendRightAlignedNumber(buf, ".", 3, (millisAsLong % DateUtils.MILLIS_PER_SECOND));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
|
@ -8,6 +8,8 @@ import ca.uhn.fhir.rest.api.Constants;
|
|||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import com.google.common.escape.Escaper;
|
import com.google.common.escape.Escaper;
|
||||||
import com.google.common.net.PercentEscaper;
|
import com.google.common.net.PercentEscaper;
|
||||||
|
import org.apache.http.NameValuePair;
|
||||||
|
import org.apache.http.client.utils.URLEncodedUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
@ -457,4 +459,23 @@ public class UrlUtil {
|
|||||||
return theString;
|
return theString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<NameValuePair> translateMatchUrl(String theMatchUrl) {
|
||||||
|
List<NameValuePair> parameters;
|
||||||
|
String matchUrl = theMatchUrl;
|
||||||
|
int questionMarkIndex = matchUrl.indexOf('?');
|
||||||
|
if (questionMarkIndex != -1) {
|
||||||
|
matchUrl = matchUrl.substring(questionMarkIndex + 1);
|
||||||
|
}
|
||||||
|
matchUrl = matchUrl.replace("|", "%7C");
|
||||||
|
matchUrl = matchUrl.replace("=>=", "=%3E%3D");
|
||||||
|
matchUrl = matchUrl.replace("=<=", "=%3C%3D");
|
||||||
|
matchUrl = matchUrl.replace("=>", "=%3E");
|
||||||
|
matchUrl = matchUrl.replace("=<", "=%3C");
|
||||||
|
if (matchUrl.contains(" ")) {
|
||||||
|
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - URL is invalid (must not contain spaces)");
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters = URLEncodedUtils.parse((matchUrl), Constants.CHARSET_UTF8, '&');
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,13 @@ import com.ctc.wstx.stax.WstxOutputFactory;
|
|||||||
import org.apache.commons.text.StringEscapeUtils;
|
import org.apache.commons.text.StringEscapeUtils;
|
||||||
import org.codehaus.stax2.XMLOutputFactory2;
|
import org.codehaus.stax2.XMLOutputFactory2;
|
||||||
import org.codehaus.stax2.io.EscapingWriterFactory;
|
import org.codehaus.stax2.io.EscapingWriterFactory;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
import javax.xml.stream.*;
|
import javax.xml.stream.*;
|
||||||
import javax.xml.stream.events.XMLEvent;
|
import javax.xml.stream.events.XMLEvent;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@ -40,7 +46,7 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility methods for working with the StAX API.
|
* Utility methods for working with the StAX API.
|
||||||
*
|
* <p>
|
||||||
* This class contains code adapted from the Apache Axiom project.
|
* This class contains code adapted from the Apache Axiom project.
|
||||||
*/
|
*/
|
||||||
public class XmlUtil {
|
public class XmlUtil {
|
||||||
@ -1507,6 +1513,73 @@ public class XmlUtil {
|
|||||||
VALID_ENTITY_NAMES = Collections.unmodifiableMap(validEntityNames);
|
VALID_ENTITY_NAMES = Collections.unmodifiableMap(validEntityNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Non-instantiable */
|
||||||
|
private XmlUtil() {}
|
||||||
|
|
||||||
|
private static final class ExtendedEntityReplacingXmlResolver implements XMLResolver {
|
||||||
|
@Override
|
||||||
|
public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) {
|
||||||
|
if (thePublicID == null && theSystemID == null) {
|
||||||
|
if (theNamespace != null && VALID_ENTITY_NAMES.containsKey(theNamespace)) {
|
||||||
|
return new String(Character.toChars(VALID_ENTITY_NAMES.get(theNamespace)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MyEscaper implements EscapingWriterFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
|
||||||
|
return createEscapingWriterFor(new OutputStreamWriter(theOut, theEnc), theEnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Writer createEscapingWriterFor(final Writer theW, String theEnc) {
|
||||||
|
return new Writer() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
theW.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void flush() throws IOException {
|
||||||
|
theW.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
|
||||||
|
boolean hasEscapable = false;
|
||||||
|
for (int i = 0; i < theLen && !hasEscapable; i++) {
|
||||||
|
char nextChar = theCbuf[i + theOff];
|
||||||
|
switch (nextChar) {
|
||||||
|
case '<':
|
||||||
|
case '>':
|
||||||
|
case '"':
|
||||||
|
case '&':
|
||||||
|
hasEscapable = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasEscapable) {
|
||||||
|
theW.write(theCbuf, theOff, theLen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String escaped = StringEscapeUtils.escapeXml10(new String(theCbuf, theOff, theLen));
|
||||||
|
theW.write(escaped.toCharArray());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private static XMLOutputFactory createOutputFactory() throws FactoryConfigurationError {
|
private static XMLOutputFactory createOutputFactory() throws FactoryConfigurationError {
|
||||||
try {
|
try {
|
||||||
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
|
// Detect if we're running with the Android lib, and force repackaged Woodstox to be used
|
||||||
@ -1637,15 +1710,11 @@ public class XmlUtil {
|
|||||||
try {
|
try {
|
||||||
Class.forName("com.ctc.wstx.stax.WstxInputFactory");
|
Class.forName("com.ctc.wstx.stax.WstxInputFactory");
|
||||||
boolean isWoodstox = inputFactory instanceof com.ctc.wstx.stax.WstxInputFactory;
|
boolean isWoodstox = inputFactory instanceof com.ctc.wstx.stax.WstxInputFactory;
|
||||||
if ( !isWoodstox )
|
if (!isWoodstox) {
|
||||||
{
|
|
||||||
// Check if implementation is woodstox by property since instanceof check does not work if running in JBoss
|
// Check if implementation is woodstox by property since instanceof check does not work if running in JBoss
|
||||||
try
|
try {
|
||||||
{
|
|
||||||
isWoodstox = inputFactory.getProperty("org.codehaus.stax2.implVersion") != null;
|
isWoodstox = inputFactory.getProperty("org.codehaus.stax2.implVersion") != null;
|
||||||
}
|
} catch (Exception e) {
|
||||||
catch ( Exception e )
|
|
||||||
{
|
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1673,7 +1742,6 @@ public class XmlUtil {
|
|||||||
return ourOutputFactory;
|
return ourOutputFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void logStaxImplementation(Class<?> theClass) {
|
private static void logStaxImplementation(Class<?> theClass) {
|
||||||
IDependencyLog logger = DependencyLogFactory.createJarLogger();
|
IDependencyLog logger = DependencyLogFactory.createJarLogger();
|
||||||
if (logger != null) {
|
if (logger != null) {
|
||||||
@ -1682,7 +1750,6 @@ public class XmlUtil {
|
|||||||
ourHaveLoggedStaxImplementation = true;
|
ourHaveLoggedStaxImplementation = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static XMLInputFactory newInputFactory() throws FactoryConfigurationError {
|
static XMLInputFactory newInputFactory() throws FactoryConfigurationError {
|
||||||
XMLInputFactory inputFactory;
|
XMLInputFactory inputFactory;
|
||||||
try {
|
try {
|
||||||
@ -1767,68 +1834,30 @@ public class XmlUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class ExtendedEntityReplacingXmlResolver implements XMLResolver {
|
public static Document parseDocument(String theInput) throws IOException, SAXException {
|
||||||
@Override
|
DocumentBuilder builder = null;
|
||||||
public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) {
|
try {
|
||||||
if (thePublicID == null && theSystemID == null) {
|
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
|
||||||
if (theNamespace != null && VALID_ENTITY_NAMES.containsKey(theNamespace)) {
|
docBuilderFactory.setNamespaceAware(true);
|
||||||
return new String(Character.toChars(VALID_ENTITY_NAMES.get(theNamespace)));
|
docBuilderFactory.setXIncludeAware(false);
|
||||||
}
|
docBuilderFactory.setExpandEntityReferences(false);
|
||||||
|
try {
|
||||||
|
docBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||||
|
docBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||||
|
docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
|
||||||
|
docBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||||
|
docBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||||
|
throwUnitTestExceptionIfConfiguredToDoSo();
|
||||||
|
} catch (Exception e) {
|
||||||
|
ourLog.warn("Failed to set feature on XML parser: " + e.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
builder = docBuilderFactory.newDocumentBuilder();
|
||||||
}
|
} catch (ParserConfigurationException e) {
|
||||||
|
throw new ConfigurationException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MyEscaper implements EscapingWriterFactory {
|
InputSource src = new InputSource(new StringReader(theInput));
|
||||||
|
return builder.parse(src);
|
||||||
@Override
|
|
||||||
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
|
|
||||||
return createEscapingWriterFor(new OutputStreamWriter(theOut, theEnc), theEnc);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Writer createEscapingWriterFor(final Writer theW, String theEnc) {
|
|
||||||
return new Writer() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
theW.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void flush() throws IOException {
|
|
||||||
theW.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
|
|
||||||
boolean hasEscapable = false;
|
|
||||||
for (int i = 0; i < theLen && !hasEscapable; i++) {
|
|
||||||
char nextChar = theCbuf[i + theOff];
|
|
||||||
switch (nextChar) {
|
|
||||||
case '<':
|
|
||||||
case '>':
|
|
||||||
case '"':
|
|
||||||
case '&':
|
|
||||||
hasEscapable = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasEscapable) {
|
|
||||||
theW.write(theCbuf, theOff, theLen);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String escaped = StringEscapeUtils.escapeXml10(new String(theCbuf, theOff, theLen));
|
|
||||||
theW.write(escaped.toCharArray());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
package ca.uhn.fhir.util.bundle;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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.BaseRuntimeElementCompositeDefinition;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
public class BundleEntryMutator {
|
||||||
|
private final IBase myEntry;
|
||||||
|
private final BaseRuntimeChildDefinition myRequestChildDef;
|
||||||
|
private final BaseRuntimeElementCompositeDefinition<?> myRequestChildContentsDef;
|
||||||
|
|
||||||
|
public BundleEntryMutator(IBase theEntry, BaseRuntimeChildDefinition theRequestChildDef, BaseRuntimeElementCompositeDefinition<?> theRequestChildContentsDef) {
|
||||||
|
myEntry = theEntry;
|
||||||
|
myRequestChildDef = theRequestChildDef;
|
||||||
|
myRequestChildContentsDef = theRequestChildContentsDef;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setRequestUrl(FhirContext theFhirContext, String theRequestUrl) {
|
||||||
|
BaseRuntimeChildDefinition requestUrlChildDef = myRequestChildContentsDef.getChildByName("url");
|
||||||
|
IPrimitiveType<?> url = ParametersUtil.createUri(theFhirContext, theRequestUrl);
|
||||||
|
for (IBase nextRequest : myRequestChildDef.getAccessor().getValues(myEntry)) {
|
||||||
|
requestUrlChildDef.getMutator().addValue(nextRequest, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package ca.uhn.fhir.util.bundle;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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.api.RequestTypeEnum;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
public class BundleEntryParts {
|
||||||
|
private final RequestTypeEnum myRequestType;
|
||||||
|
private final IBaseResource myResource;
|
||||||
|
private final String myUrl;
|
||||||
|
private final String myConditionalUrl;
|
||||||
|
|
||||||
|
public BundleEntryParts(RequestTypeEnum theRequestType, String theUrl, IBaseResource theResource, String theConditionalUrl) {
|
||||||
|
super();
|
||||||
|
myRequestType = theRequestType;
|
||||||
|
myUrl = theUrl;
|
||||||
|
myResource = theResource;
|
||||||
|
myConditionalUrl = theConditionalUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequestTypeEnum getRequestType() {
|
||||||
|
return myRequestType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IBaseResource getResource() {
|
||||||
|
return myResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConditionalUrl() {
|
||||||
|
return myConditionalUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return myUrl;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package ca.uhn.fhir.util.bundle;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 University Health Network
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class EntryListAccumulator implements Consumer<ModifiableBundleEntry> {
|
||||||
|
private final List<BundleEntryParts> myList = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(ModifiableBundleEntry theModifiableBundleEntry) {
|
||||||
|
myList.add(theModifiableBundleEntry.getBundleEntryParts());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<BundleEntryParts> getList() {
|
||||||
|
return myList;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package ca.uhn.fhir.util.bundle;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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;
|
||||||
|
|
||||||
|
public class ModifiableBundleEntry {
|
||||||
|
private final BundleEntryParts myBundleEntryParts;
|
||||||
|
private final BundleEntryMutator myBundleEntryMutator;
|
||||||
|
|
||||||
|
public ModifiableBundleEntry(BundleEntryParts theBundleEntryParts, BundleEntryMutator theBundleEntryMutator) {
|
||||||
|
myBundleEntryParts = theBundleEntryParts;
|
||||||
|
myBundleEntryMutator = theBundleEntryMutator;
|
||||||
|
}
|
||||||
|
|
||||||
|
BundleEntryParts getBundleEntryParts() {
|
||||||
|
return myBundleEntryParts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRequestUrl(FhirContext theFhirContext, String theRequestUrl) {
|
||||||
|
myBundleEntryMutator.setRequestUrl(theFhirContext, theRequestUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRequestUrl() {
|
||||||
|
return myBundleEntryParts.getUrl();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package ca.uhn.fhir.model.primitive;
|
package ca.uhn.fhir.validation;
|
||||||
|
|
||||||
/*
|
/*-
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR - Core Library
|
* HAPI FHIR - Core Library
|
||||||
* %%
|
* %%
|
||||||
@ -20,19 +20,13 @@ package ca.uhn.fhir.model.primitive;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
/**
|
||||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
* This interface marks a {@link IValidatorModule validator module} that uses the FHIR
|
||||||
|
* FhirInstanceValidator as the underlying engine (i.e. it performs
|
||||||
|
* profile validation)
|
||||||
|
*/
|
||||||
|
public interface IInstanceValidatorModule extends IValidatorModule {
|
||||||
|
|
||||||
@DatatypeDef(name = "idref")
|
// nothing extra yet
|
||||||
public class IdrefDt extends StringDt {
|
|
||||||
|
|
||||||
private IElement myTarget;
|
|
||||||
|
|
||||||
public IElement getTarget() {
|
|
||||||
return myTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTarget(IElement theTarget) {
|
|
||||||
myTarget = theTarget;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -19,26 +19,32 @@ package ca.uhn.fhir.validation;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
import java.io.*;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.xml.XMLConstants;
|
|
||||||
import javax.xml.transform.Source;
|
|
||||||
import javax.xml.transform.stream.StreamSource;
|
|
||||||
import javax.xml.validation.*;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.commons.io.input.BOMInputStream;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.w3c.dom.ls.LSInput;
|
|
||||||
import org.w3c.dom.ls.LSResourceResolver;
|
|
||||||
import org.xml.sax.*;
|
|
||||||
|
|
||||||
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.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.io.input.BOMInputStream;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.w3c.dom.ls.LSInput;
|
||||||
|
import org.w3c.dom.ls.LSResourceResolver;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.SAXNotRecognizedException;
|
||||||
|
import org.xml.sax.SAXParseException;
|
||||||
|
|
||||||
|
import javax.xml.XMLConstants;
|
||||||
|
import javax.xml.transform.Source;
|
||||||
|
import javax.xml.transform.stream.StreamSource;
|
||||||
|
import javax.xml.validation.Schema;
|
||||||
|
import javax.xml.validation.SchemaFactory;
|
||||||
|
import javax.xml.validation.Validator;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
public class SchemaBaseValidator implements IValidatorModule {
|
public class SchemaBaseValidator implements IValidatorModule {
|
||||||
public static final String RESOURCES_JAR_NOTE = "Note that as of HAPI FHIR 1.2, DSTU2 validation files are kept in a separate JAR (hapi-fhir-validation-resources-XXX.jar) which must be added to your classpath. See the HAPI FHIR download page for more information.";
|
public static final String RESOURCES_JAR_NOTE = "Note that as of HAPI FHIR 1.2, DSTU2 validation files are kept in a separate JAR (hapi-fhir-validation-resources-XXX.jar) which must be added to your classpath. See the HAPI FHIR download page for more information.";
|
||||||
@ -47,7 +53,7 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||||||
private static final Set<String> SCHEMA_NAMES;
|
private static final Set<String> SCHEMA_NAMES;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
HashSet<String> sn = new HashSet<String>();
|
HashSet<String> sn = new HashSet<>();
|
||||||
sn.add("xml.xsd");
|
sn.add("xml.xsd");
|
||||||
sn.add("xhtml1-strict.xsd");
|
sn.add("xhtml1-strict.xsd");
|
||||||
sn.add("fhir-single.xsd");
|
sn.add("fhir-single.xsd");
|
||||||
@ -59,15 +65,15 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||||||
SCHEMA_NAMES = Collections.unmodifiableSet(sn);
|
SCHEMA_NAMES = Collections.unmodifiableSet(sn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Schema> myKeyToSchema = new HashMap<String, Schema>();
|
private final Map<String, Schema> myKeyToSchema = new HashMap<>();
|
||||||
private FhirContext myCtx;
|
private FhirContext myCtx;
|
||||||
|
|
||||||
public SchemaBaseValidator(FhirContext theContext) {
|
public SchemaBaseValidator(FhirContext theContext) {
|
||||||
myCtx = theContext;
|
myCtx = theContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doValidate(IValidationContext<?> theContext, String schemaName) {
|
private void doValidate(IValidationContext<?> theContext) {
|
||||||
Schema schema = loadSchema("dstu", schemaName);
|
Schema schema = loadSchema();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Validator validator = schema.newValidator();
|
Validator validator = schema.newValidator();
|
||||||
@ -99,17 +105,14 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||||||
message.setMessage(e.getLocalizedMessage());
|
message.setMessage(e.getLocalizedMessage());
|
||||||
message.setSeverity(ResultSeverityEnum.FATAL);
|
message.setSeverity(ResultSeverityEnum.FATAL);
|
||||||
theContext.addValidationMessage(message);
|
theContext.addValidationMessage(message);
|
||||||
} catch (SAXException e) {
|
} catch (SAXException | IOException e) {
|
||||||
// Catch all
|
|
||||||
throw new ConfigurationException("Could not load/parse schema file", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
// Catch all
|
// Catch all
|
||||||
throw new ConfigurationException("Could not load/parse schema file", e);
|
throw new ConfigurationException("Could not load/parse schema file", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Schema loadSchema(String theVersion, String theSchemaName) {
|
private Schema loadSchema() {
|
||||||
String key = theVersion + "-" + theSchemaName;
|
String key = "fhir-single.xsd";
|
||||||
|
|
||||||
synchronized (myKeyToSchema) {
|
synchronized (myKeyToSchema) {
|
||||||
Schema schema = myKeyToSchema.get(key);
|
Schema schema = myKeyToSchema.get(key);
|
||||||
@ -117,7 +120,7 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
|
|
||||||
Source baseSource = loadXml(null, theSchemaName);
|
Source baseSource = loadXml("fhir-single.xsd");
|
||||||
|
|
||||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||||
schemaFactory.setResourceResolver(new MyResourceResolver());
|
schemaFactory.setResourceResolver(new MyResourceResolver());
|
||||||
@ -129,69 +132,40 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||||||
* https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
|
* https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Processing
|
||||||
*/
|
*/
|
||||||
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
schemaFactory.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
|
||||||
}catch (SAXNotRecognizedException snex){
|
} catch (SAXNotRecognizedException e) {
|
||||||
ourLog.warn("Jaxp 1.5 Support not found.",snex);
|
ourLog.warn("Jaxp 1.5 Support not found.", e);
|
||||||
}
|
}
|
||||||
schema = schemaFactory.newSchema(new Source[]{baseSource});
|
schema = schemaFactory.newSchema(new Source[]{baseSource});
|
||||||
} catch (SAXException e) {
|
} catch (SAXException e) {
|
||||||
throw new ConfigurationException("Could not load/parse schema file: " + theSchemaName, e);
|
throw new ConfigurationException("Could not load/parse schema file: " + "fhir-single.xsd", e);
|
||||||
}
|
}
|
||||||
myKeyToSchema.put(key, schema);
|
myKeyToSchema.put(key, schema);
|
||||||
return schema;
|
return schema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Source loadXml(String theSystemId, String theSchemaName) {
|
Source loadXml(String theSchemaName) {
|
||||||
String pathToBase = myCtx.getVersion().getPathToSchemaDefinitions() + '/' + theSchemaName;
|
String pathToBase = myCtx.getVersion().getPathToSchemaDefinitions() + '/' + theSchemaName;
|
||||||
ourLog.debug("Going to load resource: {}", pathToBase);
|
ourLog.debug("Going to load resource: {}", pathToBase);
|
||||||
InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase);
|
try (InputStream baseIs = FhirValidator.class.getResourceAsStream(pathToBase)) {
|
||||||
if (baseIs == null) {
|
if (baseIs == null) {
|
||||||
throw new InternalErrorException("Schema not found. " + RESOURCES_JAR_NOTE);
|
throw new InternalErrorException("Schema not found. " + RESOURCES_JAR_NOTE);
|
||||||
}
|
}
|
||||||
baseIs = new BOMInputStream(baseIs, false);
|
try (BOMInputStream bomInputStream = new BOMInputStream(baseIs, false)) {
|
||||||
InputStreamReader baseReader = new InputStreamReader(baseIs, Charset.forName("UTF-8"));
|
try (InputStreamReader baseReader = new InputStreamReader(bomInputStream, StandardCharsets.UTF_8)) {
|
||||||
Source baseSource = new StreamSource(baseReader, theSystemId);
|
// Buffer so that we can close the input stream
|
||||||
//FIXME resource leak
|
String contents = IOUtils.toString(baseReader);
|
||||||
return baseSource;
|
return new StreamSource(new StringReader(contents), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new InternalErrorException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validateResource(IValidationContext<IBaseResource> theContext) {
|
public void validateResource(IValidationContext<IBaseResource> theContext) {
|
||||||
doValidate(theContext, "fhir-single.xsd");
|
doValidate(theContext);
|
||||||
}
|
|
||||||
|
|
||||||
private static class MyErrorHandler implements org.xml.sax.ErrorHandler {
|
|
||||||
|
|
||||||
private IValidationContext<?> myContext;
|
|
||||||
|
|
||||||
public MyErrorHandler(IValidationContext<?> theContext) {
|
|
||||||
myContext = theContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addIssue(SAXParseException theException, ResultSeverityEnum theSeverity) {
|
|
||||||
SingleValidationMessage message = new SingleValidationMessage();
|
|
||||||
message.setLocationLine(theException.getLineNumber());
|
|
||||||
message.setLocationCol(theException.getColumnNumber());
|
|
||||||
message.setMessage(theException.getLocalizedMessage());
|
|
||||||
message.setSeverity(theSeverity);
|
|
||||||
myContext.addValidationMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void error(SAXParseException theException) {
|
|
||||||
addIssue(theException, ResultSeverityEnum.ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void fatalError(SAXParseException theException) {
|
|
||||||
addIssue(theException, ResultSeverityEnum.FATAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void warning(SAXParseException theException) {
|
|
||||||
addIssue(theException, ResultSeverityEnum.WARNING);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class MyResourceResolver implements LSResourceResolver {
|
private final class MyResourceResolver implements LSResourceResolver {
|
||||||
@ -225,4 +199,38 @@ public class SchemaBaseValidator implements IValidatorModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MyErrorHandler implements org.xml.sax.ErrorHandler {
|
||||||
|
|
||||||
|
private IValidationContext<?> myContext;
|
||||||
|
|
||||||
|
MyErrorHandler(IValidationContext<?> theContext) {
|
||||||
|
myContext = theContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addIssue(SAXParseException theException, ResultSeverityEnum theSeverity) {
|
||||||
|
SingleValidationMessage message = new SingleValidationMessage();
|
||||||
|
message.setLocationLine(theException.getLineNumber());
|
||||||
|
message.setLocationCol(theException.getColumnNumber());
|
||||||
|
message.setMessage(theException.getLocalizedMessage());
|
||||||
|
message.setSeverity(theSeverity);
|
||||||
|
myContext.addValidationMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(SAXParseException theException) {
|
||||||
|
addIssue(theException, ResultSeverityEnum.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fatalError(SAXParseException theException) {
|
||||||
|
addIssue(theException, ResultSeverityEnum.FATAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void warning(SAXParseException theException) {
|
||||||
|
addIssue(theException, ResultSeverityEnum.WARNING);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
@ -120,7 +121,19 @@ public class ValidationResult {
|
|||||||
location = null;
|
location = null;
|
||||||
}
|
}
|
||||||
String severity = next.getSeverity() != null ? next.getSeverity().getCode() : null;
|
String severity = next.getSeverity() != null ? next.getSeverity().getCode() : null;
|
||||||
OperationOutcomeUtil.addIssue(myCtx, theOperationOutcome, severity, next.getMessage(), location, Constants.OO_INFOSTATUS_PROCESSING);
|
IBase issue = OperationOutcomeUtil.addIssue(myCtx, theOperationOutcome, severity, next.getMessage(), location, Constants.OO_INFOSTATUS_PROCESSING);
|
||||||
|
|
||||||
|
if (next.getLocationLine() != null || next.getLocationCol() != null) {
|
||||||
|
String line = "(unknown)";
|
||||||
|
if (next.getLocationLine() != null) {
|
||||||
|
line = next.getLocationLine().toString();
|
||||||
|
}
|
||||||
|
String col = "(unknown)";
|
||||||
|
if (next.getLocationCol() != null) {
|
||||||
|
col = next.getLocationCol().toString();
|
||||||
|
}
|
||||||
|
OperationOutcomeUtil.addLocationToIssue(myCtx, issue, "Line " + line + ", Col " + col);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myMessages.isEmpty()) {
|
if (myMessages.isEmpty()) {
|
||||||
|
@ -50,14 +50,12 @@ public interface IAnyResource extends IBaseResource {
|
|||||||
|
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
@Override
|
|
||||||
IIdType getIdElement();
|
IIdType getIdElement();
|
||||||
|
|
||||||
IPrimitiveType<String> getLanguageElement();
|
IPrimitiveType<String> getLanguageElement();
|
||||||
|
|
||||||
Object getUserData(String name);
|
Object getUserData(String name);
|
||||||
|
|
||||||
@Override
|
|
||||||
IAnyResource setId(String theId);
|
IAnyResource setId(String theId);
|
||||||
|
|
||||||
void setUserData(String name, Object value);
|
void setUserData(String name, Object value);
|
||||||
|
@ -25,6 +25,8 @@ public interface IBaseBinary extends IBaseResource {
|
|||||||
|
|
||||||
byte[] getContent();
|
byte[] getContent();
|
||||||
|
|
||||||
|
IPrimitiveType<byte[]> getContentElement();
|
||||||
|
|
||||||
String getContentAsBase64();
|
String getContentAsBase64();
|
||||||
|
|
||||||
String getContentType();
|
String getContentType();
|
||||||
|
@ -44,6 +44,6 @@ public interface IBaseEnumFactory<T extends Enum<?>> extends Serializable {
|
|||||||
/**
|
/**
|
||||||
* Get the system for a given enum value
|
* Get the system for a given enum value
|
||||||
*/
|
*/
|
||||||
String toSystem(T theValue);
|
default String toSystem(T theValue) { return null; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.resourceVersionConstraintFail
|
|||||||
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.resourceIndexedCompositeStringUniqueConstraintFailure=The operation has failed with a unique index constraint failure. This probably means that the operation was trying to create/update a resource that would have resulted in a duplicate value for a unique index.
|
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.resourceIndexedCompositeStringUniqueConstraintFailure=The operation has failed with a unique index constraint failure. This probably means that the operation was trying to create/update a resource that would have resulted in a duplicate value for a unique index.
|
||||||
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.forcedIdConstraintFailure=The operation has failed with a client-assigned ID constraint failure. This typically means that multiple client threads are trying to create a new resource with the same client-assigned ID at the same time, and this thread was chosen to be rejected.
|
ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect.forcedIdConstraintFailure=The operation has failed with a client-assigned ID constraint failure. This typically means that multiple client threads are trying to create a new resource with the same client-assigned ID at the same time, and this thread was chosen to be rejected.
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.externalizedBinaryStorageExtensionFoundInRequestBody=Illegal extension found in request payload: {0}
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.externalizedBinaryStorageExtensionFoundInRequestBody=Illegal extension found in request payload - URL "{0}" and value "{1}"
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.incomingNoopInTransaction=Transaction contains resource with operation NOOP. This is only valid as a response operation, not in a request
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.incomingNoopInTransaction=Transaction contains resource with operation NOOP. This is only valid as a response operation, not in a request
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlInvalidResourceType=Invalid match URL "{0}" - Unknown resource type: "{1}"
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlInvalidResourceType=Invalid match URL "{0}" - Unknown resource type: "{1}"
|
||||||
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlNoMatches=Invalid match URL "{0}" - No resources match this search
|
ca.uhn.fhir.jpa.dao.BaseHapiFhirDao.invalidMatchUrlNoMatches=Invalid match URL "{0}" - No resources match this search
|
||||||
@ -125,9 +125,9 @@ ca.uhn.fhir.jpa.binstore.BinaryAccessProvider.unknownPath=Unable to find content
|
|||||||
ca.uhn.fhir.jpa.binstore.BinaryAccessProvider.unknownType=Content in resource of type {0} at path {1} is not appropriate for binary storage: {2}
|
ca.uhn.fhir.jpa.binstore.BinaryAccessProvider.unknownType=Content in resource of type {0} at path {1} is not appropriate for binary storage: {2}
|
||||||
|
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateCodeSystemUrl=Can not create multiple CodeSystem resources with CodeSystem.url "{0}", already have one with resource ID: {1}
|
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateCodeSystemUrl=Can not create multiple CodeSystem resources with CodeSystem.url "{0}", already have one with resource ID: {1}
|
||||||
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1}
|
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1}
|
||||||
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateValueSetUrl=Can not create multiple ValueSet resources with ValueSet.url "{0}", already have one with resource ID: {1}
|
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.cannotCreateDuplicateValueSetUrl=Can not create multiple ValueSet resources with ValueSet.url "{0}", already have one with resource ID: {1}
|
||||||
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!
|
ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!
|
||||||
|
|
||||||
ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1}
|
ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils.failedToApplyPatch=Failed to apply JSON patch to {0}: {1}
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
package ca.uhn.fhir.model.api;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class ResourceMetadataKeyEnumTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashCode() {
|
||||||
|
assertEquals(-60968467, ResourceMetadataKeyEnum.PUBLISHED.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEquals() {
|
||||||
|
assertNotEquals(ResourceMetadataKeyEnum.PROFILES, null);
|
||||||
|
assertNotEquals(ResourceMetadataKeyEnum.PROFILES, "");
|
||||||
|
assertNotEquals(ResourceMetadataKeyEnum.PROFILES, ResourceMetadataKeyEnum.PUBLISHED);
|
||||||
|
assertEquals(ResourceMetadataKeyEnum.PROFILES, ResourceMetadataKeyEnum.PROFILES);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtensionResourceEquals() {
|
||||||
|
assertNotEquals(new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://foo"), new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://bar"));
|
||||||
|
assertNotEquals(new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://foo"), null);
|
||||||
|
assertNotEquals(new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://foo"), "");
|
||||||
|
assertEquals(new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://foo"), new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://foo"));
|
||||||
|
|
||||||
|
ResourceMetadataKeyEnum.ExtensionResourceMetadataKey foo = new ResourceMetadataKeyEnum.ExtensionResourceMetadataKey("http://foo");
|
||||||
|
assertEquals(foo, foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
package ca.uhn.fhir.model.api;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class TagTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEquals() {
|
||||||
|
Tag tag1 = new Tag().setScheme("scheme").setTerm("term").setLabel("label");
|
||||||
|
Tag tag2 = new Tag().setScheme("scheme").setTerm("term").setLabel("label");
|
||||||
|
Tag tag3 = new Tag().setScheme("scheme2").setTerm("term").setLabel("label");
|
||||||
|
Tag tag4 = new Tag().setScheme("scheme").setTerm("term2").setLabel("label");
|
||||||
|
|
||||||
|
assertEquals(tag1, tag1);
|
||||||
|
assertEquals(tag1, tag2);
|
||||||
|
assertNotEquals(tag1, tag3);
|
||||||
|
assertNotEquals(tag1, tag4);
|
||||||
|
assertNotEquals(tag1, null);
|
||||||
|
assertNotEquals(tag1, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashCode() {
|
||||||
|
Tag tag1 = new Tag().setScheme("scheme").setTerm("term").setLabel("label");
|
||||||
|
assertEquals(1920714536, tag1.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstructors() throws URISyntaxException {
|
||||||
|
assertTrue(new Tag().isEmpty());
|
||||||
|
assertFalse(new Tag("http://foo").isEmpty());
|
||||||
|
assertFalse(new Tag("http://foo", "http://bar").isEmpty());
|
||||||
|
assertFalse(new Tag(new URI("http://foo"), new URI("http://bar"), "Label").isEmpty());
|
||||||
|
assertTrue(new Tag((URI)null, null, "Label").isEmpty());
|
||||||
|
|
||||||
|
assertEquals("http://foo", new Tag(new URI("http://foo"), new URI("http://bar"), "Label").getSystem());
|
||||||
|
assertEquals("http://bar", new Tag(new URI("http://foo"), new URI("http://bar"), "Label").getCode());
|
||||||
|
assertEquals("Label", new Tag(new URI("http://foo"), new URI("http://bar"), "Label").getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package ca.uhn.fhir.rest.api;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class ConstantsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConstants() {
|
||||||
|
new Constants();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,13 +2,34 @@ package ca.uhn.fhir.rest.param;
|
|||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class TokenParamTest {
|
public class TokenParamTest {
|
||||||
@Test
|
@Test
|
||||||
public void testEquals() {
|
public void testEquals() {
|
||||||
TokenParam tokenParam1 = new TokenParam("foo", "bar");
|
TokenParam tokenParam1 = new TokenParam("foo", "bar");
|
||||||
TokenParam tokenParam2 = new TokenParam("foo", "bar");
|
TokenParam tokenParam2 = new TokenParam("foo", "bar");
|
||||||
|
TokenParam tokenParam3 = new TokenParam("foo", "baz");
|
||||||
|
assertEquals(tokenParam1, tokenParam1);
|
||||||
assertEquals(tokenParam1, tokenParam2);
|
assertEquals(tokenParam1, tokenParam2);
|
||||||
|
assertNotEquals(tokenParam1, tokenParam3);
|
||||||
|
assertNotEquals(tokenParam1, null);
|
||||||
|
assertNotEquals(tokenParam1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHashCode() {
|
||||||
|
TokenParam tokenParam1 = new TokenParam("foo", "bar");
|
||||||
|
assertEquals(4716638, tokenParam1.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsEmpty() {
|
||||||
|
assertFalse(new TokenParam("foo", "bar").isEmpty());
|
||||||
|
assertTrue(new TokenParam("", "").isEmpty());
|
||||||
|
assertTrue(new TokenParam().isEmpty());
|
||||||
|
assertEquals("", new TokenParam().getValueNotNull());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
|
||||||
|
public class AsyncUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSleep() {
|
||||||
|
AsyncUtil.sleep(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSleepWithInterrupt() {
|
||||||
|
AtomicBoolean outcomeHolder = new AtomicBoolean(true);
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
boolean outcome = AsyncUtil.sleep(10000);
|
||||||
|
outcomeHolder.set(outcome);
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
thread.interrupt();
|
||||||
|
await().until(()-> outcomeHolder.get() == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAwaitLatchAndThrowInternalErrorException() {
|
||||||
|
AtomicBoolean outcomeHolder = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
AsyncUtil.awaitLatchAndThrowInternalErrorExceptionOnInterrupt(latch, 10, TimeUnit.SECONDS);
|
||||||
|
} catch (InternalErrorException e) {
|
||||||
|
outcomeHolder.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
thread.interrupt();
|
||||||
|
await().until(()-> outcomeHolder.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAwaitLatchIgnoreInterruption() {
|
||||||
|
AtomicBoolean outcomeHolder = new AtomicBoolean(true);
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
boolean outcome = AsyncUtil.awaitLatchAndIgnoreInterrupt(latch, 10, TimeUnit.SECONDS);
|
||||||
|
outcomeHolder.set(outcome);
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
thread.interrupt();
|
||||||
|
await().until(()-> outcomeHolder.get() == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ca.uhn.fhir.util;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class FileUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void formatFileSize() {
|
||||||
|
assertEquals("0 Bytes", FileUtil.formatFileSize(0).replace(",", "."));
|
||||||
|
assertEquals("1 Bytes", FileUtil.formatFileSize(1).replace(",", "."));
|
||||||
|
assertEquals("1.2 kB", FileUtil.formatFileSize(1234).replace(",", "."));
|
||||||
|
assertEquals("12.1 kB", FileUtil.formatFileSize(12345).replace(",", "."));
|
||||||
|
assertEquals("11.8 MB", FileUtil.formatFileSize(12345678).replace(",", "."));
|
||||||
|
assertEquals("103.5 GB", FileUtil.formatFileSize(111111111111L).replace(",", "."));
|
||||||
|
assertEquals("101.1 TB", FileUtil.formatFileSize(111111111111111L).replace(",", "."));
|
||||||
|
assertEquals("10105.5 TB", FileUtil.formatFileSize(11111111111111111L).replace(",", "."));
|
||||||
|
}
|
||||||
|
}
|
@ -128,6 +128,56 @@ public class StopWatchTest {
|
|||||||
assertEquals("TASK1: 500ms\nTASK2: 100ms", taskDurations);
|
assertEquals("TASK1: 500ms\nTASK2: 100ms", taskDurations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatTaskDurationsDelayBetweenTasks() {
|
||||||
|
StopWatch sw = new StopWatch();
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(1000L);
|
||||||
|
sw.startTask("TASK1");
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(1500L);
|
||||||
|
sw.endCurrentTask();
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(2000L);
|
||||||
|
sw.startTask("TASK2");
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(2100L);
|
||||||
|
sw.endCurrentTask();
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(2200L);
|
||||||
|
String taskDurations = sw.formatTaskDurations();
|
||||||
|
ourLog.info(taskDurations);
|
||||||
|
assertEquals("TASK1: 500ms\n" +
|
||||||
|
"Between: 500ms\n" +
|
||||||
|
"TASK2: 100ms\n" +
|
||||||
|
"After last task: 100ms", taskDurations);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatTaskDurationsLongDelayBeforeStart() {
|
||||||
|
StopWatch sw = new StopWatch(0);
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(1000L);
|
||||||
|
sw.startTask("TASK1");
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(1500L);
|
||||||
|
sw.startTask("TASK2");
|
||||||
|
|
||||||
|
StopWatch.setNowForUnitTestForUnitTest(1600L);
|
||||||
|
String taskDurations = sw.formatTaskDurations();
|
||||||
|
ourLog.info(taskDurations);
|
||||||
|
assertEquals("Before first task: 1000ms\nTASK1: 500ms\nTASK2: 100ms", taskDurations);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFormatTaskDurationsNoTasks() {
|
||||||
|
StopWatch sw = new StopWatch(0);
|
||||||
|
|
||||||
|
String taskDurations = sw.formatTaskDurations();
|
||||||
|
ourLog.info(taskDurations);
|
||||||
|
assertEquals("No tasks", taskDurations);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFormatThroughput60Ops4Min() {
|
public void testFormatThroughput60Ops4Min() {
|
||||||
StopWatch sw = new StopWatch(DateUtils.addMinutes(new Date(), -4));
|
StopWatch sw = new StopWatch(DateUtils.addMinutes(new Date(), -4));
|
||||||
@ -210,4 +260,34 @@ public class StopWatchTest {
|
|||||||
assertThat(string, matchesPattern("^[0-9]{3,4}ms$"));
|
assertThat(string, matchesPattern("^[0-9]{3,4}ms$"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAppendRightAlignedNumber() {
|
||||||
|
StringBuilder b= new StringBuilder();
|
||||||
|
|
||||||
|
b.setLength(0);
|
||||||
|
StopWatch.appendRightAlignedNumber(b, "PFX", 0, 100);
|
||||||
|
assertEquals("PFX100", b.toString());
|
||||||
|
|
||||||
|
b.setLength(0);
|
||||||
|
StopWatch.appendRightAlignedNumber(b, "PFX", 1, 100);
|
||||||
|
assertEquals("PFX100", b.toString());
|
||||||
|
|
||||||
|
b.setLength(0);
|
||||||
|
StopWatch.appendRightAlignedNumber(b, "PFX", 2, 100);
|
||||||
|
assertEquals("PFX100", b.toString());
|
||||||
|
|
||||||
|
b.setLength(0);
|
||||||
|
StopWatch.appendRightAlignedNumber(b, "PFX", 3, 100);
|
||||||
|
assertEquals("PFX100", b.toString());
|
||||||
|
|
||||||
|
b.setLength(0);
|
||||||
|
StopWatch.appendRightAlignedNumber(b, "PFX", 4, 100);
|
||||||
|
assertEquals("PFX0100", b.toString());
|
||||||
|
|
||||||
|
b.setLength(0);
|
||||||
|
StopWatch.appendRightAlignedNumber(b, "PFX", 10, 100);
|
||||||
|
assertEquals("PFX0000000100", b.toString());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -248,6 +248,13 @@
|
|||||||
<artifactId>jansi</artifactId>
|
<artifactId>jansi</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test Deps -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -398,7 +398,7 @@ public abstract class BaseCommand implements Comparable<BaseCommand> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected IGenericClient newClientWithBaseUrl(CommandLine theCommandLine, String theBaseUrl, String theBasicAuthOptionName, String theBearerTokenOptionName) throws ParseException {
|
protected IGenericClient newClientWithBaseUrl(CommandLine theCommandLine, String theBaseUrl, String theBasicAuthOptionName, String theBearerTokenOptionName) throws ParseException {
|
||||||
myFhirCtx.getRestfulClientFactory().setSocketTimeout(10 * 60 * 1000);
|
myFhirCtx.getRestfulClientFactory().setSocketTimeout((int) DateUtils.MILLIS_PER_HOUR);
|
||||||
IGenericClient retVal = myFhirCtx.newRestfulGenericClient(theBaseUrl);
|
IGenericClient retVal = myFhirCtx.newRestfulGenericClient(theBaseUrl);
|
||||||
|
|
||||||
String basicAuthHeaderValue = getAndParseOptionBasicAuthHeader(theCommandLine, theBasicAuthOptionName);
|
String basicAuthHeaderValue = getAndParseOptionBasicAuthHeader(theCommandLine, theBasicAuthOptionName);
|
||||||
|
@ -55,6 +55,7 @@ import org.hl7.fhir.instance.model.api.IBaseBundle;
|
|||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
@ -79,7 +80,7 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Bundle getBundleFromFileDstu2(Integer limit, File inputFile, FhirContext ctx) throws IOException, UnsupportedEncodingException {
|
private Bundle getBundleFromFileDstu2(Integer limit, File inputFile, FhirContext ctx) throws IOException {
|
||||||
|
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
|
|
||||||
@ -98,13 +99,13 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int len = 0;
|
int len;
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
while ((len = zis.read(buffer)) > 0) {
|
while ((len = zis.read(buffer)) > 0) {
|
||||||
bos.write(buffer, 0, len);
|
bos.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
byte[] exampleBytes = bos.toByteArray();
|
byte[] exampleBytes = bos.toByteArray();
|
||||||
String exampleString = new String(exampleBytes, "UTF-8");
|
String exampleString = new String(exampleBytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
if (ourLog.isTraceEnabled()) {
|
if (ourLog.isTraceEnabled()) {
|
||||||
ourLog.trace("Next example: " + exampleString);
|
ourLog.trace("Next example: " + exampleString);
|
||||||
@ -145,7 +146,7 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private org.hl7.fhir.dstu3.model.Bundle getBundleFromFileDstu3(Integer limit, File inputFile, FhirContext ctx) throws IOException, UnsupportedEncodingException {
|
private org.hl7.fhir.dstu3.model.Bundle getBundleFromFileDstu3(Integer limit, File inputFile, FhirContext ctx) throws IOException {
|
||||||
|
|
||||||
org.hl7.fhir.dstu3.model.Bundle bundle = new org.hl7.fhir.dstu3.model.Bundle();
|
org.hl7.fhir.dstu3.model.Bundle bundle = new org.hl7.fhir.dstu3.model.Bundle();
|
||||||
bundle.setType(BundleType.TRANSACTION);
|
bundle.setType(BundleType.TRANSACTION);
|
||||||
@ -168,13 +169,13 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int len = 0;
|
int len;
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
while ((len = zis.read(buffer)) > 0) {
|
while ((len = zis.read(buffer)) > 0) {
|
||||||
bos.write(buffer, 0, len);
|
bos.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
byte[] exampleBytes = bos.toByteArray();
|
byte[] exampleBytes = bos.toByteArray();
|
||||||
String exampleString = new String(exampleBytes, "UTF-8");
|
String exampleString = new String(exampleBytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
if (ourLog.isTraceEnabled()) {
|
if (ourLog.isTraceEnabled()) {
|
||||||
ourLog.trace("Next example: " + exampleString);
|
ourLog.trace("Next example: " + exampleString);
|
||||||
@ -229,7 +230,7 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private org.hl7.fhir.r4.model.Bundle getBundleFromFileR4(Integer limit, File inputFile, FhirContext ctx) throws IOException, UnsupportedEncodingException {
|
private org.hl7.fhir.r4.model.Bundle getBundleFromFileR4(Integer limit, File inputFile, FhirContext ctx) throws IOException {
|
||||||
|
|
||||||
org.hl7.fhir.r4.model.Bundle bundle = new org.hl7.fhir.r4.model.Bundle();
|
org.hl7.fhir.r4.model.Bundle bundle = new org.hl7.fhir.r4.model.Bundle();
|
||||||
bundle.setType(org.hl7.fhir.r4.model.Bundle.BundleType.TRANSACTION);
|
bundle.setType(org.hl7.fhir.r4.model.Bundle.BundleType.TRANSACTION);
|
||||||
@ -252,13 +253,13 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int len = 0;
|
int len;
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
while ((len = zis.read(buffer)) > 0) {
|
while ((len = zis.read(buffer)) > 0) {
|
||||||
bos.write(buffer, 0, len);
|
bos.write(buffer, 0, len);
|
||||||
}
|
}
|
||||||
byte[] exampleBytes = bos.toByteArray();
|
byte[] exampleBytes = bos.toByteArray();
|
||||||
String exampleString = new String(exampleBytes, "UTF-8");
|
String exampleString = new String(exampleBytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
if (ourLog.isTraceEnabled()) {
|
if (ourLog.isTraceEnabled()) {
|
||||||
ourLog.trace("Next example: " + exampleString);
|
ourLog.trace("Next example: " + exampleString);
|
||||||
@ -369,8 +370,7 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
|
|
||||||
private void processBundleDstu2(FhirContext ctx, Bundle bundle) {
|
private void processBundleDstu2(FhirContext ctx, Bundle bundle) {
|
||||||
|
|
||||||
Map<String, Integer> ids = new HashMap<String, Integer>();
|
Set<String> fullIds = new HashSet<>();
|
||||||
Set<String> fullIds = new HashSet<String>();
|
|
||||||
|
|
||||||
for (Iterator<Entry> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
for (Iterator<Entry> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
||||||
Entry next = iterator.next();
|
Entry next = iterator.next();
|
||||||
@ -397,13 +397,14 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<String> qualIds = new TreeSet<String>();
|
Set<String> qualIds = new TreeSet<>();
|
||||||
for (Iterator<Entry> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
|
||||||
Entry next = iterator.next();
|
for (Entry next : bundle.getEntry()) {
|
||||||
if (next.getResource().getId().getIdPart() != null) {
|
if (next.getResource().getId().getIdPart() != null) {
|
||||||
String nextId = next.getResource().getId().getValue();
|
String nextId = next.getResource().getId().getValue();
|
||||||
next.getRequest().setMethod(HTTPVerbEnum.PUT);
|
next.getRequest().setMethod(HTTPVerbEnum.PUT);
|
||||||
next.getRequest().setUrl(nextId);
|
next.getRequest().setUrl(nextId);
|
||||||
|
qualIds.add(nextId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,15 +450,14 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
|
|
||||||
private void processBundleDstu3(FhirContext ctx, org.hl7.fhir.dstu3.model.Bundle bundle) {
|
private void processBundleDstu3(FhirContext ctx, org.hl7.fhir.dstu3.model.Bundle bundle) {
|
||||||
|
|
||||||
Map<String, Integer> ids = new HashMap<String, Integer>();
|
Set<String> fullIds = new HashSet<>();
|
||||||
Set<String> fullIds = new HashSet<String>();
|
|
||||||
|
|
||||||
for (Iterator<BundleEntryComponent> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
for (Iterator<BundleEntryComponent> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
||||||
BundleEntryComponent next = iterator.next();
|
BundleEntryComponent next = iterator.next();
|
||||||
|
|
||||||
// DataElement have giant IDs that seem invalid, need to investigate this..
|
// DataElement have giant IDs that seem invalid, need to investigate this..
|
||||||
if ("Subscription".equals(next.getResource().getResourceType()) || "DataElement".equals(next.getResource().getResourceType())
|
if ("Subscription".equals(next.getResource().getResourceType().name()) || "DataElement".equals(next.getResource().getResourceType().name())
|
||||||
|| "OperationOutcome".equals(next.getResource().getResourceType()) || "OperationDefinition".equals(next.getResource().getResourceType())) {
|
|| "OperationOutcome".equals(next.getResource().getResourceType().name()) || "OperationDefinition".equals(next.getResource().getResourceType().name())) {
|
||||||
ourLog.info("Skipping " + next.getResource().getResourceType() + " example");
|
ourLog.info("Skipping " + next.getResource().getResourceType() + " example");
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else {
|
} else {
|
||||||
@ -477,13 +477,13 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<String> qualIds = new TreeSet<String>();
|
Set<String> qualIds = new TreeSet<>();
|
||||||
for (Iterator<BundleEntryComponent> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
for (BundleEntryComponent next : bundle.getEntry()) {
|
||||||
BundleEntryComponent next = iterator.next();
|
|
||||||
if (next.getResource().getIdElement().getIdPart() != null) {
|
if (next.getResource().getIdElement().getIdPart() != null) {
|
||||||
String nextId = next.getResource().getIdElement().getValue();
|
String nextId = next.getResource().getIdElement().getValue();
|
||||||
next.getRequest().setMethod(HTTPVerb.PUT);
|
next.getRequest().setMethod(HTTPVerb.PUT);
|
||||||
next.getRequest().setUrl(nextId);
|
next.getRequest().setUrl(nextId);
|
||||||
|
qualIds.add(nextId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,15 +529,14 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
|
|
||||||
private void processBundleR4(FhirContext ctx, org.hl7.fhir.r4.model.Bundle bundle) {
|
private void processBundleR4(FhirContext ctx, org.hl7.fhir.r4.model.Bundle bundle) {
|
||||||
|
|
||||||
Map<String, Integer> ids = new HashMap<String, Integer>();
|
Set<String> fullIds = new HashSet<>();
|
||||||
Set<String> fullIds = new HashSet<String>();
|
|
||||||
|
|
||||||
for (Iterator<org.hl7.fhir.r4.model.Bundle.BundleEntryComponent> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
for (Iterator<org.hl7.fhir.r4.model.Bundle.BundleEntryComponent> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
||||||
org.hl7.fhir.r4.model.Bundle.BundleEntryComponent next = iterator.next();
|
org.hl7.fhir.r4.model.Bundle.BundleEntryComponent next = iterator.next();
|
||||||
|
|
||||||
// DataElement have giant IDs that seem invalid, need to investigate this..
|
// DataElement have giant IDs that seem invalid, need to investigate this..
|
||||||
if ("Subscription".equals(next.getResource().getResourceType()) || "DataElement".equals(next.getResource().getResourceType())
|
if ("Subscription".equals(next.getResource().getResourceType().name()) || "DataElement".equals(next.getResource().getResourceType().name())
|
||||||
|| "OperationOutcome".equals(next.getResource().getResourceType()) || "OperationDefinition".equals(next.getResource().getResourceType())) {
|
|| "OperationOutcome".equals(next.getResource().getResourceType().name()) || "OperationDefinition".equals(next.getResource().getResourceType().name())) {
|
||||||
ourLog.info("Skipping " + next.getResource().getResourceType() + " example");
|
ourLog.info("Skipping " + next.getResource().getResourceType() + " example");
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
} else {
|
} else {
|
||||||
@ -557,13 +556,13 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Set<String> qualIds = new TreeSet<String>();
|
Set<String> qualIds = new TreeSet<>();
|
||||||
for (Iterator<org.hl7.fhir.r4.model.Bundle.BundleEntryComponent> iterator = bundle.getEntry().iterator(); iterator.hasNext(); ) {
|
for (org.hl7.fhir.r4.model.Bundle.BundleEntryComponent next : bundle.getEntry()) {
|
||||||
org.hl7.fhir.r4.model.Bundle.BundleEntryComponent next = iterator.next();
|
|
||||||
if (next.getResource().getIdElement().getIdPart() != null) {
|
if (next.getResource().getIdElement().getIdPart() != null) {
|
||||||
String nextId = next.getResource().getIdElement().getValue();
|
String nextId = next.getResource().getIdElement().getValue();
|
||||||
next.getRequest().setMethod(org.hl7.fhir.r4.model.Bundle.HTTPVerb.PUT);
|
next.getRequest().setMethod(org.hl7.fhir.r4.model.Bundle.HTTPVerb.PUT);
|
||||||
next.getRequest().setUrl(nextId);
|
next.getRequest().setUrl(nextId);
|
||||||
|
qualIds.add(nextId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,7 +634,7 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
|
|
||||||
boolean cacheFile = theCommandLine.hasOption('c');
|
boolean cacheFile = theCommandLine.hasOption('c');
|
||||||
|
|
||||||
Collection<File> inputFiles = null;
|
Collection<File> inputFiles;
|
||||||
try {
|
try {
|
||||||
inputFiles = loadFile(specUrl, filepath, cacheFile);
|
inputFiles = loadFile(specUrl, filepath, cacheFile);
|
||||||
for (File inputFile : inputFiles) {
|
for (File inputFile : inputFiles) {
|
||||||
@ -694,13 +693,11 @@ public class ExampleDataUploader extends BaseCommand {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean found = false;
|
|
||||||
for (int j = 0; j < resources.size(); j++) {
|
for (int j = 0; j < resources.size(); j++) {
|
||||||
String candidateTarget = resources.get(j).getIdElement().getValue();
|
String candidateTarget = resources.get(j).getIdElement().getValue();
|
||||||
if (isNotBlank(nextTarget) && nextTarget.equals(candidateTarget)) {
|
if (isNotBlank(nextTarget) && nextTarget.equals(candidateTarget)) {
|
||||||
ourLog.info("Reflexively adding resource {} to bundle as it is a reference target", nextTarget);
|
ourLog.info("Reflexively adding resource {} to bundle as it is a reference target", nextTarget);
|
||||||
subResourceList.add(resources.remove(j));
|
subResourceList.add(resources.remove(j));
|
||||||
found = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,16 +123,13 @@ public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConcept
|
|||||||
private void convertConceptMapToCsv(ConceptMap theConceptMap) {
|
private void convertConceptMapToCsv(ConceptMap theConceptMap) {
|
||||||
Path path = Paths.get(file);
|
Path path = Paths.get(file);
|
||||||
ourLog.info("Exporting ConceptMap to CSV: {}", path);
|
ourLog.info("Exporting ConceptMap to CSV: {}", path);
|
||||||
try (
|
try (Writer writer = Files.newBufferedWriter(path)) {
|
||||||
Writer writer = Files.newBufferedWriter(path);
|
|
||||||
CSVPrinter csvPrinter = new CSVPrinter(
|
CSVFormat format = CSVFormat.DEFAULT
|
||||||
writer,
|
|
||||||
CSVFormat
|
|
||||||
.DEFAULT
|
|
||||||
.withRecordSeparator("\n")
|
.withRecordSeparator("\n")
|
||||||
.withHeader(Header.class)
|
.withHeader(Header.class)
|
||||||
.withQuoteMode(QuoteMode.ALL));
|
.withQuoteMode(QuoteMode.ALL);
|
||||||
) {
|
try (CSVPrinter csvPrinter = new CSVPrinter(writer, format)) {
|
||||||
for (ConceptMapGroupComponent group : theConceptMap.getGroup()) {
|
for (ConceptMapGroupComponent group : theConceptMap.getGroup()) {
|
||||||
for (SourceElementComponent element : group.getElement()) {
|
for (SourceElementComponent element : group.getElement()) {
|
||||||
for (ConceptMap.TargetElementComponent target : element.getTarget()) {
|
for (ConceptMap.TargetElementComponent target : element.getTarget()) {
|
||||||
@ -153,6 +150,8 @@ public class ExportConceptMapToCsvCommand extends AbstractImportExportCsvConcept
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
csvPrinter.flush();
|
||||||
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw new InternalErrorException(ioe);
|
throw new InternalErrorException(ioe);
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ public class LoadingValidationSupportDstu3 implements IValidationSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ public class LoadingValidationSupportR4 implements org.hl7.fhir.r4.hapi.ctx.IVal
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSet) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ package ca.uhn.fhir.cli;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
@ -44,7 +44,7 @@ public class ToggleSearchParametersCommand extends BaseCommand {
|
|||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
addFhirVersionOption(options);
|
addFhirVersionOption(options);
|
||||||
addBaseUrlOption(options);
|
addBaseUrlOption(options);
|
||||||
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + IHapiTerminologyLoaderSvc.SCT_URI + ")");
|
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + ITermLoaderSvc.SCT_URI + ")");
|
||||||
addBasicAuthOption(options);
|
addBasicAuthOption(options);
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
@ -20,30 +20,48 @@ package ca.uhn.fhir.cli;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||||
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.util.AttachmentUtil;
|
||||||
|
import ca.uhn.fhir.util.FileUtil;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
import org.apache.commons.cli.CommandLine;
|
import org.apache.commons.cli.CommandLine;
|
||||||
import org.apache.commons.cli.Options;
|
import org.apache.commons.cli.Options;
|
||||||
import org.apache.commons.cli.ParseException;
|
import org.apache.commons.cli.ParseException;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.io.input.CountingInputStream;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
public class UploadTerminologyCommand extends BaseCommand {
|
public class UploadTerminologyCommand extends BaseCommand {
|
||||||
// TODO: Don't use qualified names for loggers in HAPI CLI.
|
static final String UPLOAD_TERMINOLOGY = "upload-terminology";
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UploadTerminologyCommand.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UploadTerminologyCommand.class);
|
||||||
private static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "upload-external-code-system";
|
private static final long DEFAULT_TRANSFER_SIZE_LIMIT = 10 * FileUtils.ONE_MB;
|
||||||
|
private static long ourTransferSizeLimit = DEFAULT_TRANSFER_SIZE_LIMIT;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandDescription() {
|
public String getCommandDescription() {
|
||||||
return "Uploads a terminology package (e.g. a SNOMED CT ZIP file) to a server, using the $" + UPLOAD_EXTERNAL_CODE_SYSTEM + " operation.";
|
return "Uploads a terminology package (e.g. a SNOMED CT ZIP file or a custom terminology bundle) to a server, using the appropriate operation.";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandName() {
|
public String getCommandName() {
|
||||||
return "upload-terminology";
|
return UPLOAD_TERMINOLOGY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -52,9 +70,9 @@ public class UploadTerminologyCommand extends BaseCommand {
|
|||||||
|
|
||||||
addFhirVersionOption(options);
|
addFhirVersionOption(options);
|
||||||
addBaseUrlOption(options);
|
addBaseUrlOption(options);
|
||||||
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + IHapiTerminologyLoaderSvc.SCT_URI + ")");
|
addRequiredOption(options, "u", "url", true, "The code system URL associated with this upload (e.g. " + ITermLoaderSvc.SCT_URI + ")");
|
||||||
addOptionalOption(options, "d", "data", true, "Local file to use to upload (can be a raw file or a ZIP containing the raw file)");
|
addOptionalOption(options, "d", "data", true, "Local file to use to upload (can be a raw file or a ZIP containing the raw file)");
|
||||||
addOptionalOption(options, null, "custom", false, "Indicates that this upload uses the HAPI FHIR custom external terminology format");
|
addOptionalOption(options, "m", "mode", true, "The upload mode: SNAPSHOT (default), ADD, REMOVE");
|
||||||
addBasicAuthOption(options);
|
addBasicAuthOption(options);
|
||||||
addVerboseLoggingOption(options);
|
addVerboseLoggingOption(options);
|
||||||
|
|
||||||
@ -65,6 +83,14 @@ public class UploadTerminologyCommand extends BaseCommand {
|
|||||||
public void run(CommandLine theCommandLine) throws ParseException {
|
public void run(CommandLine theCommandLine) throws ParseException {
|
||||||
parseFhirContext(theCommandLine);
|
parseFhirContext(theCommandLine);
|
||||||
|
|
||||||
|
ModeEnum mode;
|
||||||
|
String modeString = theCommandLine.getOptionValue("m", "SNAPSHOT");
|
||||||
|
try {
|
||||||
|
mode = ModeEnum.valueOf(modeString);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new ParseException("Invalid mode: " + modeString);
|
||||||
|
}
|
||||||
|
|
||||||
String termUrl = theCommandLine.getOptionValue("u");
|
String termUrl = theCommandLine.getOptionValue("u");
|
||||||
if (isBlank(termUrl)) {
|
if (isBlank(termUrl)) {
|
||||||
throw new ParseException("No URL provided");
|
throw new ParseException("No URL provided");
|
||||||
@ -77,29 +103,166 @@ public class UploadTerminologyCommand extends BaseCommand {
|
|||||||
|
|
||||||
IGenericClient client = super.newClient(theCommandLine);
|
IGenericClient client = super.newClient(theCommandLine);
|
||||||
IBaseParameters inputParameters = ParametersUtil.newInstance(myFhirCtx);
|
IBaseParameters inputParameters = ParametersUtil.newInstance(myFhirCtx);
|
||||||
ParametersUtil.addParameterToParametersUri(myFhirCtx, inputParameters, "url", termUrl);
|
|
||||||
for (String next : datafile) {
|
|
||||||
ParametersUtil.addParameterToParametersString(myFhirCtx, inputParameters, "localfile", next);
|
|
||||||
}
|
|
||||||
if (theCommandLine.hasOption("custom")) {
|
|
||||||
ParametersUtil.addParameterToParametersCode(myFhirCtx, inputParameters, "contentMode", "custom");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theCommandLine.hasOption(VERBOSE_LOGGING_PARAM)) {
|
if (theCommandLine.hasOption(VERBOSE_LOGGING_PARAM)) {
|
||||||
client.registerInterceptor(new LoggingInterceptor(true));
|
client.registerInterceptor(new LoggingInterceptor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case SNAPSHOT:
|
||||||
|
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_UPLOAD_EXTERNAL_CODE_SYSTEM);
|
||||||
|
break;
|
||||||
|
case ADD:
|
||||||
|
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_ADD);
|
||||||
|
break;
|
||||||
|
case REMOVE:
|
||||||
|
invokeOperation(termUrl, datafile, client, inputParameters, JpaConstants.OPERATION_APPLY_CODESYSTEM_DELTA_REMOVE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeOperation(String theTermUrl, String[] theDatafile, IGenericClient theClient, IBaseParameters theInputParameters, String theOperationName) throws ParseException {
|
||||||
|
ParametersUtil.addParameterToParametersUri(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_SYSTEM, theTermUrl);
|
||||||
|
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);
|
||||||
|
int compressedSourceBytesCount = 0;
|
||||||
|
int compressedFileCount = 0;
|
||||||
|
boolean haveCompressedContents = false;
|
||||||
|
try {
|
||||||
|
for (String nextDataFile : theDatafile) {
|
||||||
|
|
||||||
|
try (FileInputStream fileInputStream = new FileInputStream(nextDataFile)) {
|
||||||
|
if (nextDataFile.endsWith(".csv")) {
|
||||||
|
|
||||||
|
ourLog.info("Compressing and adding file: {}", nextDataFile);
|
||||||
|
ZipEntry nextEntry = new ZipEntry(stripPath(nextDataFile));
|
||||||
|
zipOutputStream.putNextEntry(nextEntry);
|
||||||
|
|
||||||
|
CountingInputStream countingInputStream = new CountingInputStream(fileInputStream);
|
||||||
|
IOUtils.copy(countingInputStream, zipOutputStream);
|
||||||
|
haveCompressedContents = true;
|
||||||
|
compressedSourceBytesCount += countingInputStream.getCount();
|
||||||
|
|
||||||
|
zipOutputStream.flush();
|
||||||
|
ourLog.info("Finished compressing {}", nextDataFile);
|
||||||
|
|
||||||
|
} else if (nextDataFile.endsWith(".zip")) {
|
||||||
|
|
||||||
|
ourLog.info("Adding ZIP file: {}", nextDataFile);
|
||||||
|
String fileName = "file:" + nextDataFile;
|
||||||
|
addFileToRequestBundle(theInputParameters, fileName, IOUtils.toByteArray(fileInputStream));
|
||||||
|
|
||||||
|
} else if (nextDataFile.endsWith(".json") || nextDataFile.endsWith(".xml")) {
|
||||||
|
|
||||||
|
ourLog.info("Adding CodeSystem resource file: {}", nextDataFile);
|
||||||
|
|
||||||
|
String contents = IOUtils.toString(fileInputStream, Charsets.UTF_8);
|
||||||
|
EncodingEnum encoding = EncodingEnum.detectEncodingNoDefault(contents);
|
||||||
|
if (encoding == null) {
|
||||||
|
throw new ParseException("Could not detect FHIR encoding for file: " + nextDataFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeSystem resource = encoding.newParser(myFhirCtx).parseResource(CodeSystem.class, contents);
|
||||||
|
ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_CODESYSTEM, resource);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
throw new ParseException("Don't know how to handle file: " + nextDataFile);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
zipOutputStream.flush();
|
||||||
|
zipOutputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ParseException(e.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haveCompressedContents) {
|
||||||
|
byte[] compressedBytes = byteArrayOutputStream.toByteArray();
|
||||||
|
ourLog.info("Compressed {} bytes in {} file(s) into {} bytes", FileUtil.formatFileSize(compressedSourceBytesCount), compressedFileCount, FileUtil.formatFileSize(compressedBytes.length));
|
||||||
|
|
||||||
|
addFileToRequestBundle(theInputParameters, "file:/files.zip", compressedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
ourLog.info("Beginning upload - This may take a while...");
|
ourLog.info("Beginning upload - This may take a while...");
|
||||||
|
|
||||||
IBaseParameters response = client
|
if (ourLog.isDebugEnabled() || "true".equals(System.getProperty("test"))) {
|
||||||
|
ourLog.info("Submitting parameters: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theInputParameters));
|
||||||
|
}
|
||||||
|
|
||||||
|
IBaseParameters response;
|
||||||
|
try {
|
||||||
|
response = theClient
|
||||||
.operation()
|
.operation()
|
||||||
.onType(myFhirCtx.getResourceDefinition("CodeSystem").getImplementingClass())
|
.onType(myFhirCtx.getResourceDefinition("CodeSystem").getImplementingClass())
|
||||||
.named(UPLOAD_EXTERNAL_CODE_SYSTEM)
|
.named(theOperationName)
|
||||||
.withParameters(inputParameters)
|
.withParameters(theInputParameters)
|
||||||
.execute();
|
.execute();
|
||||||
|
} catch (BaseServerResponseException e) {
|
||||||
|
if (e.getOperationOutcome() != null) {
|
||||||
|
ourLog.error("Received the following response:\n{}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ourLog.info("Upload complete!");
|
ourLog.info("Upload complete!");
|
||||||
ourLog.info("Response:\n{}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
|
ourLog.info("Response:\n{}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addFileToRequestBundle(IBaseParameters theInputParameters, String theFileName, byte[] theBytes) {
|
||||||
|
|
||||||
|
byte[] bytes = theBytes;
|
||||||
|
String fileName = theFileName;
|
||||||
|
|
||||||
|
if (bytes.length > ourTransferSizeLimit) {
|
||||||
|
ourLog.info("File size is greater than {} - Going to use a local file reference instead of a direct HTTP transfer. Note that this will only work when executing this command on the same server as the FHIR server itself.", FileUtil.formatFileSize(ourTransferSizeLimit));
|
||||||
|
|
||||||
|
String suffix = fileName.substring(fileName.lastIndexOf("."));
|
||||||
|
try {
|
||||||
|
File tempFile = File.createTempFile("hapi-fhir-cli", suffix);
|
||||||
|
tempFile.deleteOnExit();
|
||||||
|
try (OutputStream fileOutputStream = new FileOutputStream(tempFile, false)) {
|
||||||
|
fileOutputStream.write(bytes);
|
||||||
|
bytes = null;
|
||||||
|
fileName = "localfile:" + tempFile.getAbsolutePath();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CommandFailureException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ICompositeType attachment = AttachmentUtil.newInstance(myFhirCtx);
|
||||||
|
AttachmentUtil.setUrl(myFhirCtx, attachment, fileName);
|
||||||
|
if (bytes != null) {
|
||||||
|
AttachmentUtil.setData(myFhirCtx, attachment, bytes);
|
||||||
|
}
|
||||||
|
ParametersUtil.addParameterToParameters(myFhirCtx, theInputParameters, TerminologyUploaderProvider.PARAM_FILE, attachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ModeEnum {
|
||||||
|
SNAPSHOT, ADD, REMOVE
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static void setTransferSizeLimitForUnitTest(long theTransferSizeLimit) {
|
||||||
|
if (theTransferSizeLimit <= 0) {
|
||||||
|
ourTransferSizeLimit = DEFAULT_TRANSFER_SIZE_LIMIT;
|
||||||
|
}else {
|
||||||
|
ourTransferSizeLimit = theTransferSizeLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String stripPath(String thePath) {
|
||||||
|
String retVal = thePath;
|
||||||
|
if (retVal.contains("/")) {
|
||||||
|
retVal = retVal.substring(retVal.lastIndexOf("/"));
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,13 @@
|
|||||||
<!--
|
<!--
|
||||||
It's useful to have this log when uploading big terminologies
|
It's useful to have this log when uploading big terminologies
|
||||||
-->
|
-->
|
||||||
<logger name="ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl" additivity="false" level="info">
|
<logger name="ca.uhn.fhir.jpa.term.BaseTermReadSvcImpl" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="ca.uhn.fhir.jpa.term.TermCodeSystemStorageSvcImpl" additivity="false" level="info">
|
||||||
|
<appender-ref ref="STDOUT" />
|
||||||
|
</logger>
|
||||||
|
<logger name="ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl" additivity="false" level="info">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
import ca.uhn.fhir.test.utilities.LoggingRule;
|
import ca.uhn.fhir.test.utilities.LoggingRule;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
@ -24,10 +25,9 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
|
||||||
|
|
||||||
public class ExportConceptMapToCsvCommandDstu3Test {
|
public class ExportConceptMapToCsvCommandDstu3Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExportConceptMapToCsvCommandDstu3Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExportConceptMapToCsvCommandDstu3Test.class);
|
||||||
private static final String CM_URL = "http://example.com/conceptmap";
|
private static final String CM_URL = "http://example.com/conceptmap";
|
||||||
@ -36,7 +36,7 @@ public class ExportConceptMapToCsvCommandDstu3Test {
|
|||||||
private static final String CS_URL_1 = "http://example.com/codesystem/1";
|
private static final String CS_URL_1 = "http://example.com/codesystem/1";
|
||||||
private static final String CS_URL_2 = "http://example.com/codesystem/2";
|
private static final String CS_URL_2 = "http://example.com/codesystem/2";
|
||||||
private static final String CS_URL_3 = "http://example.com/codesystem/3";
|
private static final String CS_URL_3 = "http://example.com/codesystem/3";
|
||||||
private static final String FILE = "./target/output.csv";
|
private static final String FILE = "./target/output_dstu3.csv";
|
||||||
|
|
||||||
private static String ourBase;
|
private static String ourBase;
|
||||||
private static IGenericClient ourClient;
|
private static IGenericClient ourClient;
|
||||||
@ -44,13 +44,47 @@ public class ExportConceptMapToCsvCommandDstu3Test {
|
|||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
private static String ourVersion = "dstu3";
|
private static String ourVersion = "dstu3";
|
||||||
@Rule
|
|
||||||
public LoggingRule myLoggingRule = new LoggingRule();
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
System.setProperty("test", "true");
|
System.setProperty("test", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public LoggingRule myLoggingRule = new LoggingRule();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExportConceptMapToCsvCommand() throws IOException {
|
||||||
|
ourLog.debug("ConceptMap:\n" + ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createConceptMap()));
|
||||||
|
|
||||||
|
App.main(new String[]{"export-conceptmap-to-csv",
|
||||||
|
"-v", ourVersion,
|
||||||
|
"-t", ourBase,
|
||||||
|
"-u", CM_URL,
|
||||||
|
"-f", FILE,
|
||||||
|
"-l"});
|
||||||
|
await().until(() -> new File(FILE).exists());
|
||||||
|
|
||||||
|
String expected = "\"SOURCE_CODE_SYSTEM\",\"SOURCE_CODE_SYSTEM_VERSION\",\"TARGET_CODE_SYSTEM\",\"TARGET_CODE_SYSTEM_VERSION\",\"SOURCE_CODE\",\"SOURCE_DISPLAY\",\"TARGET_CODE\",\"TARGET_DISPLAY\",\"EQUIVALENCE\",\"COMMENT\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1a\",\"Display 1a\",\"Code 2a\",\"Display 2a\",\"equal\",\"2a This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1b\",\"Display 1b\",\"Code 2b\",\"Display 2b\",\"equal\",\"2b This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1c\",\"Display 1c\",\"Code 2c\",\"Display 2c\",\"equal\",\"2c This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1d\",\"Display 1d\",\"Code 2d\",\"Display 2d\",\"equal\",\"2d This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1a\",\"Display 1a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1b\",\"Display 1b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1c\",\"Display 1c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1d\",\"Display 1d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2a\",\"Display 2a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2b\",\"Display 2b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2c\",\"Display 2c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2d\",\"Display 2d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n";
|
||||||
|
|
||||||
|
ourLog.info("Going to read file: {}", FILE);
|
||||||
|
String result = IOUtils.toString(new FileInputStream(FILE), Charsets.UTF_8);
|
||||||
|
assertEquals(expected, result);
|
||||||
|
|
||||||
|
FileUtils.deleteQuietly(new File(FILE));
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClassClearContext() throws Exception {
|
public static void afterClassClearContext() throws Exception {
|
||||||
JettyUtil.closeServer(ourServer);
|
JettyUtil.closeServer(ourServer);
|
||||||
@ -81,38 +115,6 @@ public class ExportConceptMapToCsvCommandDstu3Test {
|
|||||||
ourClient.create().resource(createConceptMap()).execute();
|
ourClient.create().resource(createConceptMap()).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExportConceptMapToCsvCommand() throws IOException {
|
|
||||||
ourLog.debug("ConceptMap:\n" + ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createConceptMap()));
|
|
||||||
|
|
||||||
App.main(new String[] {"export-conceptmap-to-csv",
|
|
||||||
"-v", ourVersion,
|
|
||||||
"-t", ourBase,
|
|
||||||
"-u", CM_URL,
|
|
||||||
"-f", FILE,
|
|
||||||
"-l"});
|
|
||||||
|
|
||||||
String expected = "\"SOURCE_CODE_SYSTEM\",\"SOURCE_CODE_SYSTEM_VERSION\",\"TARGET_CODE_SYSTEM\",\"TARGET_CODE_SYSTEM_VERSION\",\"SOURCE_CODE\",\"SOURCE_DISPLAY\",\"TARGET_CODE\",\"TARGET_DISPLAY\",\"EQUIVALENCE\",\"COMMENT\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1a\",\"Display 1a\",\"Code 2a\",\"Display 2a\",\"equal\",\"2a This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1b\",\"Display 1b\",\"Code 2b\",\"Display 2b\",\"equal\",\"2b This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1c\",\"Display 1c\",\"Code 2c\",\"Display 2c\",\"equal\",\"2c This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1d\",\"Display 1d\",\"Code 2d\",\"Display 2d\",\"equal\",\"2d This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1a\",\"Display 1a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1b\",\"Display 1b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1c\",\"Display 1c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1d\",\"Display 1d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2a\",\"Display 2a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2b\",\"Display 2b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2c\",\"Display 2c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2d\",\"Display 2d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n";
|
|
||||||
|
|
||||||
ourLog.info("Going to read file: {}", FILE);
|
|
||||||
String result = IOUtils.toString(new FileInputStream(FILE), Charsets.UTF_8);
|
|
||||||
assertEquals(expected, result);
|
|
||||||
|
|
||||||
FileUtils.deleteQuietly(new File(FILE));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConceptMap createConceptMap() {
|
static ConceptMap createConceptMap() {
|
||||||
ConceptMap conceptMap = new ConceptMap();
|
ConceptMap conceptMap = new ConceptMap();
|
||||||
conceptMap
|
conceptMap
|
||||||
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
|
|||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.VerboseLoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
import ca.uhn.fhir.util.TestUtil;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
@ -22,10 +23,9 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
|
||||||
|
|
||||||
public class ExportConceptMapToCsvCommandR4Test {
|
public class ExportConceptMapToCsvCommandR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExportConceptMapToCsvCommandR4Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExportConceptMapToCsvCommandR4Test.class);
|
||||||
private static final String CM_URL = "http://example.com/conceptmap";
|
private static final String CM_URL = "http://example.com/conceptmap";
|
||||||
@ -34,7 +34,7 @@ public class ExportConceptMapToCsvCommandR4Test {
|
|||||||
private static final String CS_URL_1 = "http://example.com/codesystem/1";
|
private static final String CS_URL_1 = "http://example.com/codesystem/1";
|
||||||
private static final String CS_URL_2 = "http://example.com/codesystem/2";
|
private static final String CS_URL_2 = "http://example.com/codesystem/2";
|
||||||
private static final String CS_URL_3 = "http://example.com/codesystem/3";
|
private static final String CS_URL_3 = "http://example.com/codesystem/3";
|
||||||
private static final String FILE = new File("./target/output.csv").getAbsolutePath();
|
private static final String FILE = new File("./target/output_r4.csv").getAbsolutePath();
|
||||||
|
|
||||||
private static String ourBase;
|
private static String ourBase;
|
||||||
private static IGenericClient ourClient;
|
private static IGenericClient ourClient;
|
||||||
@ -47,6 +47,37 @@ public class ExportConceptMapToCsvCommandR4Test {
|
|||||||
System.setProperty("test", "true");
|
System.setProperty("test", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExportConceptMapToCsvCommand() throws IOException {
|
||||||
|
ourLog.info("ConceptMap:\n" + ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createConceptMap()));
|
||||||
|
|
||||||
|
App.main(new String[]{"export-conceptmap-to-csv",
|
||||||
|
"-v", ourVersion,
|
||||||
|
"-t", ourBase,
|
||||||
|
"-u", CM_URL,
|
||||||
|
"-f", FILE,
|
||||||
|
"-l"});
|
||||||
|
await().until(() -> new File(FILE).exists());
|
||||||
|
|
||||||
|
String expected = "\"SOURCE_CODE_SYSTEM\",\"SOURCE_CODE_SYSTEM_VERSION\",\"TARGET_CODE_SYSTEM\",\"TARGET_CODE_SYSTEM_VERSION\",\"SOURCE_CODE\",\"SOURCE_DISPLAY\",\"TARGET_CODE\",\"TARGET_DISPLAY\",\"EQUIVALENCE\",\"COMMENT\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1a\",\"Display 1a\",\"Code 2a\",\"Display 2a\",\"equal\",\"2a This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1b\",\"Display 1b\",\"Code 2b\",\"Display 2b\",\"equal\",\"2b This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1c\",\"Display 1c\",\"Code 2c\",\"Display 2c\",\"equal\",\"2c This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1d\",\"Display 1d\",\"Code 2d\",\"Display 2d\",\"equal\",\"2d This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1a\",\"Display 1a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1b\",\"Display 1b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1c\",\"Display 1c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1d\",\"Display 1d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2a\",\"Display 2a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2b\",\"Display 2b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2c\",\"Display 2c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
||||||
|
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2d\",\"Display 2d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n";
|
||||||
|
String result = IOUtils.toString(new FileInputStream(FILE), Charsets.UTF_8);
|
||||||
|
assertEquals(expected, result);
|
||||||
|
|
||||||
|
FileUtils.deleteQuietly(new File(FILE));
|
||||||
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterClassClearContext() throws Exception {
|
public static void afterClassClearContext() throws Exception {
|
||||||
JettyUtil.closeServer(ourServer);
|
JettyUtil.closeServer(ourServer);
|
||||||
@ -77,36 +108,6 @@ public class ExportConceptMapToCsvCommandR4Test {
|
|||||||
ourClient.create().resource(createConceptMap()).execute();
|
ourClient.create().resource(createConceptMap()).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testExportConceptMapToCsvCommand() throws IOException {
|
|
||||||
ourLog.info("ConceptMap:\n" + ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(createConceptMap()));
|
|
||||||
|
|
||||||
App.main(new String[] {"export-conceptmap-to-csv",
|
|
||||||
"-v", ourVersion,
|
|
||||||
"-t", ourBase,
|
|
||||||
"-u", CM_URL,
|
|
||||||
"-f", FILE,
|
|
||||||
"-l"});
|
|
||||||
|
|
||||||
String expected = "\"SOURCE_CODE_SYSTEM\",\"SOURCE_CODE_SYSTEM_VERSION\",\"TARGET_CODE_SYSTEM\",\"TARGET_CODE_SYSTEM_VERSION\",\"SOURCE_CODE\",\"SOURCE_DISPLAY\",\"TARGET_CODE\",\"TARGET_DISPLAY\",\"EQUIVALENCE\",\"COMMENT\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1a\",\"Display 1a\",\"Code 2a\",\"Display 2a\",\"equal\",\"2a This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1b\",\"Display 1b\",\"Code 2b\",\"Display 2b\",\"equal\",\"2b This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1c\",\"Display 1c\",\"Code 2c\",\"Display 2c\",\"equal\",\"2c This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/2\",\"Version 2t\",\"Code 1d\",\"Display 1d\",\"Code 2d\",\"Display 2d\",\"equal\",\"2d This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1a\",\"Display 1a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1b\",\"Display 1b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1c\",\"Display 1c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/1\",\"Version 1s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 1d\",\"Display 1d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2a\",\"Display 2a\",\"Code 3a\",\"Display 3a\",\"equal\",\"3a This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2b\",\"Display 2b\",\"Code 3b\",\"Display 3b\",\"equal\",\"3b This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2c\",\"Display 2c\",\"Code 3c\",\"Display 3c\",\"equal\",\"3c This is a comment.\"\n" +
|
|
||||||
"\"http://example.com/codesystem/2\",\"Version 2s\",\"http://example.com/codesystem/3\",\"Version 3t\",\"Code 2d\",\"Display 2d\",\"Code 3d\",\"Display 3d\",\"equal\",\"3d This is a comment.\"\n";
|
|
||||||
String result = IOUtils.toString(new FileInputStream(FILE), Charsets.UTF_8);
|
|
||||||
assertEquals(expected, result);
|
|
||||||
|
|
||||||
FileUtils.deleteQuietly(new File(FILE));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConceptMap createConceptMap() {
|
static ConceptMap createConceptMap() {
|
||||||
ConceptMap conceptMap = new ConceptMap();
|
ConceptMap conceptMap = new ConceptMap();
|
||||||
conceptMap
|
conceptMap
|
||||||
|
@ -0,0 +1,367 @@
|
|||||||
|
package ca.uhn.fhir.cli;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.test.BaseTest;
|
||||||
|
import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider;
|
||||||
|
import ca.uhn.fhir.jpa.term.UploadStatistics;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
|
import com.google.common.base.Charsets;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.hamcrest.Matchers;
|
||||||
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
|
import static org.hamcrest.Matchers.matchesPattern;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class UploadTerminologyCommandTest extends BaseTest {
|
||||||
|
|
||||||
|
static {
|
||||||
|
System.setProperty("test", "true");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Server myServer;
|
||||||
|
private FhirContext myCtx = FhirContext.forR4();
|
||||||
|
@Mock
|
||||||
|
private ITermLoaderSvc myTermLoaderSvc;
|
||||||
|
@Captor
|
||||||
|
private ArgumentCaptor<List<ITermLoaderSvc.FileDescriptor>> myDescriptorListCaptor;
|
||||||
|
|
||||||
|
private int myPort;
|
||||||
|
private String myConceptsFileName = "target/concepts.csv";
|
||||||
|
private File myConceptsFile = new File(myConceptsFileName);
|
||||||
|
private String myHierarchyFileName = "target/hierarchy.csv";
|
||||||
|
private File myHierarchyFile = new File(myHierarchyFileName);
|
||||||
|
private String myCodeSystemFileName = "target/codesystem.json";
|
||||||
|
private File myCodeSystemFile = new File(myCodeSystemFileName);
|
||||||
|
private String myTextFileName = "target/hello.txt";
|
||||||
|
private File myTextFile = new File(myTextFileName);
|
||||||
|
private File myArchiveFile;
|
||||||
|
private String myArchiveFileName;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeltaAdd() throws IOException {
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
|
||||||
|
when(myTermLoaderSvc.loadDeltaAdd(eq("http://foo"), anyList(), any())).thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||||
|
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "ADD",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName,
|
||||||
|
"-d", myHierarchyFileName
|
||||||
|
});
|
||||||
|
|
||||||
|
verify(myTermLoaderSvc, times(1)).loadDeltaAdd(eq("http://foo"), myDescriptorListCaptor.capture(), any());
|
||||||
|
|
||||||
|
List<ITermLoaderSvc.FileDescriptor> listOfDescriptors = myDescriptorListCaptor.getValue();
|
||||||
|
assertEquals(1, listOfDescriptors.size());
|
||||||
|
assertEquals("file:/files.zip", listOfDescriptors.get(0).getFilename());
|
||||||
|
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeltaAddUsingCodeSystemResource() throws IOException {
|
||||||
|
|
||||||
|
try (FileWriter w = new FileWriter(myCodeSystemFile, false)) {
|
||||||
|
CodeSystem cs = new CodeSystem();
|
||||||
|
cs.addConcept().setCode("CODE").setDisplay("Display");
|
||||||
|
myCtx.newJsonParser().encodeResourceToWriter(cs, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
when(myTermLoaderSvc.loadDeltaAdd(eq("http://foo"), anyList(), any())).thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||||
|
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "ADD",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myCodeSystemFileName
|
||||||
|
});
|
||||||
|
|
||||||
|
verify(myTermLoaderSvc, times(1)).loadDeltaAdd(eq("http://foo"), myDescriptorListCaptor.capture(), any());
|
||||||
|
|
||||||
|
List<ITermLoaderSvc.FileDescriptor> listOfDescriptors = myDescriptorListCaptor.getValue();
|
||||||
|
assertEquals(1, listOfDescriptors.size());
|
||||||
|
assertEquals("concepts.csv", listOfDescriptors.get(0).getFilename());
|
||||||
|
String uploadFile = IOUtils.toString(listOfDescriptors.get(0).getInputStream(), Charsets.UTF_8);
|
||||||
|
assertThat(uploadFile, containsString("CODE,Display"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeltaAddInvalidResource() throws IOException {
|
||||||
|
|
||||||
|
try (FileWriter w = new FileWriter(myCodeSystemFile, false)) {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
myCtx.newJsonParser().encodeResourceToWriter(patient, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "ADD",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myCodeSystemFileName
|
||||||
|
});
|
||||||
|
fail();
|
||||||
|
} catch (Error e) {
|
||||||
|
assertThat(e.toString(), containsString("Incorrect resource type found, expected \"CodeSystem\" but found \"Patient\""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeltaAddInvalidFileType() throws IOException {
|
||||||
|
|
||||||
|
try (FileWriter w = new FileWriter(myTextFileName, false)) {
|
||||||
|
w.append("Help I'm a Bug");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "ADD",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myTextFileName
|
||||||
|
});
|
||||||
|
fail();
|
||||||
|
} catch (Error e) {
|
||||||
|
assertThat(e.toString(), containsString("Don't know how to handle file:"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeltaAddUsingCompressedFile() throws IOException {
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
writeArchiveFile(myConceptsFile, myHierarchyFile);
|
||||||
|
|
||||||
|
when(myTermLoaderSvc.loadDeltaAdd(eq("http://foo"), anyList(), any())).thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||||
|
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "ADD",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myArchiveFileName
|
||||||
|
});
|
||||||
|
|
||||||
|
verify(myTermLoaderSvc, times(1)).loadDeltaAdd(eq("http://foo"), myDescriptorListCaptor.capture(), any());
|
||||||
|
|
||||||
|
List<ITermLoaderSvc.FileDescriptor> listOfDescriptors = myDescriptorListCaptor.getValue();
|
||||||
|
assertEquals(1, listOfDescriptors.size());
|
||||||
|
assertThat(listOfDescriptors.get(0).getFilename(), matchesPattern("^file:.*temp.*\\.zip$"));
|
||||||
|
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeltaAddInvalidFileName() throws IOException {
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
|
||||||
|
try {
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "ADD",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName + "/foo.csv",
|
||||||
|
"-d", myHierarchyFileName
|
||||||
|
});
|
||||||
|
} catch (Error e) {
|
||||||
|
assertThat(e.toString(), Matchers.containsString("FileNotFoundException: target/concepts.csv/foo.csv"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeltaRemove() throws IOException {
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
|
||||||
|
when(myTermLoaderSvc.loadDeltaRemove(eq("http://foo"), anyList(), any())).thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||||
|
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "REMOVE",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName,
|
||||||
|
"-d", myHierarchyFileName
|
||||||
|
});
|
||||||
|
|
||||||
|
verify(myTermLoaderSvc, times(1)).loadDeltaRemove(eq("http://foo"), myDescriptorListCaptor.capture(), any());
|
||||||
|
|
||||||
|
List<ITermLoaderSvc.FileDescriptor> listOfDescriptors = myDescriptorListCaptor.getValue();
|
||||||
|
assertEquals(1, listOfDescriptors.size());
|
||||||
|
assertEquals("file:/files.zip", listOfDescriptors.get(0).getFilename());
|
||||||
|
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSnapshot() throws IOException {
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
|
||||||
|
when(myTermLoaderSvc.loadCustom(any(), anyList(), any())).thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||||
|
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "SNAPSHOT",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName,
|
||||||
|
"-d", myHierarchyFileName
|
||||||
|
});
|
||||||
|
|
||||||
|
verify(myTermLoaderSvc, times(1)).loadCustom(any(), myDescriptorListCaptor.capture(), any());
|
||||||
|
|
||||||
|
List<ITermLoaderSvc.FileDescriptor> listOfDescriptors = myDescriptorListCaptor.getValue();
|
||||||
|
assertEquals(1, listOfDescriptors.size());
|
||||||
|
assertEquals("file:/files.zip", listOfDescriptors.get(0).getFilename());
|
||||||
|
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When transferring large files, we use a local file to store the binary instead of
|
||||||
|
* using HTTP to transfer a giant base 64 encoded attachment. Hopefully we can
|
||||||
|
* replace this with a bulk data import at some point when that gets implemented.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testSnapshotLargeFile() throws IOException {
|
||||||
|
UploadTerminologyCommand.setTransferSizeLimitForUnitTest(10);
|
||||||
|
|
||||||
|
writeConceptAndHierarchyFiles();
|
||||||
|
|
||||||
|
when(myTermLoaderSvc.loadCustom(any(), anyList(), any())).thenReturn(new UploadStatistics(100, new IdType("CodeSystem/101")));
|
||||||
|
|
||||||
|
App.main(new String[]{
|
||||||
|
UploadTerminologyCommand.UPLOAD_TERMINOLOGY,
|
||||||
|
"-v", "r4",
|
||||||
|
"-m", "SNAPSHOT",
|
||||||
|
"-t", "http://localhost:" + myPort,
|
||||||
|
"-u", "http://foo",
|
||||||
|
"-d", myConceptsFileName,
|
||||||
|
"-d", myHierarchyFileName
|
||||||
|
});
|
||||||
|
|
||||||
|
verify(myTermLoaderSvc, times(1)).loadCustom(any(), myDescriptorListCaptor.capture(), any());
|
||||||
|
|
||||||
|
List<ITermLoaderSvc.FileDescriptor> listOfDescriptors = myDescriptorListCaptor.getValue();
|
||||||
|
assertEquals(1, listOfDescriptors.size());
|
||||||
|
assertThat(listOfDescriptors.get(0).getFilename(), matchesPattern(".*\\.zip$"));
|
||||||
|
assertThat(IOUtils.toByteArray(listOfDescriptors.get(0).getInputStream()).length, greaterThan(100));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeArchiveFile(File... theFiles) throws IOException {
|
||||||
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
||||||
|
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream, Charsets.UTF_8);
|
||||||
|
|
||||||
|
for (File next : theFiles) {
|
||||||
|
ZipEntry nextEntry = new ZipEntry(UploadTerminologyCommand.stripPath(next.getAbsolutePath()));
|
||||||
|
zipOutputStream.putNextEntry(nextEntry);
|
||||||
|
|
||||||
|
try (FileInputStream fileInputStream = new FileInputStream(next)) {
|
||||||
|
IOUtils.copy(fileInputStream, zipOutputStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
zipOutputStream.flush();
|
||||||
|
zipOutputStream.close();
|
||||||
|
|
||||||
|
myArchiveFile = File.createTempFile("temp", ".zip");
|
||||||
|
myArchiveFile.deleteOnExit();
|
||||||
|
myArchiveFileName = myArchiveFile.getAbsolutePath();
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(myArchiveFile, false)) {
|
||||||
|
fos.write(byteArrayOutputStream.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void writeConceptAndHierarchyFiles() throws IOException {
|
||||||
|
try (FileWriter w = new FileWriter(myConceptsFile, false)) {
|
||||||
|
w.append("CODE,DISPLAY\n");
|
||||||
|
w.append("ANIMALS,Animals\n");
|
||||||
|
w.append("CATS,Cats\n");
|
||||||
|
w.append("DOGS,Dogs\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (FileWriter w = new FileWriter(myHierarchyFile, false)) {
|
||||||
|
w.append("PARENT,CHILD\n");
|
||||||
|
w.append("ANIMALS,CATS\n");
|
||||||
|
w.append("ANIMALS,DOGS\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() throws Exception {
|
||||||
|
JettyUtil.closeServer(myServer);
|
||||||
|
|
||||||
|
FileUtils.deleteQuietly(myConceptsFile);
|
||||||
|
FileUtils.deleteQuietly(myHierarchyFile);
|
||||||
|
FileUtils.deleteQuietly(myArchiveFile);
|
||||||
|
FileUtils.deleteQuietly(myCodeSystemFile);
|
||||||
|
FileUtils.deleteQuietly(myTextFile);
|
||||||
|
|
||||||
|
UploadTerminologyCommand.setTransferSizeLimitForUnitTest(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() throws Exception {
|
||||||
|
myServer = new Server(0);
|
||||||
|
|
||||||
|
TerminologyUploaderProvider provider = new TerminologyUploaderProvider(myCtx, myTermLoaderSvc);
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer(myCtx);
|
||||||
|
servlet.registerProvider(provider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
myServer.setHandler(proxyHandler);
|
||||||
|
JettyUtil.startServer(myServer);
|
||||||
|
myPort = JettyUtil.getPortForStartedServer(myServer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -51,17 +51,12 @@ public class OkHttpRestfulResponse extends BaseHttpResponse implements IHttpResp
|
|||||||
this.myResponse = theResponse;
|
this.myResponse = theResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bufferEntitity() throws IOException {
|
|
||||||
bufferEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bufferEntity() throws IOException {
|
public void bufferEntity() throws IOException {
|
||||||
if (myEntityBuffered) {
|
if (myEntityBuffered) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InputStream responseEntity = readEntity();
|
try (InputStream responseEntity = readEntity()) {
|
||||||
if (responseEntity != null) {
|
if (responseEntity != null) {
|
||||||
myEntityBuffered = true;
|
myEntityBuffered = true;
|
||||||
try {
|
try {
|
||||||
@ -71,6 +66,7 @@ public class OkHttpRestfulResponse extends BaseHttpResponse implements IHttpResp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
@ -87,11 +87,13 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
protected IHttpRequest createHttpRequest() {
|
protected IHttpRequest createHttpRequest() {
|
||||||
IHttpRequest retVal = createHttpRequest((HttpEntity)null);
|
IHttpRequest retVal = createHttpRequest((HttpEntity)null);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected IHttpRequest createHttpRequest(byte[] content) {
|
protected IHttpRequest createHttpRequest(byte[] content) {
|
||||||
/*
|
/*
|
||||||
* Note: Be careful about changing which constructor we use for
|
* Note: Be careful about changing which constructor we use for
|
||||||
@ -109,6 +111,7 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected IHttpRequest createHttpRequest(Map<String, List<String>> theParams) {
|
protected IHttpRequest createHttpRequest(Map<String, List<String>> theParams) {
|
||||||
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
|
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
|
||||||
for (Entry<String, List<String>> nextParam : theParams.entrySet()) {
|
for (Entry<String, List<String>> nextParam : theParams.entrySet()) {
|
||||||
@ -124,6 +127,7 @@ public class ApacheHttpClient extends BaseHttpClient implements IHttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
protected IHttpRequest createHttpRequest(String theContents) {
|
protected IHttpRequest createHttpRequest(String theContents) {
|
||||||
/*
|
/*
|
||||||
* We aren't using a StringEntity here because the constructors
|
* We aren't using a StringEntity here because the constructors
|
||||||
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.rest.client.apache;
|
|||||||
*/
|
*/
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
|
import ca.uhn.fhir.rest.client.impl.BaseHttpResponse;
|
||||||
@ -53,28 +54,22 @@ public class ApacheHttpResponse extends BaseHttpResponse implements IHttpRespons
|
|||||||
this.myResponse = theResponse;
|
this.myResponse = theResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // override deprecated method
|
|
||||||
@Override
|
|
||||||
public void bufferEntitity() throws IOException {
|
|
||||||
bufferEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bufferEntity() throws IOException {
|
public void bufferEntity() throws IOException {
|
||||||
if (myEntityBuffered) {
|
if (myEntityBuffered) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InputStream respEntity = readEntity();
|
try (InputStream respEntity = readEntity()) {
|
||||||
if (respEntity != null) {
|
if (respEntity != null) {
|
||||||
this.myEntityBuffered = true;
|
this.myEntityBuffered = true;
|
||||||
try {
|
try {
|
||||||
this.myEntityBytes = IOUtils.toByteArray(respEntity);
|
this.myEntityBytes = IOUtils.toByteArray(respEntity);
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
// FIXME resouce leak
|
|
||||||
throw new InternalErrorException(e);
|
throw new InternalErrorException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
@ -103,7 +98,7 @@ public class ApacheHttpResponse extends BaseHttpResponse implements IHttpRespons
|
|||||||
if (Constants.STATUS_HTTP_204_NO_CONTENT != myResponse.getStatusLine().getStatusCode()) {
|
if (Constants.STATUS_HTTP_204_NO_CONTENT != myResponse.getStatusLine().getStatusCode()) {
|
||||||
ourLog.debug("Response did not specify a charset, defaulting to utf-8");
|
ourLog.debug("Response did not specify a charset, defaulting to utf-8");
|
||||||
}
|
}
|
||||||
charset = Charset.forName("UTF-8");
|
charset = StandardCharsets.UTF_8;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new InputStreamReader(readEntity(), charset);
|
return new InputStreamReader(readEntity(), charset);
|
||||||
@ -115,11 +110,7 @@ public class ApacheHttpResponse extends BaseHttpResponse implements IHttpRespons
|
|||||||
if (myResponse.getAllHeaders() != null) {
|
if (myResponse.getAllHeaders() != null) {
|
||||||
for (Header next : myResponse.getAllHeaders()) {
|
for (Header next : myResponse.getAllHeaders()) {
|
||||||
String name = next.getName().toLowerCase();
|
String name = next.getName().toLowerCase();
|
||||||
List<String> list = headers.get(name);
|
List<String> list = headers.computeIfAbsent(name, k -> new ArrayList<>());
|
||||||
if (list == null) {
|
|
||||||
list = new ArrayList<>();
|
|
||||||
headers.put(name, list);
|
|
||||||
}
|
|
||||||
list.add(next.getValue());
|
list.add(next.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,18 +71,18 @@ public class ApacheRestfulClientFactory extends RestfulClientFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ApacheHttpClient getHttpClient(String theServerBase) {
|
protected synchronized ApacheHttpClient getHttpClient(String theServerBase) {
|
||||||
return new ApacheHttpClient(getNativeHttpClient(), new StringBuilder(theServerBase), null, null, null, null);
|
return new ApacheHttpClient(getNativeHttpClient(), new StringBuilder(theServerBase), null, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IHttpClient getHttpClient(StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams,
|
public synchronized IHttpClient getHttpClient(StringBuilder theUrl, Map<String, List<String>> theIfNoneExistParams,
|
||||||
String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
|
String theIfNoneExistString, RequestTypeEnum theRequestType, List<Header> theHeaders) {
|
||||||
return new ApacheHttpClient(getNativeHttpClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType,
|
return new ApacheHttpClient(getNativeHttpClient(), theUrl, theIfNoneExistParams, theIfNoneExistString, theRequestType,
|
||||||
theHeaders);
|
theHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized HttpClient getNativeHttpClient() {
|
public HttpClient getNativeHttpClient() {
|
||||||
if (myHttpClient == null) {
|
if (myHttpClient == null) {
|
||||||
|
|
||||||
//FIXME potential resoource leak
|
//FIXME potential resoource leak
|
||||||
|
@ -71,52 +71,52 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getConnectionRequestTimeout() {
|
public synchronized int getConnectionRequestTimeout() {
|
||||||
return myConnectionRequestTimeout;
|
return myConnectionRequestTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getConnectTimeout() {
|
public synchronized int getConnectTimeout() {
|
||||||
return myConnectTimeout;
|
return myConnectTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the proxy username to authenticate with the HTTP proxy
|
* Return the proxy username to authenticate with the HTTP proxy
|
||||||
*/
|
*/
|
||||||
protected String getProxyUsername() {
|
protected synchronized String getProxyUsername() {
|
||||||
return myProxyUsername;
|
return myProxyUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the proxy password to authenticate with the HTTP proxy
|
* Return the proxy password to authenticate with the HTTP proxy
|
||||||
*/
|
*/
|
||||||
protected String getProxyPassword() {
|
protected synchronized String getProxyPassword() {
|
||||||
return myProxyPassword;
|
return myProxyPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setProxyCredentials(String theUsername, String thePassword) {
|
public synchronized void setProxyCredentials(String theUsername, String thePassword) {
|
||||||
myProxyUsername = theUsername;
|
myProxyUsername = theUsername;
|
||||||
myProxyPassword = thePassword;
|
myProxyPassword = thePassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ServerValidationModeEnum getServerValidationMode() {
|
public synchronized ServerValidationModeEnum getServerValidationMode() {
|
||||||
return myServerValidationMode;
|
return myServerValidationMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSocketTimeout() {
|
public synchronized int getSocketTimeout() {
|
||||||
return mySocketTimeout;
|
return mySocketTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPoolMaxTotal() {
|
public synchronized int getPoolMaxTotal() {
|
||||||
return myPoolMaxTotal;
|
return myPoolMaxTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPoolMaxPerRoute() {
|
public synchronized int getPoolMaxPerRoute() {
|
||||||
return myPoolMaxPerRoute;
|
return myPoolMaxPerRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +217,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setServerValidationMode(ServerValidationModeEnum theServerValidationMode) {
|
public synchronized void setServerValidationMode(ServerValidationModeEnum theServerValidationMode) {
|
||||||
Validate.notNull(theServerValidationMode, "theServerValidationMode may not be null");
|
Validate.notNull(theServerValidationMode, "theServerValidationMode may not be null");
|
||||||
myServerValidationMode = theServerValidationMode;
|
myServerValidationMode = theServerValidationMode;
|
||||||
}
|
}
|
||||||
@ -242,13 +242,13 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
|
|||||||
|
|
||||||
@Deprecated // override deprecated method
|
@Deprecated // override deprecated method
|
||||||
@Override
|
@Override
|
||||||
public ServerValidationModeEnum getServerValidationModeEnum() {
|
public synchronized ServerValidationModeEnum getServerValidationModeEnum() {
|
||||||
return getServerValidationMode();
|
return getServerValidationMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // override deprecated method
|
@Deprecated // override deprecated method
|
||||||
@Override
|
@Override
|
||||||
public void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) {
|
public synchronized void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) {
|
||||||
setServerValidationMode(theServerValidationMode);
|
setServerValidationMode(theServerValidationMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.client.interceptor;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -76,6 +77,7 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Hook(Pointcut.CLIENT_REQUEST)
|
@Hook(Pointcut.CLIENT_REQUEST)
|
||||||
public void interceptRequest(IHttpRequest theRequest) {
|
public void interceptRequest(IHttpRequest theRequest) {
|
||||||
if (myLogRequestSummary) {
|
if (myLogRequestSummary) {
|
||||||
@ -93,14 +95,13 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||||||
if (content != null) {
|
if (content != null) {
|
||||||
myLog.info("Client request body:\n{}", content);
|
myLog.info("Client request body:\n{}", content);
|
||||||
}
|
}
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException | IOException e) {
|
||||||
myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
|
myLog.warn("Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Hook(Pointcut.CLIENT_RESPONSE)
|
@Hook(Pointcut.CLIENT_RESPONSE)
|
||||||
public void interceptResponse(IHttpResponse theResponse) throws IOException {
|
public void interceptResponse(IHttpResponse theResponse) throws IOException {
|
||||||
if (myLogResponseSummary) {
|
if (myLogResponseSummary) {
|
||||||
@ -145,11 +146,8 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (myLogResponseBody) {
|
if (myLogResponseBody) {
|
||||||
//TODO: Use of a deprecated method should be resolved.
|
theResponse.bufferEntity();
|
||||||
theResponse.bufferEntitity();
|
try (InputStream respEntity = theResponse.readEntity()) {
|
||||||
InputStream respEntity = null;
|
|
||||||
try {
|
|
||||||
respEntity = theResponse.readEntity();
|
|
||||||
if (respEntity != null) {
|
if (respEntity != null) {
|
||||||
final byte[] bytes;
|
final byte[] bytes;
|
||||||
try {
|
try {
|
||||||
@ -157,12 +155,10 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
throw new InternalErrorException(e);
|
throw new InternalErrorException(e);
|
||||||
}
|
}
|
||||||
myLog.info("Client response body:\n{}", new String(bytes, "UTF-8"));
|
myLog.info("Client response body:\n{}", new String(bytes, StandardCharsets.UTF_8));
|
||||||
} else {
|
} else {
|
||||||
myLog.info("Client response body: (none)");
|
myLog.info("Client response body: (none)");
|
||||||
}
|
}
|
||||||
} finally {
|
|
||||||
IOUtils.closeQuietly(respEntity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,7 +172,9 @@ public class LoggingInterceptor implements IClientInterceptor {
|
|||||||
Iterator<String> values = theHeaders.get(key).iterator();
|
Iterator<String> values = theHeaders.get(key).iterator();
|
||||||
while(values.hasNext()) {
|
while(values.hasNext()) {
|
||||||
String value = values.next();
|
String value = values.next();
|
||||||
b.append(key + ": " + value);
|
b.append(key);
|
||||||
|
b.append(": ");
|
||||||
|
b.append(value);
|
||||||
if (nameEntries.hasNext() || values.hasNext()) {
|
if (nameEntries.hasNext() || values.hasNext()) {
|
||||||
b.append('\n');
|
b.append('\n');
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,6 @@ package ca.uhn.fhir.rest.client.method;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.Reader;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-converter</artifactId>
|
<artifactId>hapi-fhir-converter</artifactId>
|
||||||
<!-- TODO: BND changed to jar temporarily -->
|
<packaging>bundle</packaging>
|
||||||
<packaging>jar</packaging>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -152,10 +151,6 @@
|
|||||||
</resource>
|
</resource>
|
||||||
</resources>
|
</resources>
|
||||||
<plugins>
|
<plugins>
|
||||||
<!--
|
|
||||||
TODO: BND
|
|
||||||
With this enabled, JAR file contains a bunch of .class files that
|
|
||||||
seem to get copied in from org.hl7.fhir.convertors
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.felix</groupId>
|
<groupId>org.apache.felix</groupId>
|
||||||
<artifactId>maven-bundle-plugin</artifactId>
|
<artifactId>maven-bundle-plugin</artifactId>
|
||||||
@ -170,7 +165,6 @@
|
|||||||
</instructions>
|
</instructions>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
-->
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -29,6 +29,8 @@ import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
|||||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||||
|
import org.hl7.fhir.converter.NullVersionConverterAdvisor30;
|
||||||
|
import org.hl7.fhir.converter.NullVersionConverterAdvisor40;
|
||||||
import org.hl7.fhir.convertors.*;
|
import org.hl7.fhir.convertors.*;
|
||||||
import org.hl7.fhir.dstu3.model.Resource;
|
import org.hl7.fhir.dstu3.model.Resource;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package org.hl7.fhir.convertors;
|
package org.hl7.fhir.converter;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
@ -20,6 +20,7 @@ package org.hl7.fhir.convertors;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertorAdvisor30;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
import org.hl7.fhir.dstu3.model.ValueSet;
|
@ -1,4 +1,4 @@
|
|||||||
package org.hl7.fhir.convertors;
|
package org.hl7.fhir.converter;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
@ -20,6 +20,7 @@ package org.hl7.fhir.convertors;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertorAdvisor40;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.dstu2.model.Resource;
|
import org.hl7.fhir.dstu2.model.Resource;
|
||||||
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
|
@ -1,4 +1,4 @@
|
|||||||
package org.hl7.fhir.convertors;
|
package org.hl7.fhir.converter;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
@ -20,6 +20,7 @@ package org.hl7.fhir.convertors;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertorAdvisor50;
|
||||||
import org.hl7.fhir.dstu2.model.Resource;
|
import org.hl7.fhir.dstu2.model.Resource;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.r5.model.Bundle;
|
import org.hl7.fhir.r5.model.Bundle;
|
@ -1,7 +1,8 @@
|
|||||||
package org.hl7.fhir.convertors;
|
package org.hl7.fhir.converter;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertor_10_30;
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.dstu2.model.Resource;
|
import org.hl7.fhir.dstu2.model.Resource;
|
@ -1,7 +1,8 @@
|
|||||||
package org.hl7.fhir.convertors;
|
package org.hl7.fhir.converter;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.hl7.fhir.convertors.VersionConvertor_14_30;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
@ -87,6 +87,12 @@
|
|||||||
<artifactId>slf4j-simple</artifactId>
|
<artifactId>slf4j-simple</artifactId>
|
||||||
<version>1.7.28</version>
|
<version>1.7.28</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Needed for JEE/Servlet support -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
@ -20,7 +20,7 @@ package ca.uhn.hapi.fhir.docs;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.hl7.fhir.convertors.NullVersionConverterAdvisor30;
|
import org.hl7.fhir.converter.NullVersionConverterAdvisor30;
|
||||||
import org.hl7.fhir.convertors.VersionConvertor_10_30;
|
import org.hl7.fhir.convertors.VersionConvertor_10_30;
|
||||||
import org.hl7.fhir.convertors.VersionConvertor_14_30;
|
import org.hl7.fhir.convertors.VersionConvertor_14_30;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
@ -290,7 +290,7 @@ public class ValidatorExamples {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||||
// TODO: implement (or return null if your implementation does not support this function)
|
// TODO: implement (or return null if your implementation does not support this function)
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -35,10 +35,12 @@ public class IgPackParserDstu2 extends BaseIgPackParser<IValidationSupport> {
|
|||||||
super(massage(theCtx));
|
super(massage(theCtx));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
|
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
|
||||||
return new IgPackValidationSupportDstu2(theIgResources);
|
return new IgPackValidationSupportDstu2(theIgResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected FhirVersionEnum provideExpectedVersion() {
|
protected FhirVersionEnum provideExpectedVersion() {
|
||||||
return FhirVersionEnum.DSTU2_HL7ORG;
|
return FhirVersionEnum.DSTU2_HL7ORG;
|
||||||
}
|
}
|
||||||
|
@ -37,10 +37,12 @@ public class IgPackParserDstu3 extends BaseIgPackParser<IValidationSupport> {
|
|||||||
super(theCtx);
|
super(theCtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
|
protected IValidationSupport createValidationSupport(Map<IIdType, IBaseResource> theIgResources) {
|
||||||
return new IgPackValidationSupportDstu3(theIgResources);
|
return new IgPackValidationSupportDstu3(theIgResources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
protected FhirVersionEnum provideExpectedVersion() {
|
protected FhirVersionEnum provideExpectedVersion() {
|
||||||
return FhirVersionEnum.DSTU3;
|
return FhirVersionEnum.DSTU3;
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ public class IgPackValidationSupportDstu3 implements IValidationSupport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
|
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,11 +51,6 @@ public class JaxRsHttpResponse extends BaseHttpResponse implements IHttpResponse
|
|||||||
this.myResponse = theResponse;
|
this.myResponse = theResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bufferEntitity() throws IOException {
|
|
||||||
bufferEntity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bufferEntity() throws IOException {
|
public void bufferEntity() throws IOException {
|
||||||
if(!myBufferedEntity && myResponse.hasEntity()) {
|
if(!myBufferedEntity && myResponse.hasEntity()) {
|
||||||
|
@ -120,5 +120,4 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -46,7 +46,6 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-csv</artifactId>
|
<artifactId>commons-csv</artifactId>
|
||||||
<version>1.3</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.binstore;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
import ca.uhn.fhir.rest.server.exceptions.PayloadTooLargeException;
|
||||||
import com.google.common.hash.HashFunction;
|
import com.google.common.hash.HashFunction;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
@ -34,6 +33,8 @@ import javax.annotation.Nonnull;
|
|||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
|
||||||
abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
||||||
private final SecureRandom myRandom;
|
private final SecureRandom myRandom;
|
||||||
private final String CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
private final String CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
@ -66,7 +67,8 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||||||
myMinimumBinarySize = theMinimumBinarySize;
|
myMinimumBinarySize = theMinimumBinarySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
String newRandomId() {
|
@Override
|
||||||
|
public String newBlobId() {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
for (int i = 0; i < ID_LENGTH; i++) {
|
for (int i = 0; i < ID_LENGTH; i++) {
|
||||||
int nextInt = Math.abs(myRandom.nextInt());
|
int nextInt = Math.abs(myRandom.nextInt());
|
||||||
@ -89,13 +91,13 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
CountingInputStream createCountingInputStream(InputStream theInputStream) {
|
CountingInputStream createCountingInputStream(InputStream theInputStream) {
|
||||||
InputStream is = ByteStreams.limit(theInputStream, myMaximumBinarySize + 1L);
|
InputStream is = ByteStreams.limit(theInputStream, getMaximumBinarySize() + 1L);
|
||||||
return new CountingInputStream(is) {
|
return new CountingInputStream(is) {
|
||||||
@Override
|
@Override
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
int retVal = super.getCount();
|
int retVal = super.getCount();
|
||||||
if (retVal > myMaximumBinarySize) {
|
if (retVal > getMaximumBinarySize()) {
|
||||||
throw new PayloadTooLargeException("Binary size exceeds maximum: " + myMaximumBinarySize);
|
throw new PayloadTooLargeException("Binary size exceeds maximum: " + getMaximumBinarySize());
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
@ -103,4 +105,11 @@ abstract class BaseBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String provideIdForNewBlob(String theBlobIdOrNull) {
|
||||||
|
String id = theBlobIdOrNull;
|
||||||
|
if (isBlank(theBlobIdOrNull)) {
|
||||||
|
id = newBlobId();
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,20 +86,13 @@ public class BinaryAccessProvider {
|
|||||||
|
|
||||||
IBinaryTarget target = findAttachmentForRequest(resource, path, theRequestDetails);
|
IBinaryTarget target = findAttachmentForRequest(resource, path, theRequestDetails);
|
||||||
|
|
||||||
Optional<? extends IBaseExtension<?, ?>> attachmentId = target
|
Optional<String> attachmentId = target.getAttachmentId();
|
||||||
.getTarget()
|
|
||||||
.getExtension()
|
|
||||||
.stream()
|
|
||||||
.filter(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
|
|
||||||
.findFirst();
|
|
||||||
|
|
||||||
if (attachmentId.isPresent()) {
|
if (attachmentId.isPresent()) {
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
IPrimitiveType<String> value = (IPrimitiveType<String>) attachmentId.get().getValue();
|
String blobId = attachmentId.get();
|
||||||
String blobId = value.getValueAsString();
|
|
||||||
|
|
||||||
IBinaryStorageSvc.StoredDetails blobDetails = myBinaryStorageSvc.fetchBlobDetails(theResourceId, blobId);
|
StoredDetails blobDetails = myBinaryStorageSvc.fetchBlobDetails(theResourceId, blobId);
|
||||||
if (blobDetails == null) {
|
if (blobDetails == null) {
|
||||||
String msg = myCtx.getLocalizer().getMessage(BinaryAccessProvider.class, "unknownBlobId");
|
String msg = myCtx.getLocalizer().getMessage(BinaryAccessProvider.class, "unknownBlobId");
|
||||||
throw new InvalidRequestException(msg);
|
throw new InvalidRequestException(msg);
|
||||||
@ -179,7 +172,7 @@ public class BinaryAccessProvider {
|
|||||||
if (size > 0) {
|
if (size > 0) {
|
||||||
if (myBinaryStorageSvc != null) {
|
if (myBinaryStorageSvc != null) {
|
||||||
if (myBinaryStorageSvc.shouldStoreBlob(size, theResourceId, requestContentType)) {
|
if (myBinaryStorageSvc.shouldStoreBlob(size, theResourceId, requestContentType)) {
|
||||||
IBinaryStorageSvc.StoredDetails storedDetails = myBinaryStorageSvc.storeBlob(theResourceId, requestContentType, theRequestDetails.getInputStream());
|
StoredDetails storedDetails = myBinaryStorageSvc.storeBlob(theResourceId, null, requestContentType, theRequestDetails.getInputStream());
|
||||||
size = storedDetails.getBytes();
|
size = storedDetails.getBytes();
|
||||||
blobId = storedDetails.getBlobId();
|
blobId = storedDetails.getBlobId();
|
||||||
Validate.notBlank(blobId, "BinaryStorageSvc returned a null blob ID"); // should not happen
|
Validate.notBlank(blobId, "BinaryStorageSvc returned a null blob ID"); // should not happen
|
||||||
@ -192,19 +185,7 @@ public class BinaryAccessProvider {
|
|||||||
size = bytes.length;
|
size = bytes.length;
|
||||||
target.setData(bytes);
|
target.setData(bytes);
|
||||||
} else {
|
} else {
|
||||||
|
replaceDataWithExtension(target, blobId);
|
||||||
target
|
|
||||||
.getTarget()
|
|
||||||
.getExtension()
|
|
||||||
.removeIf(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()));
|
|
||||||
target.setData(null);
|
|
||||||
|
|
||||||
IBaseExtension<?, ?> ext = target.getTarget().addExtension();
|
|
||||||
ext.setUrl(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
|
|
||||||
ext.setUserData(JpaConstants.EXTENSION_EXT_SYSTEMDEFINED, Boolean.TRUE);
|
|
||||||
IPrimitiveType<String> blobIdString = (IPrimitiveType<String>) myCtx.getElementDefinition("string").newInstance();
|
|
||||||
blobIdString.setValueAsString(blobId);
|
|
||||||
ext.setValue(blobIdString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
target.setContentType(requestContentType);
|
target.setContentType(requestContentType);
|
||||||
@ -217,52 +198,81 @@ public class BinaryAccessProvider {
|
|||||||
return outcome.getResource();
|
return outcome.getResource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
public void replaceDataWithExtension(IBinaryTarget theTarget, String theBlobId) {
|
||||||
private IBinaryTarget findAttachmentForRequest(IBaseResource theResource, String thePath, ServletRequestDetails theRequestDetails) {
|
theTarget
|
||||||
FhirContext ctx = theRequestDetails.getFhirContext();
|
.getTarget()
|
||||||
|
.getExtension()
|
||||||
|
.removeIf(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()));
|
||||||
|
theTarget.setData(null);
|
||||||
|
|
||||||
Optional<IBase> type = ctx.newFluentPath().evaluateFirst(theResource, thePath, IBase.class);
|
IBaseExtension<?, ?> ext = theTarget.getTarget().addExtension();
|
||||||
String resType = myCtx.getResourceDefinition(theResource).getName();
|
ext.setUrl(JpaConstants.EXT_EXTERNALIZED_BINARY_ID);
|
||||||
if (!type.isPresent()) {
|
ext.setUserData(JpaConstants.EXTENSION_EXT_SYSTEMDEFINED, Boolean.TRUE);
|
||||||
String msg = myCtx.getLocalizer().getMessageSanitized(BinaryAccessProvider.class, "unknownPath", resType, thePath);
|
IPrimitiveType<String> blobIdString = (IPrimitiveType<String>) myCtx.getElementDefinition("string").newInstance();
|
||||||
throw new InvalidRequestException(msg);
|
blobIdString.setValueAsString(theBlobId);
|
||||||
|
ext.setValue(blobIdString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private IBinaryTarget findAttachmentForRequest(IBaseResource theResource, String thePath, ServletRequestDetails theRequestDetails) {
|
||||||
|
Optional<IBase> type = myCtx.newFluentPath().evaluateFirst(theResource, thePath, IBase.class);
|
||||||
|
String resType = this.myCtx.getResourceDefinition(theResource).getName();
|
||||||
|
if (!type.isPresent()) {
|
||||||
|
String msg = this.myCtx.getLocalizer().getMessageSanitized(BinaryAccessProvider.class, "unknownPath", resType, thePath);
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
IBase element = type.get();
|
||||||
|
|
||||||
|
Optional<IBinaryTarget> binaryTarget = toBinaryTarget(element);
|
||||||
|
|
||||||
|
if (binaryTarget.isPresent() == false) {
|
||||||
|
BaseRuntimeElementDefinition<?> def2 = myCtx.getElementDefinition(element.getClass());
|
||||||
|
String msg = this.myCtx.getLocalizer().getMessageSanitized(BinaryAccessProvider.class, "unknownType", resType, thePath, def2.getName());
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
} else {
|
||||||
|
return binaryTarget.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<IBinaryTarget> toBinaryTarget(IBase theElement) {
|
||||||
|
IBinaryTarget binaryTarget = null;
|
||||||
|
|
||||||
// Path is attachment
|
// Path is attachment
|
||||||
BaseRuntimeElementDefinition<?> def = ctx.getElementDefinition(type.get().getClass());
|
BaseRuntimeElementDefinition<?> def = myCtx.getElementDefinition(theElement.getClass());
|
||||||
if (def.getName().equals("Attachment")) {
|
if (def.getName().equals("Attachment")) {
|
||||||
ICompositeType attachment = (ICompositeType) type.get();
|
ICompositeType attachment = (ICompositeType) theElement;
|
||||||
return new IBinaryTarget() {
|
binaryTarget = new IBinaryTarget() {
|
||||||
@Override
|
@Override
|
||||||
public void setSize(Integer theSize) {
|
public void setSize(Integer theSize) {
|
||||||
AttachmentUtil.setSize(myCtx, attachment, theSize);
|
AttachmentUtil.setSize(BinaryAccessProvider.this.myCtx, attachment, theSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getContentType() {
|
public String getContentType() {
|
||||||
return AttachmentUtil.getOrCreateContentType(myCtx, attachment).getValueAsString();
|
return AttachmentUtil.getOrCreateContentType(BinaryAccessProvider.this.myCtx, attachment).getValueAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] getData() {
|
public byte[] getData() {
|
||||||
IPrimitiveType<byte[]> dataDt = AttachmentUtil.getOrCreateData(theRequestDetails.getFhirContext(), attachment);
|
IPrimitiveType<byte[]> dataDt = AttachmentUtil.getOrCreateData(myCtx, attachment);
|
||||||
return dataDt.getValue();
|
return dataDt.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseHasExtensions getTarget() {
|
public IBaseHasExtensions getTarget() {
|
||||||
return (IBaseHasExtensions) AttachmentUtil.getOrCreateData(theRequestDetails.getFhirContext(), attachment);
|
return (IBaseHasExtensions) AttachmentUtil.getOrCreateData(myCtx, attachment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setContentType(String theContentType) {
|
public void setContentType(String theContentType) {
|
||||||
AttachmentUtil.setContentType(myCtx, attachment, theContentType);
|
AttachmentUtil.setContentType(BinaryAccessProvider.this.myCtx, attachment, theContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setData(byte[] theBytes) {
|
public void setData(byte[] theBytes) {
|
||||||
AttachmentUtil.setData(theRequestDetails.getFhirContext(), attachment, theBytes);
|
AttachmentUtil.setData(myCtx, attachment, theBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -271,8 +281,8 @@ public class BinaryAccessProvider {
|
|||||||
|
|
||||||
// Path is Binary
|
// Path is Binary
|
||||||
if (def.getName().equals("Binary")) {
|
if (def.getName().equals("Binary")) {
|
||||||
IBaseBinary binary = (IBaseBinary) type.get();
|
IBaseBinary binary = (IBaseBinary) theElement;
|
||||||
return new IBinaryTarget() {
|
binaryTarget = new IBinaryTarget() {
|
||||||
@Override
|
@Override
|
||||||
public void setSize(Integer theSize) {
|
public void setSize(Integer theSize) {
|
||||||
// ignore
|
// ignore
|
||||||
@ -290,7 +300,7 @@ public class BinaryAccessProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseHasExtensions getTarget() {
|
public IBaseHasExtensions getTarget() {
|
||||||
return (IBaseHasExtensions) BinaryUtil.getOrCreateData(myCtx, binary);
|
return (IBaseHasExtensions) BinaryUtil.getOrCreateData(BinaryAccessProvider.this.myCtx, binary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -308,9 +318,7 @@ public class BinaryAccessProvider {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
String msg = myCtx.getLocalizer().getMessageSanitized(BinaryAccessProvider.class, "unknownType", resType, thePath, def.getName());
|
return Optional.ofNullable(binaryTarget);
|
||||||
throw new InvalidRequestException(msg);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String validateResourceTypeAndPath(@IdParam IIdType theResourceId, @OperationParam(name = "path", min = 1, max = 1) IPrimitiveType<String> thePath) {
|
private String validateResourceTypeAndPath(@IdParam IIdType theResourceId, @OperationParam(name = "path", min = 1, max = 1) IPrimitiveType<String> thePath) {
|
||||||
@ -340,25 +348,5 @@ public class BinaryAccessProvider {
|
|||||||
return dao;
|
return dao;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps an Attachment datatype or Binary resource, since they both
|
|
||||||
* hold binary content but don't look entirely similar
|
|
||||||
*/
|
|
||||||
private interface IBinaryTarget {
|
|
||||||
|
|
||||||
void setSize(Integer theSize);
|
|
||||||
|
|
||||||
String getContentType();
|
|
||||||
|
|
||||||
void setContentType(String theContentType);
|
|
||||||
|
|
||||||
byte[] getData();
|
|
||||||
|
|
||||||
void setData(byte[] theBytes);
|
|
||||||
|
|
||||||
IBaseHasExtensions getTarget();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,23 +20,37 @@ package ca.uhn.fhir.jpa.binstore;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.interceptor.api.Hook;
|
import ca.uhn.fhir.interceptor.api.Hook;
|
||||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import ca.uhn.fhir.util.IModelVisitor2;
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.hl7.fhir.instance.model.api.*;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
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 java.util.List;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.model.util.JpaConstants.EXT_EXTERNALIZED_BINARY_ID;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
@Interceptor
|
@Interceptor
|
||||||
public class BinaryStorageInterceptor {
|
public class BinaryStorageInterceptor {
|
||||||
|
|
||||||
@ -45,12 +59,39 @@ public class BinaryStorageInterceptor {
|
|||||||
private IBinaryStorageSvc myBinaryStorageSvc;
|
private IBinaryStorageSvc myBinaryStorageSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myCtx;
|
private FhirContext myCtx;
|
||||||
|
@Autowired
|
||||||
|
private BinaryAccessProvider myBinaryAccessProvider;
|
||||||
|
private Class<? extends IPrimitiveType<byte[]>> myBinaryType;
|
||||||
|
private String myDeferredListKey;
|
||||||
|
private long myAutoDeExternalizeMaximumBytes = 10 * FileUtils.ONE_MB;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any externalized binaries will be rehydrated if their size is below this thhreshold when
|
||||||
|
* reading the resource back. Default is 10MB.
|
||||||
|
*/
|
||||||
|
public long getAutoDeExternalizeMaximumBytes() {
|
||||||
|
return myAutoDeExternalizeMaximumBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any externalized binaries will be rehydrated if their size is below this thhreshold when
|
||||||
|
* reading the resource back. Default is 10MB.
|
||||||
|
*/
|
||||||
|
public void setAutoDeExternalizeMaximumBytes(long theAutoDeExternalizeMaximumBytes) {
|
||||||
|
myAutoDeExternalizeMaximumBytes = theAutoDeExternalizeMaximumBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@PostConstruct
|
||||||
|
public void start() {
|
||||||
|
myBinaryType = (Class<? extends IPrimitiveType<byte[]>>) myCtx.getElementDefinition("base64Binary").getImplementingClass();
|
||||||
|
myDeferredListKey = getClass().getName() + "_" + hashCode() + "_DEFERRED_LIST";
|
||||||
|
}
|
||||||
|
|
||||||
@Hook(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE)
|
@Hook(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE)
|
||||||
public void expungeResource(AtomicInteger theCounter, IBaseResource theResource) {
|
public void expungeResource(AtomicInteger theCounter, IBaseResource theResource) {
|
||||||
|
|
||||||
Class<? extends IBase> binaryType = myCtx.getElementDefinition("base64Binary").getImplementingClass();
|
List<? extends IBase> binaryElements = myCtx.newTerser().getAllPopulatedChildElementsOfType(theResource, myBinaryType);
|
||||||
List<? extends IBase> binaryElements = myCtx.newTerser().getAllPopulatedChildElementsOfType(theResource, binaryType);
|
|
||||||
|
|
||||||
List<String> attachmentIds = binaryElements
|
List<String> attachmentIds = binaryElements
|
||||||
.stream()
|
.stream()
|
||||||
@ -67,4 +108,215 @@ public class BinaryStorageInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED)
|
||||||
|
public void extractLargeBinariesBeforeCreate(ServletRequestDetails theRequestDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException {
|
||||||
|
extractLargeBinaries(theRequestDetails, theResource, thePointcut);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED)
|
||||||
|
public void extractLargeBinariesBeforeUpdate(ServletRequestDetails theRequestDetails, IBaseResource thePreviousResource, IBaseResource theResource, Pointcut thePointcut) throws IOException {
|
||||||
|
blockIllegalExternalBinaryIds(thePreviousResource, theResource);
|
||||||
|
extractLargeBinaries(theRequestDetails, theResource, thePointcut);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't allow clients to submit resources with binary storage attachments declared unless the ID was already in the
|
||||||
|
* resource. In other words, only HAPI itself may add a binary storage ID extension to a resource unless that
|
||||||
|
* extension was already present.
|
||||||
|
*/
|
||||||
|
private void blockIllegalExternalBinaryIds(IBaseResource thePreviousResource, IBaseResource theResource) {
|
||||||
|
Set<String> existingBinaryIds = new HashSet<>();
|
||||||
|
if (thePreviousResource != null) {
|
||||||
|
List<? extends IPrimitiveType<byte[]>> base64fields = myCtx.newTerser().getAllPopulatedChildElementsOfType(thePreviousResource, myBinaryType);
|
||||||
|
for (IPrimitiveType<byte[]> nextBase64 : base64fields) {
|
||||||
|
if (nextBase64 instanceof IBaseHasExtensions) {
|
||||||
|
((IBaseHasExtensions) nextBase64)
|
||||||
|
.getExtension()
|
||||||
|
.stream()
|
||||||
|
.filter(t -> t.getUserData(JpaConstants.EXTENSION_EXT_SYSTEMDEFINED) == null)
|
||||||
|
.filter(t -> EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
|
||||||
|
.map(t -> (IPrimitiveType) t.getValue())
|
||||||
|
.map(t -> t.getValueAsString())
|
||||||
|
.filter(t -> isNotBlank(t))
|
||||||
|
.forEach(t -> existingBinaryIds.add(t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<? extends IPrimitiveType<byte[]>> base64fields = myCtx.newTerser().getAllPopulatedChildElementsOfType(theResource, myBinaryType);
|
||||||
|
for (IPrimitiveType<byte[]> nextBase64 : base64fields) {
|
||||||
|
if (nextBase64 instanceof IBaseHasExtensions) {
|
||||||
|
Optional<String> hasExternalizedBinaryReference = ((IBaseHasExtensions) nextBase64)
|
||||||
|
.getExtension()
|
||||||
|
.stream()
|
||||||
|
.filter(t -> t.getUserData(JpaConstants.EXTENSION_EXT_SYSTEMDEFINED) == null)
|
||||||
|
.filter(t -> t.getUrl().equals(EXT_EXTERNALIZED_BINARY_ID))
|
||||||
|
.map(t->(IPrimitiveType) t.getValue())
|
||||||
|
.map(t->t.getValueAsString())
|
||||||
|
.filter(t->isNotBlank(t))
|
||||||
|
.filter(t->{
|
||||||
|
return !existingBinaryIds.contains(t);
|
||||||
|
}).findFirst();
|
||||||
|
|
||||||
|
if (hasExternalizedBinaryReference.isPresent()) {
|
||||||
|
String msg = myCtx.getLocalizer().getMessage(BaseHapiFhirDao.class, "externalizedBinaryStorageExtensionFoundInRequestBody", EXT_EXTERNALIZED_BINARY_ID, hasExternalizedBinaryReference.get());
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractLargeBinaries(ServletRequestDetails theRequestDetails, IBaseResource theResource, Pointcut thePointcut) throws IOException {
|
||||||
|
if (theRequestDetails == null) {
|
||||||
|
// RequestDetails will only be null for internal HAPI events. If externalization is required for them it will need to be done in a different way.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
IIdType resourceId = theResource.getIdElement();
|
||||||
|
if (!resourceId.hasResourceType() && resourceId.hasIdPart()) {
|
||||||
|
String resourceType = myCtx.getResourceDefinition(theResource).getName();
|
||||||
|
resourceId = new IdType(resourceType + "/" + resourceId.getIdPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IBinaryTarget> attachments = recursivelyScanResourceForBinaryData(theResource);
|
||||||
|
for (IBinaryTarget nextTarget : attachments) {
|
||||||
|
byte[] data = nextTarget.getData();
|
||||||
|
if (data != null && data.length > 0) {
|
||||||
|
|
||||||
|
long nextPayloadLength = data.length;
|
||||||
|
String nextContentType = nextTarget.getContentType();
|
||||||
|
boolean shouldStoreBlob = myBinaryStorageSvc.shouldStoreBlob(nextPayloadLength, resourceId, nextContentType);
|
||||||
|
if (shouldStoreBlob) {
|
||||||
|
|
||||||
|
String newBlobId;
|
||||||
|
if (resourceId.hasIdPart()) {
|
||||||
|
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
|
||||||
|
StoredDetails storedDetails = myBinaryStorageSvc.storeBlob(resourceId, null, nextContentType, inputStream);
|
||||||
|
newBlobId = storedDetails.getBlobId();
|
||||||
|
} else {
|
||||||
|
assert thePointcut == Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED : thePointcut.name();
|
||||||
|
newBlobId = myBinaryStorageSvc.newBlobId();
|
||||||
|
List<DeferredBinaryTarget> deferredBinaryTargets = getOrCreateDeferredBinaryStorageMap(theRequestDetails);
|
||||||
|
DeferredBinaryTarget newDeferredBinaryTarget = new DeferredBinaryTarget(newBlobId, nextTarget, data);
|
||||||
|
deferredBinaryTargets.add(newDeferredBinaryTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
myBinaryAccessProvider.replaceDataWithExtension(nextTarget, newBlobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private List<DeferredBinaryTarget> getOrCreateDeferredBinaryStorageMap(ServletRequestDetails theRequestDetails) {
|
||||||
|
List<DeferredBinaryTarget> deferredBinaryTargets = (List<DeferredBinaryTarget>) theRequestDetails.getUserData().get(getDeferredListKey());
|
||||||
|
if (deferredBinaryTargets == null) {
|
||||||
|
deferredBinaryTargets = new ArrayList<>();
|
||||||
|
theRequestDetails.getUserData().put(getDeferredListKey(), deferredBinaryTargets);
|
||||||
|
}
|
||||||
|
return deferredBinaryTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Hook(Pointcut.STORAGE_PRECOMMIT_RESOURCE_CREATED)
|
||||||
|
public void storeLargeBinariesBeforeCreatePersistence(ServletRequestDetails theRequestDetails, IBaseResource theResource, Pointcut thePoincut) throws IOException {
|
||||||
|
if (theRequestDetails == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<DeferredBinaryTarget> deferredBinaryTargets = (List<DeferredBinaryTarget>) theRequestDetails.getUserData().get(getDeferredListKey());
|
||||||
|
if (deferredBinaryTargets != null) {
|
||||||
|
IIdType resourceId = theResource.getIdElement();
|
||||||
|
for (DeferredBinaryTarget next : deferredBinaryTargets) {
|
||||||
|
String blobId = next.getBlobId();
|
||||||
|
IBinaryTarget target = next.getBinaryTarget();
|
||||||
|
InputStream dataStream = next.getDataStream();
|
||||||
|
String contentType = target.getContentType();
|
||||||
|
myBinaryStorageSvc.storeBlob(resourceId, blobId, contentType, dataStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDeferredListKey() {
|
||||||
|
return myDeferredListKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Hook(Pointcut.STORAGE_PRESHOW_RESOURCES)
|
||||||
|
public void preShow(IPreResourceShowDetails theDetails) throws IOException {
|
||||||
|
long unmarshalledByteCount = 0;
|
||||||
|
|
||||||
|
for (IBaseResource nextResource : theDetails) {
|
||||||
|
|
||||||
|
IIdType resourceId = nextResource.getIdElement();
|
||||||
|
List<IBinaryTarget> attachments = recursivelyScanResourceForBinaryData(nextResource);
|
||||||
|
|
||||||
|
for (IBinaryTarget nextTarget : attachments) {
|
||||||
|
Optional<String> attachmentId = nextTarget.getAttachmentId();
|
||||||
|
if (attachmentId.isPresent()) {
|
||||||
|
|
||||||
|
StoredDetails blobDetails = myBinaryStorageSvc.fetchBlobDetails(resourceId, attachmentId.get());
|
||||||
|
if (blobDetails == null) {
|
||||||
|
String msg = myCtx.getLocalizer().getMessage(BinaryAccessProvider.class, "unknownBlobId");
|
||||||
|
throw new InvalidRequestException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((unmarshalledByteCount + blobDetails.getBytes()) < myAutoDeExternalizeMaximumBytes) {
|
||||||
|
|
||||||
|
byte[] bytes = myBinaryStorageSvc.fetchBlob(resourceId, attachmentId.get());
|
||||||
|
nextTarget.setData(bytes);
|
||||||
|
unmarshalledByteCount += blobDetails.getBytes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private List<IBinaryTarget> recursivelyScanResourceForBinaryData(IBaseResource theResource) {
|
||||||
|
List<IBinaryTarget> binaryTargets = new ArrayList<>();
|
||||||
|
myCtx.newTerser().visit(theResource, new IModelVisitor2() {
|
||||||
|
@Override
|
||||||
|
public boolean acceptElement(IBase theElement, List<IBase> theContainingElementPath, List<BaseRuntimeChildDefinition> theChildDefinitionPath, List<BaseRuntimeElementDefinition<?>> theElementDefinitionPath) {
|
||||||
|
|
||||||
|
if (theElement.getClass().equals(myBinaryType)) {
|
||||||
|
IBase parent = theContainingElementPath.get(theContainingElementPath.size() - 2);
|
||||||
|
Optional<IBinaryTarget> binaryTarget = myBinaryAccessProvider.toBinaryTarget(parent);
|
||||||
|
if (binaryTarget.isPresent()) {
|
||||||
|
binaryTargets.add(binaryTarget.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return binaryTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DeferredBinaryTarget {
|
||||||
|
private final String myBlobId;
|
||||||
|
private final IBinaryTarget myBinaryTarget;
|
||||||
|
private final InputStream myDataStream;
|
||||||
|
|
||||||
|
private DeferredBinaryTarget(String theBlobId, IBinaryTarget theBinaryTarget, byte[] theData) {
|
||||||
|
myBlobId = theBlobId;
|
||||||
|
myBinaryTarget = theBinaryTarget;
|
||||||
|
myDataStream = new ByteArrayInputStream(theData);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getBlobId() {
|
||||||
|
return myBlobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
IBinaryTarget getBinaryTarget() {
|
||||||
|
return myBinaryTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream getDataStream() {
|
||||||
|
return myDataStream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.binstore;
|
|||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.data.IBinaryStorageEntityDao;
|
import ca.uhn.fhir.jpa.dao.data.IBinaryStorageEntityDao;
|
||||||
import ca.uhn.fhir.jpa.model.entity.BinaryStorageEntity;
|
import ca.uhn.fhir.jpa.model.entity.BinaryStorageEntity;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import com.google.common.hash.HashingInputStream;
|
import com.google.common.hash.HashingInputStream;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.io.input.CountingInputStream;
|
import org.apache.commons.io.input.CountingInputStream;
|
||||||
@ -57,13 +58,13 @@ public class DatabaseBlobBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(Transactional.TxType.SUPPORTS)
|
@Transactional(Transactional.TxType.SUPPORTS)
|
||||||
public StoredDetails storeBlob(IIdType theResourceId, String theContentType, InputStream theInputStream) {
|
public StoredDetails storeBlob(IIdType theResourceId, String theBlobIdOrNull, String theContentType, InputStream theInputStream) {
|
||||||
Date publishedDate = new Date();
|
Date publishedDate = new Date();
|
||||||
|
|
||||||
HashingInputStream hashingInputStream = createHashingInputStream(theInputStream);
|
HashingInputStream hashingInputStream = createHashingInputStream(theInputStream);
|
||||||
CountingInputStream countingInputStream = createCountingInputStream(hashingInputStream);
|
CountingInputStream countingInputStream = createCountingInputStream(hashingInputStream);
|
||||||
|
|
||||||
String id = newRandomId();
|
String id = super.provideIdForNewBlob(theBlobIdOrNull);
|
||||||
|
|
||||||
BinaryStorageEntity entity = new BinaryStorageEntity();
|
BinaryStorageEntity entity = new BinaryStorageEntity();
|
||||||
entity.setResourceId(theResourceId.toUnqualifiedVersionless().getValue());
|
entity.setResourceId(theResourceId.toUnqualifiedVersionless().getValue());
|
||||||
@ -126,12 +127,7 @@ public class DatabaseBlobBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
copyBlobToOutputStream(theOutputStream, entityOpt.get());
|
||||||
InputStream inputStream = entityOpt.get().getBlob().getBinaryStream();
|
|
||||||
IOUtils.copy(inputStream, theOutputStream);
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -139,6 +135,32 @@ public class DatabaseBlobBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||||||
@Override
|
@Override
|
||||||
public void expungeBlob(IIdType theResourceId, String theBlobId) {
|
public void expungeBlob(IIdType theResourceId, String theBlobId) {
|
||||||
Optional<BinaryStorageEntity> entityOpt = myBinaryStorageEntityDao.findByIdAndResourceId(theBlobId, theResourceId.toUnqualifiedVersionless().getValue());
|
Optional<BinaryStorageEntity> entityOpt = myBinaryStorageEntityDao.findByIdAndResourceId(theBlobId, theResourceId.toUnqualifiedVersionless().getValue());
|
||||||
entityOpt.ifPresent(theBinaryStorageEntity -> myBinaryStorageEntityDao.delete(theBinaryStorageEntity));
|
entityOpt.ifPresent(theBinaryStorageEntity -> myBinaryStorageEntityDao.deleteByPid(theBinaryStorageEntity.getBlobId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] fetchBlob(IIdType theResourceId, String theBlobId) throws IOException {
|
||||||
|
BinaryStorageEntity entityOpt = myBinaryStorageEntityDao
|
||||||
|
.findByIdAndResourceId(theBlobId, theResourceId.toUnqualifiedVersionless().getValue())
|
||||||
|
.orElseThrow(() -> new ResourceNotFoundException("Unknown blob ID: " + theBlobId + " for resource ID " + theResourceId));
|
||||||
|
|
||||||
|
return copyBlobToByteArray(entityOpt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copyBlobToOutputStream(OutputStream theOutputStream, BinaryStorageEntity theEntity) throws IOException {
|
||||||
|
try (InputStream inputStream = theEntity.getBlob().getBinaryStream()) {
|
||||||
|
IOUtils.copy(inputStream, theOutputStream);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] copyBlobToByteArray(BinaryStorageEntity theEntity) throws IOException {
|
||||||
|
int size = theEntity.getSize();
|
||||||
|
try {
|
||||||
|
return IOUtils.toByteArray(theEntity.getBlob().getBinaryStream(), size);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.binstore;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
@ -31,6 +32,7 @@ import org.apache.commons.io.IOUtils;
|
|||||||
import org.apache.commons.io.input.CountingInputStream;
|
import org.apache.commons.io.input.CountingInputStream;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -63,8 +65,8 @@ public class FilesystemBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoredDetails storeBlob(IIdType theResourceId, String theContentType, InputStream theInputStream) throws IOException {
|
public StoredDetails storeBlob(IIdType theResourceId, String theBlobIdOrNull, String theContentType, InputStream theInputStream) throws IOException {
|
||||||
String id = newRandomId();
|
String id = super.provideIdForNewBlob(theBlobIdOrNull);
|
||||||
File storagePath = getStoragePath(id, true);
|
File storagePath = getStoragePath(id, true);
|
||||||
|
|
||||||
// Write binary file
|
// Write binary file
|
||||||
@ -111,17 +113,31 @@ public class FilesystemBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean writeBlob(IIdType theResourceId, String theBlobId, OutputStream theOutputStream) throws IOException {
|
public boolean writeBlob(IIdType theResourceId, String theBlobId, OutputStream theOutputStream) throws IOException {
|
||||||
|
InputStream inputStream = getInputStream(theResourceId, theBlobId);
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
try {
|
||||||
|
IOUtils.copy(inputStream, theOutputStream);
|
||||||
|
theOutputStream.close();
|
||||||
|
} finally {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private InputStream getInputStream(IIdType theResourceId, String theBlobId) throws FileNotFoundException {
|
||||||
File storagePath = getStoragePath(theBlobId, false);
|
File storagePath = getStoragePath(theBlobId, false);
|
||||||
|
InputStream inputStream = null;
|
||||||
if (storagePath != null) {
|
if (storagePath != null) {
|
||||||
File file = getStorageFilename(storagePath, theResourceId, theBlobId);
|
File file = getStorageFilename(storagePath, theResourceId, theBlobId);
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
try (InputStream inputStream = new FileInputStream(file)) {
|
inputStream = new FileInputStream(file);
|
||||||
IOUtils.copy(inputStream, theOutputStream);
|
|
||||||
theOutputStream.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
return inputStream;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -139,6 +155,20 @@ public class FilesystemBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] fetchBlob(IIdType theResourceId, String theBlobId) throws IOException {
|
||||||
|
StoredDetails details = fetchBlobDetails(theResourceId, theBlobId);
|
||||||
|
try (InputStream inputStream = getInputStream(theResourceId, theBlobId)) {
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
return IOUtils.toByteArray(inputStream, details.getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ResourceNotFoundException("Unknown blob ID: " + theBlobId + " for resource ID " + theResourceId);
|
||||||
|
}
|
||||||
|
|
||||||
private void delete(File theStorageFile, String theBlobId) {
|
private void delete(File theStorageFile, String theBlobId) {
|
||||||
Validate.isTrue(theStorageFile.delete(), "Failed to delete file for blob %s", theBlobId);
|
Validate.isTrue(theStorageFile.delete(), "Failed to delete file for blob %s", theBlobId);
|
||||||
}
|
}
|
||||||
|
@ -20,22 +20,12 @@ package ca.uhn.fhir.jpa.binstore;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.util.JsonDateDeserializer;
|
|
||||||
import ca.uhn.fhir.jpa.util.JsonDateSerializer;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
|
||||||
import com.google.common.hash.HashingInputStream;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public interface IBinaryStorageSvc {
|
public interface IBinaryStorageSvc {
|
||||||
|
|
||||||
@ -77,15 +67,22 @@ public interface IBinaryStorageSvc {
|
|||||||
*/
|
*/
|
||||||
boolean shouldStoreBlob(long theSize, IIdType theResourceId, String theContentType);
|
boolean shouldStoreBlob(long theSize, IIdType theResourceId, String theContentType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a new blob ID that will be passed to {@link #storeBlob(IIdType, String, String, InputStream)} later
|
||||||
|
*/
|
||||||
|
String newBlobId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a new binary blob
|
* Store a new binary blob
|
||||||
*
|
*
|
||||||
* @param theResourceId The resource ID that owns this blob. Note that it should not be possible to retrieve a blob without both the resource ID and the blob ID being correct.
|
* @param theResourceId The resource ID that owns this blob. Note that it should not be possible to retrieve a blob without both the resource ID and the blob ID being correct.
|
||||||
|
* @param theBlobIdOrNull If set, forces
|
||||||
* @param theContentType The content type to associate with this blob
|
* @param theContentType The content type to associate with this blob
|
||||||
* @param theInputStream An InputStream to read from. This method should close the stream when it has been fully consumed.
|
* @param theInputStream An InputStream to read from. This method should close the stream when it has been fully consumed.
|
||||||
* @return Returns details about the stored data
|
* @return Returns details about the stored data
|
||||||
*/
|
*/
|
||||||
StoredDetails storeBlob(IIdType theResourceId, String theContentType, InputStream theInputStream) throws IOException;
|
@Nonnull
|
||||||
|
StoredDetails storeBlob(IIdType theResourceId, String theBlobIdOrNull, String theContentType, InputStream theInputStream) throws IOException;
|
||||||
|
|
||||||
StoredDetails fetchBlobDetails(IIdType theResourceId, String theBlobId) throws IOException;
|
StoredDetails fetchBlobDetails(IIdType theResourceId, String theBlobId) throws IOException;
|
||||||
|
|
||||||
@ -96,100 +93,12 @@ public interface IBinaryStorageSvc {
|
|||||||
|
|
||||||
void expungeBlob(IIdType theResourceId, String theBlobId);
|
void expungeBlob(IIdType theResourceId, String theBlobId);
|
||||||
|
|
||||||
|
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
|
||||||
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
|
||||||
class StoredDetails {
|
|
||||||
|
|
||||||
@JsonProperty("blobId")
|
|
||||||
private String myBlobId;
|
|
||||||
@JsonProperty("bytes")
|
|
||||||
private long myBytes;
|
|
||||||
@JsonProperty("contentType")
|
|
||||||
private String myContentType;
|
|
||||||
@JsonProperty("hash")
|
|
||||||
private String myHash;
|
|
||||||
@JsonProperty("published")
|
|
||||||
@JsonSerialize(using = JsonDateSerializer.class)
|
|
||||||
@JsonDeserialize(using = JsonDateDeserializer.class)
|
|
||||||
private Date myPublished;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Fetch the contents of the given blob
|
||||||
|
*
|
||||||
|
* @param theResourceId The resource ID
|
||||||
|
* @param theBlobId The blob ID
|
||||||
|
* @return The payload as a byte array
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
byte[] fetchBlob(IIdType theResourceId, String theBlobId) throws IOException;
|
||||||
public StoredDetails() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
public StoredDetails(@Nonnull String theBlobId, long theBytes, @Nonnull String theContentType, HashingInputStream theIs, Date thePublished) {
|
|
||||||
myBlobId = theBlobId;
|
|
||||||
myBytes = theBytes;
|
|
||||||
myContentType = theContentType;
|
|
||||||
myHash = theIs.hash().toString();
|
|
||||||
myPublished = thePublished;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return new ToStringBuilder(this)
|
|
||||||
.append("blobId", myBlobId)
|
|
||||||
.append("bytes", myBytes)
|
|
||||||
.append("contentType", myContentType)
|
|
||||||
.append("hash", myHash)
|
|
||||||
.append("published", myPublished)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getHash() {
|
|
||||||
return myHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredDetails setHash(String theHash) {
|
|
||||||
myHash = theHash;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getPublished() {
|
|
||||||
return myPublished;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredDetails setPublished(Date thePublished) {
|
|
||||||
myPublished = thePublished;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public String getContentType() {
|
|
||||||
return myContentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredDetails setContentType(String theContentType) {
|
|
||||||
myContentType = theContentType;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
|
||||||
public String getBlobId() {
|
|
||||||
return myBlobId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredDetails setBlobId(String theBlobId) {
|
|
||||||
myBlobId = theBlobId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getBytes() {
|
|
||||||
return myBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StoredDetails setBytes(long theBytes) {
|
|
||||||
myBytes = theBytes;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
package ca.uhn.fhir.jpa.binstore;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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.jpa.model.util.JpaConstants;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps an Attachment datatype or Binary resource, since they both
|
||||||
|
* hold binary content but don't look entirely similar
|
||||||
|
*/
|
||||||
|
interface IBinaryTarget {
|
||||||
|
|
||||||
|
void setSize(Integer theSize);
|
||||||
|
|
||||||
|
String getContentType();
|
||||||
|
|
||||||
|
void setContentType(String theContentType);
|
||||||
|
|
||||||
|
byte[] getData();
|
||||||
|
|
||||||
|
void setData(byte[] theBytes);
|
||||||
|
|
||||||
|
IBaseHasExtensions getTarget();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
default Optional<String> getAttachmentId() {
|
||||||
|
return getTarget()
|
||||||
|
.getExtension()
|
||||||
|
.stream()
|
||||||
|
.filter(t -> JpaConstants.EXT_EXTERNALIZED_BINARY_ID.equals(t.getUrl()))
|
||||||
|
.filter(t -> t.getValue() instanceof IPrimitiveType)
|
||||||
|
.map(t -> (IPrimitiveType<String>) t.getValue())
|
||||||
|
.map(t -> t.getValue())
|
||||||
|
.filter(t -> isNotBlank(t))
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
}
|
@ -49,8 +49,8 @@ public class MemoryBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl impleme
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoredDetails storeBlob(IIdType theResourceId, String theContentType, InputStream theInputStream) throws IOException {
|
public StoredDetails storeBlob(IIdType theResourceId, String theBlobIdOrNull, String theContentType, InputStream theInputStream) throws IOException {
|
||||||
String id = newRandomId();
|
String id = super.provideIdForNewBlob(theBlobIdOrNull);
|
||||||
String key = toKey(theResourceId, id);
|
String key = toKey(theResourceId, id);
|
||||||
|
|
||||||
HashingInputStream hashingIs = createHashingInputStream(theInputStream);
|
HashingInputStream hashingIs = createHashingInputStream(theInputStream);
|
||||||
@ -88,8 +88,18 @@ public class MemoryBinaryStorageSvcImpl extends BaseBinaryStorageSvcImpl impleme
|
|||||||
myDetailsMap.remove(key);
|
myDetailsMap.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] fetchBlob(IIdType theResourceId, String theBlobId) {
|
||||||
|
String key = toKey(theResourceId, theBlobId);
|
||||||
|
return myDataMap.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
private String toKey(IIdType theResourceId, String theBlobId) {
|
private String toKey(IIdType theResourceId, String theBlobId) {
|
||||||
return theBlobId + '-' + theResourceId.toUnqualifiedVersionless().getValue();
|
return theBlobId + '-' + theResourceId.toUnqualifiedVersionless().getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void clear() {
|
||||||
|
myDetailsMap.clear();
|
||||||
|
myDataMap.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,12 @@ public class NullBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StoredDetails storeBlob(IIdType theResourceId, String theContentType, InputStream theInputStream) {
|
public String newBlobId() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoredDetails storeBlob(IIdType theResourceId, String theBlobIdOrNull, String theContentType, InputStream theInputStream) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,4 +76,9 @@ public class NullBinaryStorageSvcImpl implements IBinaryStorageSvc {
|
|||||||
public void expungeBlob(IIdType theIdElement, String theBlobId) {
|
public void expungeBlob(IIdType theIdElement, String theBlobId) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] fetchBlob(IIdType theResourceId, String theBlobId) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,130 @@
|
|||||||
|
package ca.uhn.fhir.jpa.binstore;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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.jpa.util.JsonDateDeserializer;
|
||||||
|
import ca.uhn.fhir.jpa.util.JsonDateSerializer;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
|
import com.google.common.hash.HashingInputStream;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
|
@JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE)
|
||||||
|
public class StoredDetails {
|
||||||
|
|
||||||
|
@JsonProperty("blobId")
|
||||||
|
private String myBlobId;
|
||||||
|
@JsonProperty("bytes")
|
||||||
|
private long myBytes;
|
||||||
|
@JsonProperty("contentType")
|
||||||
|
private String myContentType;
|
||||||
|
@JsonProperty("hash")
|
||||||
|
private String myHash;
|
||||||
|
@JsonProperty("published")
|
||||||
|
@JsonSerialize(using = JsonDateSerializer.class)
|
||||||
|
@JsonDeserialize(using = JsonDateDeserializer.class)
|
||||||
|
private Date myPublished;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public StoredDetails() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public StoredDetails(@Nonnull String theBlobId, long theBytes, @Nonnull String theContentType, HashingInputStream theIs, Date thePublished) {
|
||||||
|
myBlobId = theBlobId;
|
||||||
|
myBytes = theBytes;
|
||||||
|
myContentType = theContentType;
|
||||||
|
myHash = theIs.hash().toString();
|
||||||
|
myPublished = thePublished;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return new ToStringBuilder(this)
|
||||||
|
.append("blobId", myBlobId)
|
||||||
|
.append("bytes", myBytes)
|
||||||
|
.append("contentType", myContentType)
|
||||||
|
.append("hash", myHash)
|
||||||
|
.append("published", myPublished)
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHash() {
|
||||||
|
return myHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredDetails setHash(String theHash) {
|
||||||
|
myHash = theHash;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getPublished() {
|
||||||
|
return myPublished;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredDetails setPublished(Date thePublished) {
|
||||||
|
myPublished = thePublished;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public String getContentType() {
|
||||||
|
return myContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredDetails setContentType(String theContentType) {
|
||||||
|
myContentType = theContentType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public String getBlobId() {
|
||||||
|
return myBlobId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredDetails setBlobId(String theBlobId) {
|
||||||
|
myBlobId = theBlobId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getBytes() {
|
||||||
|
return myBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StoredDetails setBytes(long theBytes) {
|
||||||
|
myBytes = theBytes;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -165,7 +165,7 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc {
|
|||||||
|
|
||||||
if (jobToDelete.isPresent()) {
|
if (jobToDelete.isPresent()) {
|
||||||
|
|
||||||
ourLog.info("Deleting bulk export job: {}", jobToDelete.get().getJobId());
|
ourLog.info("Deleting bulk export job: {}", jobToDelete.get());
|
||||||
|
|
||||||
myTxTemplate.execute(t -> {
|
myTxTemplate.execute(t -> {
|
||||||
|
|
||||||
@ -176,17 +176,20 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc {
|
|||||||
ourLog.info("Purging bulk data file: {}", nextFile.getResourceId());
|
ourLog.info("Purging bulk data file: {}", nextFile.getResourceId());
|
||||||
getBinaryDao().delete(toId(nextFile.getResourceId()));
|
getBinaryDao().delete(toId(nextFile.getResourceId()));
|
||||||
getBinaryDao().forceExpungeInExistingTransaction(toId(nextFile.getResourceId()), new ExpungeOptions().setExpungeDeletedResources(true).setExpungeOldVersions(true), null);
|
getBinaryDao().forceExpungeInExistingTransaction(toId(nextFile.getResourceId()), new ExpungeOptions().setExpungeDeletedResources(true).setExpungeOldVersions(true), null);
|
||||||
myBulkExportCollectionFileDao.delete(nextFile);
|
myBulkExportCollectionFileDao.deleteByPid(nextFile.getId());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
myBulkExportCollectionDao.delete(nextCollection);
|
myBulkExportCollectionDao.deleteByPid(nextCollection.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
myBulkExportJobDao.delete(job);
|
ourLog.info("*** ABOUT TO DELETE");
|
||||||
|
myBulkExportJobDao.deleteByPid(job.getId());
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ourLog.info("Finished deleting bulk export job: {}", jobToDelete.get());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -452,11 +455,17 @@ public class BulkDataExportSvcImpl implements IBulkDataExportSvc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(Transactional.TxType.NEVER)
|
||||||
public synchronized void cancelAndPurgeAllJobs() {
|
public synchronized void cancelAndPurgeAllJobs() {
|
||||||
myBulkExportCollectionFileDao.deleteAll();
|
myTxTemplate.execute(t -> {
|
||||||
myBulkExportCollectionDao.deleteAll();
|
ourLog.info("Deleting all files");
|
||||||
myBulkExportJobDao.deleteAll();
|
myBulkExportCollectionFileDao.deleteAllFiles();
|
||||||
|
ourLog.info("Deleting all collections");
|
||||||
|
myBulkExportCollectionDao.deleteAllFiles();
|
||||||
|
ourLog.info("Deleting all jobs");
|
||||||
|
myBulkExportJobDao.deleteAllFiles();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@DisallowConcurrentExecution
|
@DisallowConcurrentExecution
|
||||||
|
@ -6,8 +6,8 @@ import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
|||||||
import ca.uhn.fhir.interceptor.executor.InterceptorService;
|
import ca.uhn.fhir.interceptor.executor.InterceptorService;
|
||||||
import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider;
|
import ca.uhn.fhir.jpa.binstore.BinaryAccessProvider;
|
||||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||||
import ca.uhn.fhir.jpa.bulk.BulkDataExportSvcImpl;
|
|
||||||
import ca.uhn.fhir.jpa.bulk.BulkDataExportProvider;
|
import ca.uhn.fhir.jpa.bulk.BulkDataExportProvider;
|
||||||
|
import ca.uhn.fhir.jpa.bulk.BulkDataExportSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc;
|
import ca.uhn.fhir.jpa.bulk.IBulkDataExportSvc;
|
||||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
import ca.uhn.fhir.jpa.graphql.JpaStorageServices;
|
||||||
@ -28,10 +28,17 @@ import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
|
|||||||
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
|
import ca.uhn.fhir.jpa.search.reindex.ResourceReindexingSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.subscription.dbmatcher.CompositeInMemoryDaoSubscriptionMatcher;
|
import ca.uhn.fhir.jpa.subscription.dbmatcher.CompositeInMemoryDaoSubscriptionMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.dbmatcher.DaoSubscriptionMatcher;
|
import ca.uhn.fhir.jpa.subscription.dbmatcher.DaoSubscriptionMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.module.cache.ISubscribableChannelFactory;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.module.cache.LinkedBlockingQueueSubscribableChannelFactory;
|
import ca.uhn.fhir.jpa.subscription.module.cache.LinkedBlockingQueueSubscribableChannelFactory;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.channel.ISubscribableChannelFactory;
|
||||||
import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
import ca.uhn.fhir.jpa.subscription.module.matcher.ISubscriptionMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
|
import ca.uhn.fhir.jpa.subscription.module.matcher.InMemorySubscriptionMatcher;
|
||||||
|
import ca.uhn.fhir.jpa.term.TermCodeSystemStorageSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc;
|
||||||
|
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermReindexingSvc;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
|
import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
|
||||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||||
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
|
||||||
@ -256,7 +263,6 @@ public abstract class BaseConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void configureEntityManagerFactory(LocalContainerEntityManagerFactoryBean theFactory, FhirContext theCtx) {
|
public static void configureEntityManagerFactory(LocalContainerEntityManagerFactoryBean theFactory, FhirContext theCtx) {
|
||||||
theFactory.setJpaDialect(hibernateJpaDialect(theCtx.getLocalizer()));
|
theFactory.setJpaDialect(hibernateJpaDialect(theCtx.getLocalizer()));
|
||||||
theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity");
|
theFactory.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity");
|
||||||
|
@ -0,0 +1,54 @@
|
|||||||
|
package ca.uhn.fhir.jpa.config;
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR JPA Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2019 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.jpa.term.TermCodeSystemStorageSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.TermDeferredStorageSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermReindexingSvc;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermVersionAdapterSvc;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public abstract class BaseConfigDstu3Plus extends BaseConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ITermCodeSystemStorageSvc termCodeSystemStorageSvc() {
|
||||||
|
return new TermCodeSystemStorageSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ITermDeferredStorageSvc termDeferredStorageSvc() {
|
||||||
|
return new TermDeferredStorageSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ITermReindexingSvc termReindexingSvc() {
|
||||||
|
return new TermReindexingSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public abstract ITermVersionAdapterSvc terminologyVersionAdapterSvc();
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user