001/* 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.dao; 021 022import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoSearchParameter; 023import ca.uhn.fhir.jpa.dao.validation.SearchParameterDaoValidator; 024import ca.uhn.fhir.jpa.model.entity.ResourceTable; 025import ca.uhn.fhir.rest.api.server.RequestDetails; 026import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; 027import com.google.common.annotations.VisibleForTesting; 028import org.hl7.fhir.instance.model.api.IBaseResource; 029import org.hl7.fhir.r5.model.Enumeration; 030import org.springframework.beans.factory.annotation.Autowired; 031import org.springframework.transaction.support.TransactionSynchronization; 032import org.springframework.transaction.support.TransactionSynchronizationManager; 033 034import java.util.List; 035import java.util.concurrent.atomic.AtomicBoolean; 036import java.util.stream.Collectors; 037 038public class JpaResourceDaoSearchParameter<T extends IBaseResource> extends BaseHapiFhirResourceDao<T> 039 implements IFhirResourceDaoSearchParameter<T> { 040 041 private final AtomicBoolean myCacheReloadTriggered = new AtomicBoolean(false); 042 043 @Autowired 044 private VersionCanonicalizer myVersionCanonicalizer; 045 046 @Autowired 047 private SearchParameterDaoValidator mySearchParameterDaoValidator; 048 049 protected void reindexAffectedResources(T theResource, RequestDetails theRequestDetails) { 050 051 /* 052 * After we commit, flush the search parameter cache. This only helps on the 053 * local server (ie in a cluster the other servers won't be flushed) but 054 * the cache is short anyhow, and flushing locally is better than nothing. 055 * Many use cases where you would create a search parameter and immediately 056 * try to use it tend to be on single-server setups anyhow, e.g. unit tests 057 */ 058 if (!shouldSkipReindex(theRequestDetails)) { 059 if (!myCacheReloadTriggered.getAndSet(true)) { 060 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { 061 @Override 062 public void afterCommit() { 063 myCacheReloadTriggered.set(false); 064 mySearchParamRegistry.forceRefresh(); 065 } 066 }); 067 } 068 } 069 070 // N.B. Don't do this on the canonicalized version 071 Boolean reindex = theResource != null ? CURRENTLY_REINDEXING.get(theResource) : null; 072 073 org.hl7.fhir.r5.model.SearchParameter searchParameter = 074 myVersionCanonicalizer.searchParameterToCanonical(theResource); 075 List<String> base = theResource != null 076 ? searchParameter.getBase().stream().map(Enumeration::getCode).collect(Collectors.toList()) 077 : null; 078 requestReindexForRelatedResources(reindex, base, theRequestDetails); 079 } 080 081 @Override 082 protected void postPersist(ResourceTable theEntity, T theResource, RequestDetails theRequestDetails) { 083 super.postPersist(theEntity, theResource, theRequestDetails); 084 reindexAffectedResources(theResource, theRequestDetails); 085 } 086 087 @Override 088 protected void postUpdate(ResourceTable theEntity, T theResource, RequestDetails theRequestDetails) { 089 super.postUpdate(theEntity, theResource, theRequestDetails); 090 reindexAffectedResources(theResource, theRequestDetails); 091 } 092 093 @Override 094 protected void preDelete(T theResourceToDelete, ResourceTable theEntityToDelete, RequestDetails theRequestDetails) { 095 super.preDelete(theResourceToDelete, theEntityToDelete, theRequestDetails); 096 reindexAffectedResources(theResourceToDelete, theRequestDetails); 097 } 098 099 @Override 100 protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) { 101 super.validateResourceForStorage(theResource, theEntityToSave); 102 103 validateSearchParam(theResource); 104 } 105 106 public void validateSearchParam(IBaseResource theResource) { 107 org.hl7.fhir.r5.model.SearchParameter searchParameter = 108 myVersionCanonicalizer.searchParameterToCanonical(theResource); 109 mySearchParameterDaoValidator.validate(searchParameter); 110 } 111 112 @VisibleForTesting 113 void setVersionCanonicalizerForUnitTest(VersionCanonicalizer theVersionCanonicalizer) { 114 myVersionCanonicalizer = theVersionCanonicalizer; 115 } 116 117 @VisibleForTesting 118 public void setSearchParameterDaoValidatorForUnitTest(SearchParameterDaoValidator theSearchParameterDaoValidator) { 119 mySearchParameterDaoValidator = theSearchParameterDaoValidator; 120 } 121}