diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java index 29ac5e4dd7b..5d03edf6fe2 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.model.api; * 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. @@ -19,18 +19,6 @@ package ca.uhn.fhir.model.api; * limitations under the License. * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IPrimitiveType; import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.primitive.DecimalDt; @@ -39,6 +27,14 @@ import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.apache.commons.lang3.StringUtils; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; + +import java.io.Serializable; +import java.util.*; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; /** * Keys in this map refer to resource metadata keys, which are keys used to access information about specific resource instances that live outside of the resource body. Typically, these are @@ -64,8 +60,6 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; */ public abstract class ResourceMetadataKeyEnum implements Serializable { - private static final long serialVersionUID = 1L; - /** * If present and populated with a date/time (as an instance of {@link InstantDt}), this value is an indication that the resource is in the deleted state. This key is only used in a limited number * of scenarios, such as POSTing transaction bundles to a server, or returning resource history. @@ -81,23 +75,22 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT); } - @Override - public void put(IResource theResource, InstantDt theObject) { - theResource.getResourceMetadata().put(DELETED_AT, theObject); - } - @SuppressWarnings("unchecked") @Override public IPrimitiveType get(IAnyResource theResource) { return (IPrimitiveType) theResource.getUserData(DELETED_AT.name()); } + @Override + public void put(IResource theResource, InstantDt theObject) { + theResource.getResourceMetadata().put(DELETED_AT, theObject); + } + @Override public void put(IAnyResource theResource, IPrimitiveType 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 * Bundle.entry.score in a Bundle resource. @@ -121,7 +114,6 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { 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. * The value for this key corresponds to field Bundle.entry.search.mode. This value can be set to provide a status value of "include" for included resources being returned by a @@ -135,27 +127,27 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { */ public static final ResourceMetadataKeySupportingAnyResource ENTRY_SEARCH_MODE = new ResourceMetadataKeySupportingAnyResource("ENTRY_SEARCH_MODE") { private static final long serialVersionUID = 1L; + @Override public BundleEntrySearchModeEnum get(IResource theResource) { return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_SEARCH_MODE, BundleEntrySearchModeEnum.class, BundleEntrySearchModeEnum.VALUESET_BINDER); } - @Override - public void put(IResource theResource, BundleEntrySearchModeEnum theObject) { - theResource.getResourceMetadata().put(ENTRY_SEARCH_MODE, theObject); - } - @Override public String get(IAnyResource theResource) { return (String) theResource.getUserData(ENTRY_SEARCH_MODE.name()); } + @Override + public void put(IResource theResource, BundleEntrySearchModeEnum theObject) { + theResource.getResourceMetadata().put(ENTRY_SEARCH_MODE, theObject); + } + @Override public void put(IAnyResource theResource, String theObject) { theResource.setUserData(ENTRY_SEARCH_MODE.name(), theObject); } }; - /** * If present and populated with a {@link BundleEntryTransactionMethodEnum}, contains the "bundle entry transaction operation", which is the value of the status field in the Bundle entry * containing this resource. The value for this key corresponds to field Bundle.entry.transaction.operation. This value can be set in resources being transmitted to a server to @@ -169,18 +161,13 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { *

*/ public static final ResourceMetadataKeySupportingAnyResource ENTRY_TRANSACTION_METHOD = new ResourceMetadataKeySupportingAnyResource( - "ENTRY_TRANSACTION_OPERATION") { - private static final long serialVersionUID = 1L; + "ENTRY_TRANSACTION_OPERATION") { + private static final long serialVersionUID = 1L; @Override public BundleEntryTransactionMethodEnum get(IResource theResource) { return getEnumFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ENTRY_TRANSACTION_METHOD, BundleEntryTransactionMethodEnum.class, - BundleEntryTransactionMethodEnum.VALUESET_BINDER); - } - - @Override - public void put(IResource theResource, BundleEntryTransactionMethodEnum theObject) { - theResource.getResourceMetadata().put(ENTRY_TRANSACTION_METHOD, theObject); + BundleEntryTransactionMethodEnum.VALUESET_BINDER); } @Override @@ -188,13 +175,17 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { return (String) theResource.getUserData(ENTRY_TRANSACTION_METHOD.name()); } + @Override + public void put(IResource theResource, BundleEntryTransactionMethodEnum theObject) { + theResource.getResourceMetadata().put(ENTRY_TRANSACTION_METHOD, theObject); + } + @Override public void put(IAnyResource theResource, String theObject) { theResource.setUserData(ENTRY_TRANSACTION_METHOD.name(), theObject); } - - }; + }; /** * If present and populated with a string, provides the "alternate link" (the link element in the bundle entry with rel="alternate"). 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 @@ -205,6 +196,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { */ public static final ResourceMetadataKeyEnum LINK_ALTERNATE = new ResourceMetadataKeyEnum("LINK_ALTERNATE") { private static final long serialVersionUID = 1L; + @Override public String get(IResource theResource) { return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_ALTERNATE); @@ -215,7 +207,6 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { 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 rel="search"). 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 @@ -226,6 +217,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { */ public static final ResourceMetadataKeyEnum LINK_SEARCH = new ResourceMetadataKeyEnum("LINK_SEARCH") { private static final long serialVersionUID = 1L; + @Override public String get(IResource theResource) { return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), LINK_SEARCH); @@ -236,7 +228,6 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { 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. *

@@ -245,6 +236,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { */ public static final ResourceMetadataKeyEnum PREVIOUS_ID = new ResourceMetadataKeyEnum("PREVIOUS_ID") { private static final long serialVersionUID = 1L; + @Override public IdDt get(IResource theResource) { return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID); @@ -255,16 +247,16 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { 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. - * + *

*

* Values for this key are of type List<IdDt>. Note that the returned list is unmodifiable, so you need to create a new list and call put to change its value. *

*/ public static final ResourceMetadataKeyEnum> PROFILES = new ResourceMetadataKeyEnum>("PROFILES") { private static final long serialVersionUID = 1L; + @Override public List get(IResource theResource) { return getIdListFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PROFILES); @@ -275,7 +267,6 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { theResource.getResourceMetadata().put(PROFILES, theObject); } }; - /** * The value for this key is the bundle entry Published time. This is defined by FHIR as "Time resource copied into the feed", which is generally best left to the current time. *

@@ -284,11 +275,12 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { *

* Server Note: In servers, it is generally advisable to leave this value null, in which case the server will substitute the current time automatically. *

- * + * * @see InstantDt */ public static final ResourceMetadataKeyEnum PUBLISHED = new ResourceMetadataKeyEnum("PUBLISHED") { private static final long serialVersionUID = 1L; + @Override public InstantDt get(IResource theResource) { return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED); @@ -299,9 +291,9 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { theResource.getResourceMetadata().put(PUBLISHED, theObject); } }; - public static final ResourceMetadataKeyEnum> SECURITY_LABELS = new ResourceMetadataKeyEnum>("SECURITY_LABELS") { private static final long serialVersionUID = 1L; + @Override public List get(IResource resource) { Object obj = resource.getResourceMetadata().get(SECURITY_LABELS); @@ -317,7 +309,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { 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()); + + BaseCodingDt.class.getCanonicalName()); } } @@ -328,17 +320,17 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } }; - /** * The value for this key is the list of tags associated with this resource *

* Values for this key are of type {@link TagList} *

- * + * * @see TagList */ public static final ResourceMetadataKeyEnum TAG_LIST = new ResourceMetadataKeyEnum("TAG_LIST") { private static final long serialVersionUID = 1L; + @Override public TagList get(IResource theResource) { Object retValObj = theResource.getResourceMetadata().get(TAG_LIST); @@ -351,7 +343,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { 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()); + + TagList.class.getCanonicalName()); } @Override @@ -359,7 +351,6 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { 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. *

@@ -368,6 +359,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { */ public static final ResourceMetadataKeyEnum TITLE = new ResourceMetadataKeyEnum("TITLE") { private static final long serialVersionUID = 1L; + @Override public String get(IResource theResource) { return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), TITLE); @@ -378,18 +370,18 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { theResource.getResourceMetadata().put(TITLE, theObject); } }; - /** * The value for this key is the bundle entry Updated 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.) *

* Values for this key are of type {@link InstantDt} *

- * + * * @see InstantDt */ public static final ResourceMetadataKeyEnum UPDATED = new ResourceMetadataKeyEnum("UPDATED") { private static final long serialVersionUID = 1L; + @Override public InstantDt get(IResource theResource) { return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED); @@ -400,7 +392,6 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { theResource.getResourceMetadata().put(UPDATED, theObject); } }; - /** * The value for this key is the version ID of the resource object. *

@@ -409,6 +400,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { */ public static final ResourceMetadataKeyEnum VERSION = new ResourceMetadataKeyEnum("VERSION") { private static final long serialVersionUID = 1L; + @Override public String get(IResource theResource) { return getStringFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION); @@ -419,18 +411,18 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { theResource.getResourceMetadata().put(VERSION, theObject); } }; - /** * The value for this key is the version ID of the resource object. *

* Values for this key are of type {@link IdDt} *

- * + * * @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 VERSION_ID = new ResourceMetadataKeyEnum("VERSION_ID") { private static final long serialVersionUID = 1L; + @Override public IdDt get(IResource theResource) { return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID); @@ -441,7 +433,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { theResource.getResourceMetadata().put(VERSION_ID, theObject); } }; - + private static final long serialVersionUID = 1L; private final String myValue; public ResourceMetadataKeyEnum(String theValue) { @@ -504,12 +496,12 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { return new DecimalDt((Double) retValObj); } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " - + InstantDt.class.getCanonicalName()); + + InstantDt.class.getCanonicalName()); } @SuppressWarnings("unchecked") private static > T getEnumFromMetadataOrNullIfNone(Map, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey, Class theEnumType, - IValueSetEnumBinder theBinder) { + IValueSetEnumBinder theBinder) { Object retValObj = theResourceMetadata.get(theKey); if (retValObj == null) { return null; @@ -519,7 +511,7 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { return theBinder.fromCodeString((String) retValObj); } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " - + InstantDt.class.getCanonicalName()); + + InstantDt.class.getCanonicalName()); } private static IdDt getIdFromMetadataOrNullIfNone(Map, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) { @@ -558,11 +550,11 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } else if (retValObj instanceof InstantDt) { if (((InstantDt) retValObj).isEmpty()) { return null; - } + } return (InstantDt) retValObj; } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " - + InstantDt.class.getCanonicalName()); + + InstantDt.class.getCanonicalName()); } private static String getStringFromMetadataOrNullIfNone(Map, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) { @@ -572,11 +564,11 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { } else if (retValObj instanceof String) { if (StringUtils.isBlank(((String) retValObj))) { return null; - } + } return (String) retValObj; } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " - + String.class.getCanonicalName()); + + String.class.getCanonicalName()); } private static IdDt toId(ResourceMetadataKeyEnum theKey, Object retValObj) { @@ -596,22 +588,21 @@ public abstract class ResourceMetadataKeyEnum implements Serializable { return new IdDt(((Number) retValObj).toString()); } throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " - + IdDt.class.getCanonicalName()); + + IdDt.class.getCanonicalName()); } public static abstract class ResourceMetadataKeySupportingAnyResource extends ResourceMetadataKeyEnum { + private static final long serialVersionUID = 1L; + public ResourceMetadataKeySupportingAnyResource(String theValue) { super(theValue); } - private static final long serialVersionUID = 1L; - - public abstract T2 get(IAnyResource theResource); public abstract void put(IAnyResource theResource, T2 theObject); } - + } diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseResource.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseResource.java index 1f40435cb23..5aad20f90d4 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseResource.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IBaseResource.java @@ -44,12 +44,12 @@ public interface IBaseResource extends IBase, IElement { /** * Include constant for * (return all includes) */ - public static final Include INCLUDE_ALL = new Include("*", false).toLocked(); + Include INCLUDE_ALL = new Include("*", false).toLocked(); /** * Include set containing only {@link #INCLUDE_ALL} */ - public static final Set WILDCARD_ALL_SET = Collections.unmodifiableSet(new HashSet(Arrays.asList(INCLUDE_ALL))); + Set WILDCARD_ALL_SET = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(INCLUDE_ALL))); IIdType getIdElement(); 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 5fe0af073ff..9c26432e15c 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 @@ -127,7 +127,7 @@ public abstract class BaseHapiFhirResourceDao extends B } } - ourLog.info("Processed addTag {}/{} on {} in {}ms", new Object[]{theScheme, theTerm, theId, w.getMillisAndRestart()}); + ourLog.info("Processed addTag {}/{} on {} in {}ms", theScheme, theTerm, theId, w.getMillisAndRestart()); } @Override @@ -350,7 +350,7 @@ public abstract class BaseHapiFhirResourceDao extends B OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code); } - ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", new Object[]{theUrl, deletedResources.size(), w.getMillis()}); + ourLog.info("Processed delete on {} (matched {} resource(s)) in {}ms", theUrl, deletedResources.size(), w.getMillis()); DeleteMethodOutcome retVal = new DeleteMethodOutcome(); retVal.setDeletedEntities(deletedResources); @@ -613,7 +613,12 @@ public abstract class BaseHapiFhirResourceDao extends B return theRequestDetails.getServer().getPagingProvider() instanceof DatabaseBackedPagingProvider; } - protected void markResourcesMatchingExpressionAsNeedingReindexing(String theExpression) { + protected void markResourcesMatchingExpressionAsNeedingReindexing(Boolean theCurrentlyReindexing, String theExpression) { + // Avoid endless loops + if (Boolean.TRUE.equals(theCurrentlyReindexing)) { + return; + } + if (myDaoConfig.isMarkResourcesForReindexingUponSearchParameterChange()) { if (isNotBlank(theExpression)) { final String resourceType = theExpression.substring(0, theExpression.indexOf('.')); @@ -924,7 +929,9 @@ public abstract class BaseHapiFhirResourceDao extends B @Override public void reindex(T theResource, ResourceTable theEntity) { ourLog.debug("Indexing resource {} - PID {}", theResource.getIdElement().getValue(), theEntity.getId()); + CURRENTLY_REINDEXING.put(theResource, Boolean.TRUE); updateEntity(theResource, theEntity, null, true, false, theEntity.getUpdatedDate(), true, false); + CURRENTLY_REINDEXING.put(theResource, null); } @Override @@ -961,7 +968,7 @@ public abstract class BaseHapiFhirResourceDao extends B myEntityManager.merge(entity); - ourLog.info("Processed remove tag {}/{} on {} in {}ms", new Object[]{theScheme, theTerm, theId.getValue(), w.getMillisAndRestart()}); + ourLog.info("Processed remove tag {}/{} on {} in {}ms", theScheme, theTerm, theId.getValue(), w.getMillisAndRestart()); } @Transactional(propagation = Propagation.SUPPORTS) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java index b8e153ef90a..7b6e38baeea 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoSearchParameterDstu2.java @@ -45,7 +45,9 @@ public class FhirResourceDaoSearchParameterDstu2 extends FhirResourceDaoDstu2 mySystemDao; protected void markAffectedResources(SearchParameter theResource) { - markResourcesMatchingExpressionAsNeedingReindexing(theResource != null ? theResource.getXpath() : null); + Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null; + String expression = theResource != null ? theResource.getXpath() : null; + markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression); } /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java index e4dd8767534..90f8e6e4a15 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IDao.java @@ -1,17 +1,15 @@ package ca.uhn.fhir.jpa.dao; -import java.util.Collection; -import java.util.Set; - -import org.hl7.fhir.instance.model.api.IBaseResource; - import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.jpa.entity.BaseHasResource; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.search.PersistedJpaBundleProvider; -import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; + +import java.util.Collection; +import java.util.Set; /* * #%L @@ -35,7 +33,9 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupporti public interface IDao { - ResourceMetadataKeySupportingAnyResource RESOURCE_PID = new MetadataKeyResourcePid("RESOURCE_PID"); + MetadataKeyResourcePid RESOURCE_PID = new MetadataKeyResourcePid("RESOURCE_PID"); + + MetadataKeyCurrentlyReindexing CURRENTLY_REINDEXING = new MetadataKeyCurrentlyReindexing("CURRENTLY_REINDEXING"); FhirContext getContext(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyCurrentlyReindexing.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyCurrentlyReindexing.java new file mode 100644 index 00000000000..c78eaa18041 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/MetadataKeyCurrentlyReindexing.java @@ -0,0 +1,70 @@ +package ca.uhn.fhir.jpa.dao; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum.ResourceMetadataKeySupportingAnyResource; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; + +public final class MetadataKeyCurrentlyReindexing extends ResourceMetadataKeySupportingAnyResource { + private static final long serialVersionUID = 1L; + + MetadataKeyCurrentlyReindexing(String theValue) { + super(theValue); + } + + @Override + public Boolean get(IAnyResource theResource) { + return (Boolean) theResource.getUserData(IDao.CURRENTLY_REINDEXING.name()); + } + + @Override + public Boolean get(IResource theResource) { + return (Boolean) theResource.getResourceMetadata().get(IDao.CURRENTLY_REINDEXING); + } + + public Boolean get(IBaseResource theResource) { + if (theResource instanceof IAnyResource) { + return get((IAnyResource) theResource); + } else { + return get((IResource) theResource); + } + } + + @Override + public void put(IAnyResource theResource, Boolean theObject) { + theResource.setUserData(IDao.CURRENTLY_REINDEXING.name(), theObject); + } + + public void put(IBaseResource theResource, Boolean theValue) { + if (theResource instanceof IAnyResource) { + put((IAnyResource) theResource, theValue); + } else { + put((IResource) theResource, theValue); + } + } + + @Override + public void put(IResource theResource, Boolean theObject) { + theResource.getResourceMetadata().put(IDao.CURRENTLY_REINDEXING, theObject); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java index 975dc118d4a..dd41bc48524 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoSearchParameterDstu3.java @@ -50,7 +50,9 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3 mySystemDao; protected void markAffectedResources(SearchParameter theResource) { - markResourcesMatchingExpressionAsNeedingReindexing(theResource != null ? theResource.getExpression() : null); + Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null; + String expression = theResource != null ? theResource.getExpression() : null; + markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression); } /** diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java index 8c7c0d96ab4..2baba40c03f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoSearchParameterR4.java @@ -47,7 +47,9 @@ public class FhirResourceDaoSearchParameterR4 extends FhirResourceDaoR4 mySystemDao; protected void markAffectedResources(SearchParameter theResource) { - markResourcesMatchingExpressionAsNeedingReindexing(theResource != null ? theResource.getExpression() : null); + Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null; + String expression = theResource != null ? theResource.getExpression() : null; + markResourcesMatchingExpressionAsNeedingReindexing(reindex, expression); } /** diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java index 561076f8493..5402caf8a7f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoR4SearchCustomSearchParamTest.java @@ -144,6 +144,24 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test } + @Test + public void testCreateSearchParameterOnSearchParameterDoesntCauseEndlessReindexLoop() throws InterruptedException { + SearchParameter fooSp = new SearchParameter(); + fooSp.setCode("foo"); + fooSp.addBase("SearchParameter"); + fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.TOKEN); + fooSp.setTitle("FOO SP"); + fooSp.setExpression("SearchParameter.code"); + fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL); + fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE); + + mySearchParameterDao.create(fooSp, mySrd); + + assertEquals(1, mySystemDao.performReindexingPass(100).intValue()); + assertEquals(0, mySystemDao.performReindexingPass(100).intValue()); + + } + @Test public void testCustomReferenceParameter() throws Exception { SearchParameter sp = new SearchParameter(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index c3e9fea322b..797bac874a6 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -140,6 +140,10 @@ parameters and updating a resource to no longer match one of these search parameters. + + Avoid an endless loop of reindexing in JPA if a SearchParameter is + created which indexed the SearchParameter resource itself +