diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java
index eb2dbba07f9..91a622301cd 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java
@@ -32,9 +32,11 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -77,9 +79,9 @@ public interface IValidationSupport {
* Expands the given portion of a ValueSet
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
- * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
- * @param theExpansionOptions If provided (may be null), contains options controlling the expansion
- * @param theValueSetToExpand The valueset that should be expanded
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * @param theExpansionOptions If provided (may be null), contains options controlling the expansion
+ * @param theValueSetToExpand The valueset that should be expanded
* @return The expansion, or null
*/
default ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, @Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
@@ -116,15 +118,33 @@ public interface IValidationSupport {
* Loads a resource needed by the validation (a StructureDefinition, or a
* ValueSet)
*
- * @param theClass The type of the resource to load
+ *
+ * Note: Since 5.3.0, {@literal theClass} can be {@literal null}
+ *
+ *
+ * @param theClass The type of the resource to load, or null to return any resource with the given canonical URI
* @param theUri The resource URI
* @return Returns the resource, or null if no resource with the
* given URI can be found
*/
- default T fetchResource(Class theClass, String theUri) {
- Validate.notNull(theClass, "theClass must not be null or blank");
+ @SuppressWarnings("unchecked")
+ default T fetchResource(@Nullable Class theClass, String theUri) {
Validate.notBlank(theUri, "theUri must not be null or blank");
+ if (theClass == null) {
+ Supplier[] sources = new Supplier[]{
+ () -> fetchStructureDefinition(theUri),
+ () -> fetchValueSet(theUri),
+ () -> fetchCodeSystem(theUri)
+ };
+ return (T) Arrays
+ .stream(sources)
+ .map(t -> t.get())
+ .filter(t -> t != null)
+ .findFirst()
+ .orElse(null);
+ }
+
switch (getFhirContext().getResourceType(theClass)) {
case "StructureDefinition":
return theClass.cast(fetchStructureDefinition(theUri));
@@ -150,8 +170,8 @@ public interface IValidationSupport {
* or validated
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
- * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
- * @param theSystem The URI for the code system, e.g. "http://loinc.org"
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * @param theSystem The URI for the code system, e.g. "http://loinc.org"
* @return Returns true if codes in the given code system can be
* validated
*/
@@ -172,11 +192,11 @@ public interface IValidationSupport {
* binding fields (e.g. Observation.code in the default profile.
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
- * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
- * @param theOptions Provides options controlling the validation
- * @param theCodeSystem The code system, e.g. "http://loinc.org"
- * @param theCode The code, e.g. "1234-5"
- * @param theDisplay The display name, if it should also be validated
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * @param theOptions Provides options controlling the validation
+ * @param theCodeSystem The code system, e.g. "http://loinc.org"
+ * @param theCode The code, e.g. "1234-5"
+ * @param theDisplay The display name, if it should also be validated
* @return Returns a validation result object
*/
default CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
@@ -189,11 +209,11 @@ public interface IValidationSupport {
* binding fields (e.g. Observation.code in the default profile.
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
- * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
- * @param theCodeSystem The code system, e.g. "http://loinc.org"
- * @param theCode The code, e.g. "1234-5"
- * @param theDisplay The display name, if it should also be validated
- * @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * @param theCodeSystem The code system, e.g. "http://loinc.org"
+ * @param theCode The code, e.g. "1234-5"
+ * @param theDisplay The display name, if it should also be validated
+ * @param theValueSet The ValueSet to validate against. Must not be null, and must be a ValueSet resource.
* @return Returns a validation result object, or null if this validation support module can not handle this kind of request
*/
default CodeValidationResult validateCodeInValueSet(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, @Nonnull IBaseResource theValueSet) {
@@ -204,9 +224,9 @@ public interface IValidationSupport {
* Look up a code using the system and code value
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
- * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
- * @param theSystem The CodeSystem URL
- * @param theCode The code
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * @param theSystem The CodeSystem URL
+ * @param theCode The code
*/
default LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode) {
return null;
@@ -217,8 +237,8 @@ public interface IValidationSupport {
* validation support module
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
- * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
- * @param theValueSetUrl The ValueSet canonical URL
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * @param theValueSetUrl The ValueSet canonical URL
*/
default boolean isValueSetSupported(ValidationSupportContext theValidationSupportContext, String theValueSetUrl) {
return false;
@@ -228,7 +248,7 @@ public interface IValidationSupport {
* Generate a snapshot from the given differential profile.
*
* @param theValidationSupportContext The validation support module will be passed in to this method. This is convenient in cases where the operation needs to make calls to
- * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
+ * other method in the support chain, so that they can be passed through the entire chain. Implementations of this interface may always safely ignore this parameter.
* @return Returns null if this module does not know how to handle this request
*/
default IBaseResource generateSnapshot(ValidationSupportContext theValidationSupportContext, IBaseResource theInput, String theUrl, String theWebUrl, String theProfileName) {
@@ -249,6 +269,25 @@ public interface IValidationSupport {
}
+ enum IssueSeverity {
+ /**
+ * The issue caused the action to fail, and no further checking could be performed.
+ */
+ FATAL,
+ /**
+ * The issue is sufficiently important to cause the action to fail.
+ */
+ ERROR,
+ /**
+ * The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.
+ */
+ WARNING,
+ /**
+ * The issue has no relation to the degree of success of the action.
+ */
+ INFORMATION
+ }
+
class ConceptDesignation {
private String myLanguage;
private String myUseSystem;
@@ -365,25 +404,6 @@ public interface IValidationSupport {
}
}
- enum IssueSeverity {
- /**
- * The issue caused the action to fail, and no further checking could be performed.
- */
- FATAL,
- /**
- * The issue is sufficiently important to cause the action to fail.
- */
- ERROR,
- /**
- * The issue is not important enough to cause the action to fail, but may cause it to be performed suboptimally or in a way that is not as desired.
- */
- WARNING,
- /**
- * The issue has no relation to the degree of success of the action.
- */
- INFORMATION
- }
-
class CodeValidationResult {
private String myCode;
private String myMessage;
diff --git a/hapi-fhir-docs/src/test/java/ca/uhn/hapi/fhir/docs/ChangelogFilesTest.java b/hapi-fhir-docs/src/test/java/ca/uhn/hapi/fhir/docs/ChangelogFilesTest.java
index 399d889bfa3..579982577ce 100644
--- a/hapi-fhir-docs/src/test/java/ca/uhn/hapi/fhir/docs/ChangelogFilesTest.java
+++ b/hapi-fhir-docs/src/test/java/ca/uhn/hapi/fhir/docs/ChangelogFilesTest.java
@@ -66,6 +66,9 @@ public class ChangelogFilesTest {
// this one is optional
boolean haveIssue = fieldNames.remove("issue");
+ // this one is optional
+ fieldNames.remove("backport");
+
assertThat("Invalid element in " + next + ": " + fieldNames, fieldNames, empty());
if (haveIssue) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java
index 8ac6b6ace81..07648601c5b 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/binstore/FilesystemBinaryStorageSvcImpl.java
@@ -32,11 +32,11 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IIdType;
-import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import java.io.*;
import java.util.Date;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
index d5ad689a154..04c29943127 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java
@@ -103,8 +103,6 @@ import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.data.domain.SliceImpl;
@@ -539,7 +537,7 @@ public abstract class BaseHapiFhirResourceDao extends B
return myDeleteExpungeService.expungeByResourcePids(theUrl, myResourceName, new SliceImpl<>(ResourcePersistentId.toLongList(theResourceIds)), theTheRequest);
}
- @NotNull
+ @Nonnull
@Override
public DeleteMethodOutcome deletePidList(String theUrl, Collection theResourceIds, DeleteConflictList theDeleteConflicts, RequestDetails theRequest) {
StopWatch w = new StopWatch();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
index 51e5f2dc559..a5a89c5a84a 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FulltextSearchSvcImpl.java
@@ -20,54 +20,34 @@ package ca.uhn.fhir.jpa.dao;
* #L%
*/
-import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
-import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
+import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam;
-import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
-import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.Validate;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.TokenStream;
-import org.apache.lucene.search.highlight.Formatter;
-import org.apache.lucene.search.highlight.Highlighter;
-import org.apache.lucene.search.highlight.Scorer;
-import org.apache.lucene.search.highlight.TextFragment;
-import org.apache.lucene.search.highlight.TokenGroup;
-import org.hibernate.search.backend.lucene.index.LuceneIndexManager;
import org.hibernate.search.engine.search.predicate.dsl.BooleanPredicateClausesStep;
import org.hibernate.search.engine.search.predicate.dsl.SearchPredicateFactory;
-import org.hibernate.search.engine.search.query.SearchQuery;
import org.hibernate.search.mapper.orm.Search;
-import org.hibernate.search.mapper.orm.mapping.SearchMapping;
import org.hibernate.search.mapper.orm.session.SearchSession;
import org.hl7.fhir.instance.model.api.IAnyResource;
-import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
+import javax.annotation.Nonnull;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -76,20 +56,20 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FulltextSearchSvcImpl.class);
-
- @PersistenceContext(type = PersistenceContextType.TRANSACTION)
- private EntityManager myEntityManager;
-
- @Autowired
- private PlatformTransactionManager myTxManager;
-
@Autowired
protected IForcedIdDao myForcedIdDao;
-
+ @PersistenceContext(type = PersistenceContextType.TRANSACTION)
+ private EntityManager myEntityManager;
+ @Autowired
+ private PlatformTransactionManager myTxManager;
@Autowired
private IdHelperService myIdHelperService;
private Boolean ourDisabled;
+ @Autowired
+ private IRequestPartitionHelperSvc myRequestPartitionHelperService;
+ @Autowired
+ private PartitionSettings myPartitionSettings;
/**
* Constructor
@@ -98,28 +78,28 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
super();
}
- private void addTextSearch(SearchPredicateFactory f, BooleanPredicateClausesStep> b, List> theTerms, String theFieldName, String theFieldNameEdgeNGram, String theFieldNameTextNGram){
+ private void addTextSearch(SearchPredicateFactory f, BooleanPredicateClausesStep> b, List> theTerms, String theFieldName, String theFieldNameEdgeNGram, String theFieldNameTextNGram) {
if (theTerms == null) {
return;
}
for (List extends IQueryParameterType> nextAnd : theTerms) {
Set terms = extractOrStringParams(nextAnd);
- if (terms.size() == 1) {
- b.must(f.phrase()
- .field(theFieldName)
- .boost(4.0f)
- .matching(terms.iterator().next().toLowerCase())
- .slop(2));
- } else if (terms.size() > 1){
- String joinedTerms = StringUtils.join(terms, ' ');
- b.must(f.match().field(theFieldName).matching(joinedTerms));
- } else {
- ourLog.debug("No Terms found in query parameter {}", nextAnd);
- }
+ if (terms.size() == 1) {
+ b.must(f.phrase()
+ .field(theFieldName)
+ .boost(4.0f)
+ .matching(terms.iterator().next().toLowerCase())
+ .slop(2));
+ } else if (terms.size() > 1) {
+ String joinedTerms = StringUtils.join(terms, ' ');
+ b.must(f.match().field(theFieldName).matching(joinedTerms));
+ } else {
+ ourLog.debug("No Terms found in query parameter {}", nextAnd);
}
}
+ }
- @NotNull
+ @Nonnull
private Set extractOrStringParams(List extends IQueryParameterType> nextAnd) {
Set terms = new HashSet<>();
for (IQueryParameterType nextOr : nextAnd) {
@@ -229,10 +209,4 @@ public class FulltextSearchSvcImpl implements IFulltextSearchSvc {
return doSearch(theResourceName, theParams, null);
}
- @Autowired
- private IRequestPartitionHelperSvc myRequestPartitionHelperService;
-
- @Autowired
- private PartitionSettings myPartitionSettings;
-
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java
index c5e177d7e45..d3a1fe2adf1 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/JpaPersistedResourceValidationSupport.java
@@ -46,9 +46,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
import javax.transaction.Transactional;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
import static org.apache.commons.lang3.StringUtils.isBlank;
@@ -104,110 +107,13 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
@Override
@SuppressWarnings({"unchecked", "unused"})
- public T fetchResource(Class theClass, String theUri) {
+ public T fetchResource(@Nullable Class theClass, String theUri) {
if (isBlank(theUri)) {
return null;
}
- String key = theClass.getSimpleName() + " " + theUri;
- IBaseResource fetched = myLoadCache.get(key, t -> {
- IdType id = new IdType(theUri);
- boolean localReference = false;
- if (id.hasBaseUrl() == false && id.hasIdPart() == true) {
- localReference = true;
- }
-
- String resourceName = myFhirContext.getResourceType(theClass);
- IBundleProvider search;
- switch (resourceName) {
- case "ValueSet":
- if (localReference) {
- SearchParameterMap params = new SearchParameterMap();
- params.setLoadSynchronousUpTo(1);
- params.add(IAnyResource.SP_RES_ID, new StringParam(theUri));
- search = myDaoRegistry.getResourceDao(resourceName).search(params);
- if (search.size() == 0) {
- params = new SearchParameterMap();
- params.setLoadSynchronousUpTo(1);
- params.add(ValueSet.SP_URL, new UriParam(theUri));
- search = myDaoRegistry.getResourceDao(resourceName).search(params);
- }
- } else {
- int versionSeparator = theUri.lastIndexOf('|');
- SearchParameterMap params = new SearchParameterMap();
- params.setLoadSynchronousUpTo(1);
- if (versionSeparator != -1) {
- params.add(ValueSet.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1)));
- params.add(ValueSet.SP_URL, new UriParam(theUri.substring(0, versionSeparator)));
- } else {
- params.add(ValueSet.SP_URL, new UriParam(theUri));
- }
- params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC));
- search = myDaoRegistry.getResourceDao(resourceName).search(params);
- }
- break;
- case "StructureDefinition": {
- // Don't allow the core FHIR definitions to be overwritten
- if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
- String typeName = theUri.substring("http://hl7.org/fhir/StructureDefinition/".length());
- if (myFhirContext.getElementDefinition(typeName) != null) {
- return myNoMatch;
- }
- }
- SearchParameterMap params = new SearchParameterMap();
- params.setLoadSynchronousUpTo(1);
- params.add(StructureDefinition.SP_URL, new UriParam(theUri));
- search = myDaoRegistry.getResourceDao("StructureDefinition").search(params);
- break;
- }
- case "Questionnaire": {
- SearchParameterMap params = new SearchParameterMap();
- params.setLoadSynchronousUpTo(1);
- if (localReference || myFhirContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.DSTU2)) {
- params.add(IAnyResource.SP_RES_ID, new StringParam(id.getIdPart()));
- } else {
- params.add(Questionnaire.SP_URL, new UriParam(id.getValue()));
- }
- search = myDaoRegistry.getResourceDao("Questionnaire").search(params);
- break;
- }
- case "CodeSystem": {
- int versionSeparator = theUri.lastIndexOf('|');
- SearchParameterMap params = new SearchParameterMap();
- params.setLoadSynchronousUpTo(1);
- if (versionSeparator != -1) {
- params.add(CodeSystem.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1)));
- params.add(CodeSystem.SP_URL, new UriParam(theUri.substring(0, versionSeparator)));
- } else {
- params.add(CodeSystem.SP_URL, new UriParam(theUri));
- }
- params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC));
- search = myDaoRegistry.getResourceDao(resourceName).search(params);
- break;
- }
- case "ImplementationGuide":
- case "SearchParameter": {
- SearchParameterMap params = new SearchParameterMap();
- params.setLoadSynchronousUpTo(1);
- params.add(ImplementationGuide.SP_URL, new UriParam(theUri));
- search = myDaoRegistry.getResourceDao(resourceName).search(params);
- break;
- }
- default:
- throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
- }
-
- Integer size = search.size();
- if (size == null || size == 0) {
- return myNoMatch;
- }
-
- if (size > 1) {
- ourLog.warn("Found multiple {} instances with URL search value of: {}", resourceName, theUri);
- }
-
- return search.getResources(0, 1).get(0);
- });
+ String key = theClass + " " + theUri;
+ IBaseResource fetched = myLoadCache.get(key, t -> doFetchResource(theClass, theUri));
if (fetched == myNoMatch) {
return null;
@@ -216,6 +122,119 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
return (T) fetched;
}
+ private IBaseResource doFetchResource(@Nullable Class theClass, String theUri) {
+ if (theClass == null) {
+ Supplier[] fetchers = new Supplier[]{
+ () -> doFetchResource(ValueSet.class, theUri),
+ () -> doFetchResource(CodeSystem.class, theUri),
+ () -> doFetchResource(StructureDefinition.class, theUri)
+ };
+ return Arrays
+ .stream(fetchers)
+ .map(t -> t.get())
+ .filter(t -> t != myNoMatch)
+ .findFirst()
+ .orElse(myNoMatch);
+ }
+
+ IdType id = new IdType(theUri);
+ boolean localReference = false;
+ if (id.hasBaseUrl() == false && id.hasIdPart() == true) {
+ localReference = true;
+ }
+
+ String resourceName = myFhirContext.getResourceType(theClass);
+ IBundleProvider search;
+ switch (resourceName) {
+ case "ValueSet":
+ if (localReference) {
+ SearchParameterMap params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ params.add(IAnyResource.SP_RES_ID, new StringParam(theUri));
+ search = myDaoRegistry.getResourceDao(resourceName).search(params);
+ if (search.size() == 0) {
+ params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ params.add(ValueSet.SP_URL, new UriParam(theUri));
+ search = myDaoRegistry.getResourceDao(resourceName).search(params);
+ }
+ } else {
+ int versionSeparator = theUri.lastIndexOf('|');
+ SearchParameterMap params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ if (versionSeparator != -1) {
+ params.add(ValueSet.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1)));
+ params.add(ValueSet.SP_URL, new UriParam(theUri.substring(0, versionSeparator)));
+ } else {
+ params.add(ValueSet.SP_URL, new UriParam(theUri));
+ }
+ params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC));
+ search = myDaoRegistry.getResourceDao(resourceName).search(params);
+ }
+ break;
+ case "StructureDefinition": {
+ // Don't allow the core FHIR definitions to be overwritten
+ if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
+ String typeName = theUri.substring("http://hl7.org/fhir/StructureDefinition/".length());
+ if (myFhirContext.getElementDefinition(typeName) != null) {
+ return myNoMatch;
+ }
+ }
+ SearchParameterMap params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ params.add(StructureDefinition.SP_URL, new UriParam(theUri));
+ search = myDaoRegistry.getResourceDao("StructureDefinition").search(params);
+ break;
+ }
+ case "Questionnaire": {
+ SearchParameterMap params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ if (localReference || myFhirContext.getVersion().getVersion().isEquivalentTo(FhirVersionEnum.DSTU2)) {
+ params.add(IAnyResource.SP_RES_ID, new StringParam(id.getIdPart()));
+ } else {
+ params.add(Questionnaire.SP_URL, new UriParam(id.getValue()));
+ }
+ search = myDaoRegistry.getResourceDao("Questionnaire").search(params);
+ break;
+ }
+ case "CodeSystem": {
+ int versionSeparator = theUri.lastIndexOf('|');
+ SearchParameterMap params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ if (versionSeparator != -1) {
+ params.add(CodeSystem.SP_VERSION, new TokenParam(theUri.substring(versionSeparator + 1)));
+ params.add(CodeSystem.SP_URL, new UriParam(theUri.substring(0, versionSeparator)));
+ } else {
+ params.add(CodeSystem.SP_URL, new UriParam(theUri));
+ }
+ params.setSort(new SortSpec("_lastUpdated").setOrder(SortOrderEnum.DESC));
+ search = myDaoRegistry.getResourceDao(resourceName).search(params);
+ break;
+ }
+ case "ImplementationGuide":
+ case "SearchParameter": {
+ SearchParameterMap params = new SearchParameterMap();
+ params.setLoadSynchronousUpTo(1);
+ params.add(ImplementationGuide.SP_URL, new UriParam(theUri));
+ search = myDaoRegistry.getResourceDao(resourceName).search(params);
+ break;
+ }
+ default:
+ throw new IllegalArgumentException("Can't fetch resource type: " + resourceName);
+ }
+
+ Integer size = search.size();
+ if (size == null || size == 0) {
+ return myNoMatch;
+ }
+
+ if (size > 1) {
+ ourLog.warn("Found multiple {} instances with URL search value of: {}", resourceName, theUri);
+ }
+
+ return search.getResources(0, 1).get(0);
+ }
+
@Override
public FhirContext getFhirContext() {
return myFhirContext;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java
index e148a42d6ea..ce5d66f0a6f 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/PartitionManagementProvider.java
@@ -29,9 +29,10 @@ import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
-import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
+import javax.annotation.Nonnull;
+
import static ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.validatePartitionIdSupplied;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hl7.fhir.instance.model.api.IPrimitiveType.toValueOrNull;
@@ -151,7 +152,7 @@ public class PartitionManagementProvider {
return retVal;
}
- @NotNull
+ @Nonnull
private PartitionEntity parseInput(@OperationParam(name = ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, min = 1, max = 1, typeName = "integer") IPrimitiveType thePartitionId, @OperationParam(name = ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, min = 1, max = 1, typeName = "code") IPrimitiveType thePartitionName, @OperationParam(name = ProviderConstants.PARTITION_MANAGEMENT_PARTITION_DESC, min = 0, max = 1, typeName = "string") IPrimitiveType thePartitionDescription) {
PartitionEntity input = new PartitionEntity();
if (thePartitionId != null) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java
index a6f27c7c177..8d1f45b35cf 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/PersistedJpaBundleProvider.java
@@ -52,7 +52,6 @@ import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -191,7 +190,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
return retVal;
}
- @NotNull
+ @Nonnull
private RequestPartitionId getRequestPartitionId() {
if (myRequestPartitionId == null) {
if (mySearchEntity.getResourceId() != null) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
index 6878a1d2934..80328cf2f73 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/SearchCoordinatorSvcImpl.java
@@ -576,7 +576,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
return null;
}
- @org.jetbrains.annotations.Nullable
+ @Nullable
private Integer getLoadSynchronousUpToOrNull(CacheControlDirective theCacheControlDirective) {
final Integer loadSynchronousUpTo;
if (theCacheControlDirective != null && theCacheControlDirective.isNoStore()) {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java
index 9255acd1496..df30c410d11 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/ValidatorResourceFetcher.java
@@ -32,13 +32,13 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.JsonParser;
+import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.utils.IResourceValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
-import java.io.IOException;
import java.util.Locale;
public class ValidatorResourceFetcher implements IResourceValidator.IValidatorResourceFetcher {
@@ -61,7 +61,6 @@ public class ValidatorResourceFetcher implements IResourceValidator.IValidatorRe
}
- @SuppressWarnings("ConstantConditions")
@Override
public Element fetch(Object appContext, String theUrl) throws FHIRException {
@@ -94,13 +93,13 @@ public class ValidatorResourceFetcher implements IResourceValidator.IValidatorRe
}
@Override
- public boolean resolveURL(Object appContext, String path, String url) throws IOException, FHIRException {
+ public boolean resolveURL(Object appContext, String path, String url, String type) throws FHIRException {
return true;
}
@Override
- public byte[] fetchRaw(String url) throws IOException {
- return new byte[0];
+ public byte[] fetchRaw(String url) {
+ throw new UnsupportedOperationException();
}
@Override
@@ -108,4 +107,14 @@ public class ValidatorResourceFetcher implements IResourceValidator.IValidatorRe
// ignore
}
+ @Override
+ public CanonicalResource fetchCanonicalResource(String url) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean fetchesCanonicalResource(String url) {
+ return false;
+ }
+
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java
index cc6897acf1f..6b3ab511fe9 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2ValidateTest.java
@@ -144,7 +144,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
} catch (PreconditionFailedException e) {
String ooString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome());
ourLog.info(ooString);
- assertThat(ooString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
+ assertThat(ooString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"));
}
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java
index 8b50cd2c2ed..ab04c07c46f 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3ValidateTest.java
@@ -333,7 +333,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
ourLog.info(outputString);
- assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
+ assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"));
}
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java
index 06e7d516992..72b16b4d25f 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4ValidateTest.java
@@ -1138,7 +1138,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) e.getOperationOutcome();
String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
ourLog.info(outputString);
- assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
+ assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"));
}
}
@@ -1174,7 +1174,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) e.getOperationOutcome();
String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
ourLog.info(outputString);
- assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
+ assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' has not been checked because it is unknown"));
}
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java
index ab609103900..ad02f5fc22a 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/partition/PartitionManagementProviderTest.java
@@ -12,7 +12,6 @@ import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.StringType;
-import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -28,6 +27,8 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
+import javax.annotation.Nonnull;
+
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -87,7 +88,7 @@ public class PartitionManagementProviderTest {
assertEquals("a description", ((StringType) response.getParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_DESC)).getValue());
}
- @NotNull
+ @Nonnull
private Parameters createInputPartition() {
Parameters input = new Parameters();
input.addParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(123));
@@ -248,7 +249,7 @@ public class PartitionManagementProviderTest {
}
- @NotNull
+ @Nonnull
private static Answer