diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java
index acc3759746b..58807b32839 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java
@@ -45,7 +45,7 @@ public interface ISort {
/**
* Sort descending
*
- * @param A query param - Could be a constant such as Patient.ADDRESS or a custom
+ * @param theParam A query param - Could be a constant such as Patient.ADDRESS or a custom
* param such as new StringClientParam("foo")
*/
IQuery descending(IParam theParam);
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
index c05a5b432ba..41bfee6d0e6 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java
@@ -187,12 +187,6 @@ public interface IServerInterceptor {
* A bean containing details about the request that is about to be processed, including
* @param theResponseObject
* The actual object which is being streamed to the client as a response
- * @param theServletRequest
- * The incoming request
- * @param theServletResponse
- * The response. Note that interceptors may choose to provide a response (i.e. by calling
- * {@link HttpServletResponse#getWriter()}) but in that case it is important to return false
- * to indicate that the server itself should not also provide a response.
* @return Return true if processing should continue normally. This is generally the right thing to do.
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
* must return false. In this case, no further processing will occur and no further interceptors
@@ -201,7 +195,7 @@ public interface IServerInterceptor {
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
*/
- boolean outgoingResponse(RequestDetails theRequest, Bundle bundle);
+ boolean outgoingResponse(RequestDetails theRequest, Bundle theResponseObject);
/**
* This method is called after the server implementation method has been called, but before any attempt to stream the
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java
index 20f694e62b1..7b95cb667e2 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java
@@ -20,6 +20,16 @@ package ca.uhn.fhir.jpa.config;
* #L%
*/
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.dao.*;
+import ca.uhn.fhir.jpa.term.HapiTerminologySvcDstu2;
+import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
+import ca.uhn.fhir.model.dstu2.composite.MetaDt;
+import ca.uhn.fhir.validation.IValidatorModule;
+import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport;
+import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
+import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain;
+import org.hl7.fhir.instance.validation.IResourceValidator.BestPracticeWarningLevel;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -27,17 +37,6 @@ import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement;
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl;
-import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
-import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
-import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
-import ca.uhn.fhir.jpa.dao.SearchParamExtractorDstu2;
-import ca.uhn.fhir.jpa.dao.SearchParamRegistryDstu2;
-import ca.uhn.fhir.jpa.term.HapiTerminologySvcDstu2;
-import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
-import ca.uhn.fhir.model.dstu2.composite.MetaDt;
-
@Configuration
@EnableTransactionManagement
public class BaseDstu2Config extends BaseConfig {
@@ -75,6 +74,15 @@ public class BaseDstu2Config extends BaseConfig {
return retVal;
}
+ @Bean(name = "myInstanceValidatorDstu2")
+ @Lazy
+ public IValidatorModule instanceValidatorDstu2() {
+ FhirInstanceValidator retVal = new FhirInstanceValidator();
+ retVal.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning);
+ retVal.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), jpaValidationSupportDstu2()));
+ return retVal;
+ }
+
@Bean(autowire = Autowire.BY_TYPE)
public IFulltextSearchSvc searchDao() {
FulltextSearchSvcImpl searchDao = new FulltextSearchSvcImpl();
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
index d4b5053a328..80bff07e751 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
@@ -312,29 +312,14 @@ public abstract class BaseHapiFhirDao implements IDao {
}
}
- Set treatReferencesAsLogical = myConfig.getTreatReferencesAsLogical();
- if (treatReferencesAsLogical != null) {
- boolean isLogical = false;
- for (String nextLogicalRef : treatReferencesAsLogical) {
- nextLogicalRef = trim(nextLogicalRef);
- if (nextLogicalRef.charAt(nextLogicalRef.length() - 1) == '*') {
- if (nextId.getValue().startsWith(nextLogicalRef.substring(0, nextLogicalRef.length() -1))) {
- isLogical = true;
- break;
- }
- } else {
- if (nextId.getValue().equals(nextLogicalRef)) {
- isLogical = true;
- break;
- }
- }
- }
-
- if (isLogical) {
- continue;
+ if (isLogicalReference(nextId)) {
+ ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
+ if (theLinks.add(resourceLink)) {
+ ourLog.info("Indexing remote resource reference URL: {}", nextId);
}
+ continue;
}
-
+
String baseUrl = nextId.getBaseUrl();
String typeString = nextId.getResourceType();
if (isBlank(typeString)) {
@@ -412,6 +397,26 @@ public abstract class BaseHapiFhirDao implements IDao {
}
+ protected boolean isLogicalReference(IIdType theId) {
+ Set treatReferencesAsLogical = myConfig.getTreatReferencesAsLogical();
+ if (treatReferencesAsLogical != null) {
+ for (String nextLogicalRef : treatReferencesAsLogical) {
+ nextLogicalRef = trim(nextLogicalRef);
+ if (nextLogicalRef.charAt(nextLogicalRef.length() - 1) == '*') {
+ if (theId.getValue().startsWith(nextLogicalRef.substring(0, nextLogicalRef.length() - 1))) {
+ return true;
+ }
+ } else {
+ if (theId.getValue().equals(nextLogicalRef)) {
+ return true;
+ }
+ }
+ }
+
+ }
+ return false;
+ }
+
protected Set extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
}
@@ -959,12 +964,11 @@ public abstract class BaseHapiFhirDao implements IDao {
* Subclasses may override to provide behaviour. Called when a resource has been inserted into the database for the first time.
*
* @param theEntity
- * The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
+ * The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* @param theTag
- * The tag
+ * The tag
* @return Returns true if the tag should be removed
*/
- @SuppressWarnings("unused")
protected void postPersist(ResourceTable theEntity, T theResource) {
// nothing
}
@@ -973,9 +977,9 @@ public abstract class BaseHapiFhirDao implements IDao {
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
*
* @param theEntity
- * The resource
+ * The resource
* @param theResource
- * The resource being persisted
+ * The resource being persisted
*/
protected void postUpdate(ResourceTable theEntity, T theResource) {
// nothing
@@ -998,7 +1002,6 @@ public abstract class BaseHapiFhirDao implements IDao {
return ids;
}
- @SuppressWarnings("unused")
@CoverageIgnore
public BaseHasResource readEntity(IIdType theValueId) {
throw new NotImplementedException("");
@@ -1031,9 +1034,9 @@ public abstract class BaseHapiFhirDao implements IDao {
*
*
* @param theEntity
- * The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
+ * The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* @param theTag
- * The tag
+ * The tag
* @return Retturns true if the tag should be removed
*/
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
@@ -1269,7 +1272,7 @@ public abstract class BaseHapiFhirDao implements IDao {
setUpdatedTime(uriParams, theUpdateTime);
setUpdatedTime(coordsParams, theUpdateTime);
setUpdatedTime(tokenParams, theUpdateTime);
-
+
/*
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
* matching
@@ -1531,11 +1534,14 @@ public abstract class BaseHapiFhirDao implements IDao {
for (IBase nextChild : values) {
IBaseReference nextRef = (IBaseReference) nextChild;
- if (!isBlank(nextRef.getReferenceElement().getResourceType())) {
- if (!nextRef.getReferenceElement().getValue().contains("?")) {
- if (!validTypes.contains(nextRef.getReferenceElement().getResourceType())) {
- throw new UnprocessableEntityException(
- "Invalid reference found at path '" + newPath + "'. Resource type '" + nextRef.getReferenceElement().getResourceType() + "' is not valid for this path");
+ IIdType referencedId = nextRef.getReferenceElement();
+ if (!isBlank(referencedId.getResourceType())) {
+ if (!isLogicalReference(referencedId)) {
+ if (!referencedId.getValue().contains("?")) {
+ if (!validTypes.contains(referencedId.getResourceType())) {
+ throw new UnprocessableEntityException(
+ "Invalid reference found at path '" + newPath + "'. Resource type '" + referencedId.getResourceType() + "' is not valid for this path");
+ }
}
}
}
@@ -1575,9 +1581,9 @@ public abstract class BaseHapiFhirDao implements IDao {
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
*
* @param theResource
- * The resource that is about to be persisted
+ * The resource that is about to be persisted
* @param theEntityToSave
- * TODO
+ * TODO
*/
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
Object tag = null;
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
index 675cae7cb5c..930fdcae038 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java
@@ -56,7 +56,8 @@ public class DaoConfig {
// ***
// update setter javadoc if default changes
// ***
- private boolean myAllowInlineMatchUrlReferences = false;
+ private boolean myAllowInlineMatchUrlReferences = true;
+
private boolean myAllowMultipleDelete;
private boolean myDefaultSearchParamsCanBeOverridden = false;
// ***
@@ -93,6 +94,20 @@ public class DaoConfig {
private Set myTreatBaseUrlsAsLocal = new HashSet();
private Set myTreatReferencesAsLogical = new HashSet(DEFAULT_LOGICAL_BASE_URLS);
+ /**
+ * Add a value to the {@link #setTreatReferencesAsLogical(Set) logical references list}.
+ *
+ * @see #setTreatReferencesAsLogical(Set)
+ */
+ public void addTreatReferencesAsLogical(String theTreatReferencesAsLogical) {
+ validateTreatBaseUrlsAsLocal(theTreatReferencesAsLogical);
+
+ if (myTreatReferencesAsLogical == null) {
+ myTreatReferencesAsLogical = new HashSet();
+ }
+ myTreatReferencesAsLogical.add(theTreatReferencesAsLogical);
+ }
+
/**
* When a code system is added that contains more than this number of codes,
* the code system will be indexed later in an incremental process in order to
@@ -115,9 +130,9 @@ public class DaoConfig {
* (next/prev links in search response bundles) will become invalid. Defaults to 1 hour.
*
*
- *
- * @see To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
- *
+ *
+ * To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
+ *
*
* @since 1.5
*/
@@ -196,9 +211,9 @@ public class DaoConfig {
* references instead of being treated as real references.
*
* A logical reference is a reference which is treated as an identifier, and
- * does not neccesarily resolve. See {@link http://hl7.org/fhir/references.html} for
+ * does not neccesarily resolve. See {@link "http://hl7.org/fhir/references.html"} for
* a description of logical references. For example, the valueset
- * {@link http://hl7.org/fhir/valueset-quantity-comparator.html} is a logical
+ * {@link "http://hl7.org/fhir/valueset-quantity-comparator.html"} is a logical
* reference.
*
*
@@ -209,7 +224,7 @@ public class DaoConfig {
*
http://example.com/some-base*(will match anything beginning with the part before the *)
*
*
- * @see #DEFAULT_LOGICAL_BASE_URLS for a list of default values for this setting
+ * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property
*/
public Set getTreatReferencesAsLogical() {
return myTreatReferencesAsLogical;
@@ -337,7 +352,9 @@ public class DaoConfig {
* to "Patient?identifier=12345", this is reference match URL will be resolved and replaced according
* to the usual match URL rules.
*
- * Default is false for now, as this is an experimental feature.
+ * Default is {@literal true} beginning in HAPI FHIR 2.4, since this
+ * feature is now specified in the FHIR specification. (Previously it
+ * was an experimental/rpposed feature)
*
*
* @since 1.5
@@ -401,8 +418,10 @@ public class DaoConfig {
*
*
*
- * @see To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
- *
+ *
+ * To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
+ *
+ *
* @since 1.5
*/
public void setExpireSearchResultsAfterMillis(long theExpireSearchResultsAfterMillis) {
@@ -418,7 +437,7 @@ public class DaoConfig {
* paging provider instead. Deprecated in HAPI FHIR 2.3 (Jan 2017)
*/
@Deprecated
- public void setHardSearchLimit(@SuppressWarnings("unused") int theHardSearchLimit) {
+ public void setHardSearchLimit(int theHardSearchLimit) {
// this method does nothing
}
@@ -528,6 +547,12 @@ public class DaoConfig {
* means no references will be treated as external
*/
public void setTreatBaseUrlsAsLocal(Set theTreatBaseUrlsAsLocal) {
+ if (theTreatBaseUrlsAsLocal != null) {
+ for (String next : theTreatBaseUrlsAsLocal) {
+ validateTreatBaseUrlsAsLocal(next);
+ }
+ }
+
HashSet treatBaseUrlsAsLocal = new HashSet();
for (String next : ObjectUtils.defaultIfNull(theTreatBaseUrlsAsLocal, new HashSet())) {
while (next.endsWith("/")) {
@@ -544,9 +569,9 @@ public class DaoConfig {
* references instead of being treated as real references.
*
* A logical reference is a reference which is treated as an identifier, and
- * does not neccesarily resolve. See {@link http://hl7.org/fhir/references.html} for
+ * does not neccesarily resolve. See {@link "http://hl7.org/fhir/references.html"} for
* a description of logical references. For example, the valueset
- * {@link http://hl7.org/fhir/valueset-quantity-comparator.html} is a logical
+ * {@link "http://hl7.org/fhir/valueset-quantity-comparator.html"} is a logical
* reference.
*
*
@@ -557,11 +582,23 @@ public class DaoConfig {
*
http://example.com/some-base*(will match anything beginning with the part before the *)
*
*
- * @see #DEFAULT_LOGICAL_BASE_URLS for a list of default values for this setting
+ * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property
*/
public DaoConfig setTreatReferencesAsLogical(Set theTreatReferencesAsLogical) {
myTreatReferencesAsLogical = theTreatReferencesAsLogical;
return this;
}
+ private static void validateTreatBaseUrlsAsLocal(String theUrl) {
+ Validate.notBlank(theUrl, "Base URL must not be null or empty");
+
+ int starIdx = theUrl.indexOf('*');
+ if (starIdx != -1) {
+ if (starIdx != theUrl.length() - 1) {
+ throw new IllegalArgumentException("Base URL wildcard character (*) can only appear at the end of the string: " + theUrl);
+ }
+ }
+
+ }
+
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java
index 62ae1312e66..7df0c6be92a 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java
@@ -1,43 +1,5 @@
package ca.uhn.fhir.jpa.dao;
-import static org.apache.commons.lang3.StringUtils.isNotBlank;
-
-import java.util.ArrayList;
-
-/*
- * #%L
- * HAPI FHIR JPA Server
- * %%
- * Copyright (C) 2014 - 2017 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.Collections;
-import java.util.List;
-
-import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport;
-import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator;
-import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
-import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain;
-import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.instance.model.api.IIdType;
-import org.hl7.fhir.instance.validation.IResourceValidator.BestPracticeWarningLevel;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceTable;
@@ -64,6 +26,38 @@ import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.IValidationContext;
import ca.uhn.fhir.validation.IValidatorModule;
import ca.uhn.fhir.validation.ValidationResult;
+import org.hl7.fhir.instance.hapi.validation.IValidationSupport;
+import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
+
+/*
+ * #%L
+ * HAPI FHIR JPA Server
+ * %%
+ * Copyright (C) 2014 - 2017 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
public class FhirResourceDaoDstu2 extends BaseHapiFhirResourceDao {
@@ -71,6 +65,9 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou
@Qualifier("myJpaValidationSupportDstu2")
private IValidationSupport myJpaValidationSupport;
+ @Autowired()
+ @Qualifier("myInstanceValidatorDstu2")
+ private IValidatorModule myInstanceValidator;
@Override
protected List