Cache service loaders (#4196)
* Extracting a Cache interface and building service loaders for Caffeine and Guava * Fix estimated size interface * Use LoadingCache when needed. * Removing Caffeine from dependency lists. * Adding hapi-fhir-cache-caffeine as a test dependency * Putting caching solutions in a single module. * Fixing the spacing * Standardizing the use of TimeUnits * Making a new module to simplify the switch of the cache library in tests. * Making sure the Guava design matches the behavior of Caffeine. * Making sure the Cache structure also does not throw InvalidCacheLoading exception to match the LoadingCache. * Renaming module names for the caching group. * Better error handing that informs devs what to do. * Improving documentation * Typo * Matching error message design with Caffeine. * Matching the behavior of Caffeine with Guava * Final adjustments for the test dependencies on the cache modules. * Fixing relative pom path. * Adding caffeine as a testing requirement for the new modules. * Add changelog and set JPA server to use caffeine cache * POM fixes * Build fix * Buid fix * Fixes * Address review comment * One more cache * Move changelog to next release * Update pom versions * Build fix * Build fixes * Build fix * Try to get build working * Experiment with failing build * Rever change * Fix POM version * Build fix * Build fix * Add Msg.code to new exceptions Co-authored-by: Vitor Pamplona <vitor@vitorpamplona.com>
This commit is contained in:
parent
f50d350f1f
commit
9a45576793
|
@ -80,6 +80,16 @@ stages:
|
|||
module: hapi-fhir-server-mdm
|
||||
- name: hapi_fhir_server_openapi
|
||||
module: hapi-fhir-server-openapi
|
||||
- name: hapi_fhir_serviceloaders
|
||||
module: hapi-fhir-serviceloaders
|
||||
- name: hapi_fhir_caching_api
|
||||
module: hapi-fhir-serviceloaders/hapi-fhir-caching-api
|
||||
- name: hapi_fhir_caching_caffeine
|
||||
module: hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine
|
||||
- name: hapi_fhir_caching_guava
|
||||
module: hapi-fhir-serviceloaders/hapi-fhir-caching-guava
|
||||
- name: hapi_fhir_caching_testing
|
||||
module: hapi-fhir-serviceloaders/hapi-fhir-caching-testing
|
||||
- name: hapi_fhir_spring_boot_autoconfigure
|
||||
module: hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure
|
||||
- name: hapi_fhir_spring_boot_sample_server_jersey
|
||||
|
|
|
@ -38,12 +38,19 @@
|
|||
<artifactId>hapi-fhir-sql-migrate</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
type: add
|
||||
issue: 4196
|
||||
title: "In-memory caching provided by Caffeine has been refactored into using
|
||||
s ServiceLoader pattern in order to improve Android compatibility. Thanks to
|
||||
Vitor Pamplona for the pull request!"
|
|
@ -366,8 +366,15 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-caffeine</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -35,9 +35,9 @@ import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
|
@ -91,7 +91,7 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
|
|||
// TermReadSvcImpl calls these methods as a part of its "isCodeSystemSupported" calls.
|
||||
// We should modify CachingValidationSupport to cache the results of "isXXXSupported"
|
||||
// at which point we could do away with this cache
|
||||
private Cache<String, IBaseResource> myLoadCache = Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||
private Cache<String, IBaseResource> myLoadCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(1), 1000);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -123,7 +123,9 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
|
|||
* version is always pointed by the ForcedId for the no-versioned CS
|
||||
*/
|
||||
private Optional<IBaseResource> getCodeSystemCurrentVersion(UriType theUrl) {
|
||||
if (!theUrl.getValueAsString().contains(LOINC_LOW)) return Optional.empty();
|
||||
if (!theUrl.getValueAsString().contains(LOINC_LOW)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return myTermReadSvc.readCodeSystemByForcedId(LOINC_LOW);
|
||||
}
|
||||
|
@ -145,7 +147,9 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
|
|||
*/
|
||||
private Optional<IBaseResource> getValueSetCurrentVersion(UriType theUrl) {
|
||||
Optional<String> vsIdOpt = TermReadSvcUtil.getValueSetId(theUrl.getValueAsString());
|
||||
if (!vsIdOpt.isPresent()) return Optional.empty();
|
||||
if (!vsIdOpt.isPresent()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
IFhirResourceDao<? extends IBaseResource> valueSetResourceDao = myDaoRegistry.getResourceDao(myValueSetType);
|
||||
IBaseResource valueSet = valueSetResourceDao.read(new IdDt("ValueSet", vsIdOpt.get()));
|
||||
|
@ -188,8 +192,17 @@ public class JpaPersistedResourceValidationSupport implements IValidationSupport
|
|||
|
||||
private <T extends IBaseResource> IBaseResource doFetchResource(@Nullable Class<T> theClass, String theUri) {
|
||||
if (theClass == null) {
|
||||
Supplier<IBaseResource>[] 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);
|
||||
Supplier<IBaseResource>[] 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);
|
||||
|
|
|
@ -27,8 +27,8 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
|
|||
import ca.uhn.fhir.interceptor.model.TransactionWriteOperationsDetails;
|
||||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -61,10 +61,7 @@ public class TransactionConcurrencySemaphoreInterceptor {
|
|||
*/
|
||||
public TransactionConcurrencySemaphoreInterceptor(MemoryCacheService theMemoryCacheService) {
|
||||
myMemoryCacheService = theMemoryCacheService;
|
||||
mySemaphoreCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterAccess(1, TimeUnit.MINUTES)
|
||||
.build();
|
||||
mySemaphoreCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(1));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,12 +29,12 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import ca.uhn.fhir.sl.cache.CacheLoader;
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@ -78,14 +78,8 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
|
|||
@Override
|
||||
@PostConstruct
|
||||
public void start() {
|
||||
myNameToPartitionCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build(new NameToPartitionCacheLoader());
|
||||
myIdToPartitionCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build(new IdToPartitionCacheLoader());
|
||||
myNameToPartitionCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(1), new NameToPartitionCacheLoader());
|
||||
myIdToPartitionCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(1), new IdToPartitionCacheLoader());
|
||||
myTxTemplate = new TransactionTemplate(myTxManager);
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,8 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.FhirVersionIndependentConcept;
|
||||
import ca.uhn.fhir.util.HapiExtensions;
|
||||
|
@ -85,8 +87,6 @@ import ca.uhn.fhir.util.StopWatch;
|
|||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import ca.uhn.hapi.converters.canonical.VersionCanonicalizer;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
|
@ -193,25 +193,22 @@ import static org.apache.commons.lang3.StringUtils.startsWithIgnoreCase;
|
|||
|
||||
public class TermReadSvcImpl implements ITermReadSvc {
|
||||
public static final int DEFAULT_FETCH_SIZE = 250;
|
||||
public static final int DEFAULT_MASS_INDEXER_OBJECT_LOADING_THREADS = 2;
|
||||
// doesn't seem to be much gain by using more threads than this value
|
||||
public static final int MAX_MASS_INDEXER_OBJECT_LOADING_THREADS = 6;
|
||||
private static final int SINGLE_FETCH_SIZE = 1;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermReadSvcImpl.class);
|
||||
private static final ValueSetExpansionOptions DEFAULT_EXPANSION_OPTIONS = new ValueSetExpansionOptions();
|
||||
private static final TermCodeSystemVersionDetails NO_CURRENT_VERSION = new TermCodeSystemVersionDetails(-1L, null);
|
||||
private static Runnable myInvokeOnNextCallForUnitTest;
|
||||
private static boolean ourForceDisableHibernateSearchForUnitTest;
|
||||
|
||||
private static final String IDX_PROPERTIES = "myProperties";
|
||||
private static final String IDX_PROP_KEY = IDX_PROPERTIES + ".myKey";
|
||||
private static final String IDX_PROP_VALUE_STRING = IDX_PROPERTIES + ".myValueString";
|
||||
private static final String IDX_PROP_DISPLAY_STRING = IDX_PROPERTIES + ".myDisplayString";
|
||||
|
||||
public static final int DEFAULT_MASS_INDEXER_OBJECT_LOADING_THREADS = 2;
|
||||
// doesn't seem to be much gain by using more threads than this value
|
||||
public static final int MAX_MASS_INDEXER_OBJECT_LOADING_THREADS = 6;
|
||||
|
||||
private boolean myPreExpandingValueSets = false;
|
||||
|
||||
private final Cache<String, TermCodeSystemVersionDetails> myCodeSystemCurrentVersionCache = Caffeine.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build();
|
||||
private static final int SECONDS_IN_MINUTE = 60;
|
||||
private static final int INDEXED_ROOTS_LOGGING_COUNT = 50_000;
|
||||
private static Runnable myInvokeOnNextCallForUnitTest;
|
||||
private static boolean ourForceDisableHibernateSearchForUnitTest;
|
||||
private final Cache<String, TermCodeSystemVersionDetails> myCodeSystemCurrentVersionCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(1));
|
||||
@Autowired
|
||||
protected DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
|
@ -232,6 +229,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
protected FhirContext myContext;
|
||||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
protected EntityManager myEntityManager;
|
||||
private boolean myPreExpandingValueSets = false;
|
||||
@Autowired
|
||||
private ITermCodeSystemVersionDao myCodeSystemVersionDao;
|
||||
@Autowired
|
||||
|
@ -255,19 +253,17 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
private ITermDeferredStorageSvc myDeferredStorageSvc;
|
||||
@Autowired
|
||||
private IIdHelperService myIdHelperService;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
|
||||
private volatile IValidationSupport myJpaValidationSupport;
|
||||
private volatile IValidationSupport myValidationSupport;
|
||||
|
||||
//We need this bean so we can tell which mode hibernate search is running in.
|
||||
@Autowired
|
||||
private HibernatePropertiesProvider myHibernatePropertiesProvider;
|
||||
@Autowired
|
||||
private CachingValidationSupport myCachingValidationSupport;
|
||||
|
||||
@Autowired
|
||||
private VersionCanonicalizer myVersionCanonicalizer;
|
||||
|
||||
@Override
|
||||
public boolean isCodeSystemSupported(ValidationSupportContext theValidationSupportContext, String theSystem) {
|
||||
|
@ -368,7 +364,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
myCodeSystemCurrentVersionCache.invalidateAll();
|
||||
}
|
||||
|
||||
|
||||
public void deleteValueSetForResource(ResourceTable theResourceTable) {
|
||||
// Get existing entity so it can be deleted.
|
||||
Optional<TermValueSet> optionalExistingTermValueSetById = myTermValueSetDao.findByResourcePid(theResourceTable.getId());
|
||||
|
@ -394,7 +389,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
deleteValueSetForResource(theResourceTable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public List<FhirVersionIndependentConcept> expandValueSetIntoConceptList(@Nullable ValueSetExpansionOptions theExpansionOptions, @Nonnull String theValueSetCanonicalUrl) {
|
||||
|
@ -447,7 +441,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
accumulator.addParameter().setName("count").setValue(new IntegerType(count));
|
||||
}
|
||||
|
||||
myTxTemplate.executeWithoutResult(tx-> {
|
||||
myTxTemplate.executeWithoutResult(tx -> {
|
||||
expandValueSetIntoAccumulator(theValueSetToExpand, theExpansionOptions, accumulator, theFilter, true);
|
||||
});
|
||||
|
||||
|
@ -590,7 +584,8 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
//-- this is quick solution, may need to revisit
|
||||
if (!applyFilter(display, filterDisplayValue)) {
|
||||
continue;}
|
||||
continue;
|
||||
}
|
||||
|
||||
Long conceptPid = conceptView.getConceptPid();
|
||||
if (!pidToConcept.containsKey(conceptPid)) {
|
||||
|
@ -602,7 +597,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
if (conceptView.getDesignationPid() != null) {
|
||||
TermConceptDesignation designation = new TermConceptDesignation();
|
||||
|
||||
if(isValueSetDisplayLanguageMatch(theExpansionOptions, conceptView.getDesignationLang() )) {
|
||||
if (isValueSetDisplayLanguageMatch(theExpansionOptions, conceptView.getDesignationLang())) {
|
||||
designation.setUseSystem(conceptView.getDesignationUseSystem());
|
||||
designation.setUseCode(conceptView.getDesignationUseCode());
|
||||
designation.setUseDisplay(conceptView.getDesignationUseDisplay());
|
||||
|
@ -660,19 +655,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
logConceptsExpanded("Finished expanding concepts. ", theTermValueSet, conceptsExpanded);
|
||||
}
|
||||
|
||||
static boolean isValueSetDisplayLanguageMatch(ValueSetExpansionOptions theExpansionOptions, String theStoredLang){
|
||||
if( theExpansionOptions == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if(theExpansionOptions.getTheDisplayLanguage() == null || theStoredLang == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return theExpansionOptions.getTheDisplayLanguage().equalsIgnoreCase(theStoredLang);
|
||||
|
||||
}
|
||||
|
||||
private void logConceptsExpanded(String theLogDescriptionPrefix, TermValueSet theTermValueSet, int theConceptsExpanded) {
|
||||
if (theConceptsExpanded > 0) {
|
||||
ourLog.debug("{}Have expanded {} concepts in ValueSet[{}]", theLogDescriptionPrefix, theConceptsExpanded, theTermValueSet.getUrl());
|
||||
|
@ -730,7 +712,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
* Note: Not transactional because specific calls within this method
|
||||
* get executed in a transaction
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void doExpandValueSet(ValueSetExpansionOptions theExpansionOptions, ValueSet theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator, @Nonnull ExpansionFilter theExpansionFilter) {
|
||||
Set<String> addedCodes = new HashSet<>();
|
||||
|
||||
|
@ -751,16 +732,16 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
ourLog.debug("Handling includes");
|
||||
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) {
|
||||
myTxTemplate.executeWithoutResult(tx ->
|
||||
expandValueSetHandleIncludeOrExclude(theExpansionOptions, theValueSetCodeAccumulator, addedCodes,
|
||||
include, true, theExpansionFilter) );
|
||||
expandValueSetHandleIncludeOrExclude(theExpansionOptions, theValueSetCodeAccumulator, addedCodes,
|
||||
include, true, theExpansionFilter));
|
||||
}
|
||||
|
||||
// Handle excludes
|
||||
ourLog.debug("Handling excludes");
|
||||
for (ValueSet.ConceptSetComponent exclude : theValueSetToExpand.getCompose().getExclude()) {
|
||||
myTxTemplate.executeWithoutResult(tx ->
|
||||
expandValueSetHandleIncludeOrExclude(theExpansionOptions, theValueSetCodeAccumulator, addedCodes,
|
||||
exclude, false, ExpansionFilter.NO_FILTER) );
|
||||
expandValueSetHandleIncludeOrExclude(theExpansionOptions, theValueSetCodeAccumulator, addedCodes,
|
||||
exclude, false, ExpansionFilter.NO_FILTER));
|
||||
}
|
||||
|
||||
if (theValueSetCodeAccumulator instanceof ValueSetConceptAccumulator) {
|
||||
|
@ -770,7 +751,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
ourLog.debug("Done working with {} in {}ms", valueSetInfo, sw.getMillis());
|
||||
}
|
||||
|
||||
|
||||
private String getValueSetInfo(ValueSet theValueSet) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean isIdentified = false;
|
||||
|
@ -799,11 +779,11 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
* Returns true if there are potentially more results to process.
|
||||
*/
|
||||
private void expandValueSetHandleIncludeOrExclude(@Nullable ValueSetExpansionOptions theExpansionOptions,
|
||||
IValueSetConceptAccumulator theValueSetCodeAccumulator,
|
||||
Set<String> theAddedCodes,
|
||||
ValueSet.ConceptSetComponent theIncludeOrExclude,
|
||||
boolean theAdd,
|
||||
@Nonnull ExpansionFilter theExpansionFilter) {
|
||||
IValueSetConceptAccumulator theValueSetCodeAccumulator,
|
||||
Set<String> theAddedCodes,
|
||||
ValueSet.ConceptSetComponent theIncludeOrExclude,
|
||||
boolean theAdd,
|
||||
@Nonnull ExpansionFilter theExpansionFilter) {
|
||||
|
||||
String system = theIncludeOrExclude.getSystem();
|
||||
boolean hasSystem = isNotBlank(system);
|
||||
|
@ -841,7 +821,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent includeOrExclude = ValueSet40_50.convertConceptSetComponent(theIncludeOrExclude);
|
||||
new InMemoryTerminologyServerValidationSupport(myContext).expandValueSetIncludeOrExclude(new ValidationSupportContext(provideValidationSupport()), consumer, includeOrExclude);
|
||||
} catch (InMemoryTerminologyServerValidationSupport.ExpansionCouldNotBeCompletedInternallyException e) {
|
||||
if (!theExpansionOptions.isFailOnMissingCodeSystem() && e.getFailureType() == InMemoryTerminologyServerValidationSupport.FailureType.UNKNOWN_CODE_SYSTEM) {
|
||||
if (theExpansionOptions != null && !theExpansionOptions.isFailOnMissingCodeSystem() && e.getFailureType() == InMemoryTerminologyServerValidationSupport.FailureType.UNKNOWN_CODE_SYSTEM) {
|
||||
return;
|
||||
}
|
||||
throw new InternalErrorException(Msg.code(888) + e);
|
||||
|
@ -881,14 +861,14 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
private void expandValueSetHandleIncludeOrExcludeUsingDatabase(
|
||||
ValueSetExpansionOptions theExpansionOptions,
|
||||
IValueSetConceptAccumulator theValueSetCodeAccumulator,
|
||||
Set<String> theAddedCodes,
|
||||
ValueSet.ConceptSetComponent theIncludeOrExclude,
|
||||
boolean theAdd,
|
||||
@Nonnull ExpansionFilter theExpansionFilter,
|
||||
String theSystem,
|
||||
TermCodeSystem theCs) {
|
||||
ValueSetExpansionOptions theExpansionOptions,
|
||||
IValueSetConceptAccumulator theValueSetCodeAccumulator,
|
||||
Set<String> theAddedCodes,
|
||||
ValueSet.ConceptSetComponent theIncludeOrExclude,
|
||||
boolean theAdd,
|
||||
@Nonnull ExpansionFilter theExpansionFilter,
|
||||
String theSystem,
|
||||
TermCodeSystem theCs) {
|
||||
|
||||
StopWatch fullOperationSw = new StopWatch();
|
||||
|
||||
|
@ -913,17 +893,19 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
int count = 0;
|
||||
|
||||
Optional<Integer> chunkSizeOpt = getScrollChunkSize(theAdd, theValueSetCodeAccumulator);
|
||||
if (chunkSizeOpt.isEmpty()) { return; }
|
||||
if (chunkSizeOpt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
int chunkSize = chunkSizeOpt.get();
|
||||
|
||||
SearchProperties searchProps = buildSearchScroll(termCodeSystemVersion, theExpansionFilter, theSystem,
|
||||
theIncludeOrExclude, chunkSize, includeOrExcludeVersion);
|
||||
|
||||
int accumulatedBatchesSoFar = 0;
|
||||
try ( SearchScroll<EntityReference> scroll = searchProps.getSearchScroll() ) {
|
||||
try (SearchScroll<EntityReference> scroll = searchProps.getSearchScroll()) {
|
||||
|
||||
ourLog.debug("Beginning batch expansion for {} with max results per batch: {}", (theAdd ? "inclusion" : "exclusion"), chunkSize);
|
||||
for ( SearchScrollResult<EntityReference> chunk = scroll.next(); chunk.hasHits(); chunk = scroll.next() ) {
|
||||
for (SearchScrollResult<EntityReference> chunk = scroll.next(); chunk.hasHits(); chunk = scroll.next()) {
|
||||
int countForBatch = 0;
|
||||
|
||||
List<Long> pids = chunk.hits()
|
||||
|
@ -970,7 +952,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private List<TermConcept> sortTermConcepts(SearchProperties searchProps, List<TermConcept> termConcepts) {
|
||||
List<String> codes = searchProps.getIncludeOrExcludeCodes();
|
||||
if (codes.size() > 1) {
|
||||
|
@ -988,7 +969,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return termConcepts;
|
||||
}
|
||||
|
||||
|
||||
private Optional<Integer> getScrollChunkSize(boolean theAdd, IValueSetConceptAccumulator theValueSetCodeAccumulator) {
|
||||
int maxResultsPerBatch = SearchBuilder.getMaximumPageSize();
|
||||
|
||||
|
@ -1002,18 +982,14 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
maxResultsPerBatch = Math.min(maxResultsPerBatch, accumulatorCapacityRemaining + 1);
|
||||
}
|
||||
}
|
||||
return maxResultsPerBatch > 0 ? Optional.of(maxResultsPerBatch): Optional.empty();
|
||||
return maxResultsPerBatch > 0 ? Optional.of(maxResultsPerBatch) : Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private SearchProperties buildSearchScroll(TermCodeSystemVersion theTermCodeSystemVersion,
|
||||
ExpansionFilter theExpansionFilter,
|
||||
String theSystem,
|
||||
ValueSet.ConceptSetComponent theIncludeOrExclude,
|
||||
Integer theScrollChunkSize, String theIncludeOrExcludeVersion) {
|
||||
ExpansionFilter theExpansionFilter,
|
||||
String theSystem,
|
||||
ValueSet.ConceptSetComponent theIncludeOrExclude,
|
||||
Integer theScrollChunkSize, String theIncludeOrExcludeVersion) {
|
||||
SearchSession searchSession = Search.session(myEntityManager);
|
||||
//Manually building a predicate since we need to throw it around.
|
||||
SearchPredicateFactory predicate = searchSession.scope(TermConcept.class).predicate();
|
||||
|
@ -1067,11 +1043,10 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
.where(f -> finishedQuery)
|
||||
.toQuery();
|
||||
|
||||
returnProps.setSearchScroll( termConceptsQuery.scroll(theScrollChunkSize) );
|
||||
returnProps.setSearchScroll(termConceptsQuery.scroll(theScrollChunkSize));
|
||||
return returnProps;
|
||||
}
|
||||
|
||||
|
||||
private ValueSet.ConceptReferenceComponent getMatchedConceptIncludedInValueSet(ValueSet.ConceptSetComponent theIncludeOrExclude, TermConcept concept) {
|
||||
return theIncludeOrExclude
|
||||
.getConcept()
|
||||
|
@ -1084,13 +1059,15 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
* Helper method which builds a predicate for the expansion
|
||||
*/
|
||||
private Optional<PredicateFinalStep> buildExpansionPredicate(List<String> theCodes, SearchPredicateFactory thePredicate) {
|
||||
if (CollectionUtils.isEmpty(theCodes)) { return Optional.empty(); }
|
||||
if (CollectionUtils.isEmpty(theCodes)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
if (theCodes.size() < BooleanQuery.getMaxClauseCount()) {
|
||||
return Optional.of(thePredicate.simpleQueryString()
|
||||
.field( "myCode" ).matching( String.join(" | ", theCodes)) );
|
||||
.field("myCode").matching(String.join(" | ", theCodes)));
|
||||
}
|
||||
|
||||
|
||||
// Number of codes is larger than maxClauseCount, so we split the query in several clauses
|
||||
|
||||
// partition codes in lists of BooleanQuery.getMaxClauseCount() size
|
||||
|
@ -1106,7 +1083,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return Optional.of(step);
|
||||
}
|
||||
|
||||
|
||||
private String buildCodeSystemUrlAndVersion(String theSystem, String theIncludeOrExcludeVersion) {
|
||||
String codeSystemUrlAndVersion;
|
||||
if (theIncludeOrExcludeVersion != null) {
|
||||
|
@ -1177,14 +1153,13 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
|
||||
private void handleFilterPropertyDefault(SearchPredicateFactory theF,
|
||||
BooleanPredicateClausesStep<?> theB, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
BooleanPredicateClausesStep<?> theB, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
|
||||
String value = theFilter.getValue();
|
||||
Term term = new Term(CONCEPT_PROPERTY_PREFIX_NAME + theFilter.getProperty(), value);
|
||||
theB.must(theF.match().field(term.field()).matching(term.text()));
|
||||
}
|
||||
|
||||
|
||||
private void handleFilterRegex(SearchPredicateFactory theF, BooleanPredicateClausesStep<?> theB, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
/*
|
||||
* We treat the regex filter as a match on the regex
|
||||
|
@ -1206,12 +1181,11 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
theB.must(theF.regexp()
|
||||
.field(CONCEPT_PROPERTY_PREFIX_NAME + theFilter.getProperty())
|
||||
.matching(value) );
|
||||
}
|
||||
|
||||
.matching(value));
|
||||
}
|
||||
|
||||
private void handleFilterLoincCopyright(SearchPredicateFactory theF, BooleanPredicateClausesStep<?> theB,
|
||||
ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
|
||||
if (theFilter.getOp() == ValueSet.FilterOperator.EQUAL) {
|
||||
|
||||
|
@ -1238,7 +1212,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
theB.mustNot(theF.exists().field(CONCEPT_PROPERTY_PREFIX_NAME + "EXTERNAL_COPYRIGHT_NOTICE"));
|
||||
}
|
||||
|
||||
|
||||
private void addFilterLoincCopyright3rdParty(SearchPredicateFactory theF, BooleanPredicateClausesStep<?> theB) {
|
||||
theB.must(theF.exists().field(CONCEPT_PROPERTY_PREFIX_NAME + "EXTERNAL_COPYRIGHT_NOTICE"));
|
||||
}
|
||||
|
@ -1276,7 +1249,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
b.must(f.bool(innerB -> terms.forEach(term -> innerB.should(f.match().field(term.field()).matching(term.text())))));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void handleFilterLoincParentChild(SearchPredicateFactory f, BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
switch (theFilter.getOp()) {
|
||||
|
@ -1370,7 +1342,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return new Term(CONCEPT_PROPERTY_PREFIX_NAME + theProperty, theValue);
|
||||
}
|
||||
|
||||
|
||||
private List<Term> getAncestorTerms(String theSystem, String theProperty, String theValue) {
|
||||
List<Term> retVal = new ArrayList<>();
|
||||
|
||||
|
@ -1383,7 +1354,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("EnumSwitchStatementWhichMissesCases")
|
||||
private void handleFilterLoincDescendant(String theSystem, SearchPredicateFactory f, BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
switch (theFilter.getOp()) {
|
||||
|
@ -1398,15 +1368,14 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void addLoincFilterDescendantEqual(String theSystem, SearchPredicateFactory f,
|
||||
BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
|
||||
List<Long> parentPids = getCodeParentPids(theSystem, theFilter.getProperty(), theFilter.getValue());
|
||||
if (parentPids.isEmpty()) {
|
||||
// Can't return empty must, because it wil match according to other predicates.
|
||||
// Some day there will be a 'matchNone' predicate (https://discourse.hibernate.org/t/fail-fast-predicate/6062)
|
||||
b.mustNot( f.matchAll() );
|
||||
b.mustNot(f.matchAll());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1423,7 +1392,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
* representing the codes in theFilter.getValue()
|
||||
*/
|
||||
private void addLoincFilterDescendantIn(String theSystem, SearchPredicateFactory f,
|
||||
BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
BooleanPredicateClausesStep<?> b, ValueSet.ConceptSetFilterComponent theFilter) {
|
||||
|
||||
String[] values = theFilter.getValue().split(",");
|
||||
if (values.length == 0) {
|
||||
|
@ -1437,7 +1406,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
)));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the list of parentId(s) of the TermConcept representing theValue as a code
|
||||
*/
|
||||
|
@ -1448,14 +1416,13 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
String[] parentPids = code.getParentPidsAsString().split(" ");
|
||||
List<Long> retVal = Arrays.stream(parentPids)
|
||||
.filter( pid -> !StringUtils.equals(pid, "NONE") )
|
||||
.filter(pid -> !StringUtils.equals(pid, "NONE"))
|
||||
.map(Long::parseLong)
|
||||
.collect(Collectors.toList());
|
||||
logFilteringValueOnProperty(theValue, theProperty);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the list of parentId(s) of the TermConcept representing theValue as a code
|
||||
*/
|
||||
|
@ -1470,7 +1437,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
|
||||
List<Long> retVal = termConcepts.stream()
|
||||
.flatMap(tc -> Arrays.stream(tc.getParentPidsAsString().split(" ")))
|
||||
.filter( pid -> !StringUtils.equals(pid, "NONE") )
|
||||
.filter(pid -> !StringUtils.equals(pid, "NONE"))
|
||||
.map(Long::parseLong)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
|
@ -1488,19 +1455,18 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return "Invalid filter criteria - More TermConcepts were found than indicated codes. Queried codes: [" +
|
||||
join(",", theValues + "]; Obtained TermConcept IDs, codes: [" +
|
||||
theTermConcepts.stream().map(tc -> tc.getId() + ", " + tc.getCode())
|
||||
.collect(joining("; "))+ "]");
|
||||
.collect(joining("; ")) + "]");
|
||||
}
|
||||
|
||||
// case: less TermConcept(s) retrieved than codes queried
|
||||
Set<String> matchedCodes = theTermConcepts.stream().map(TermConcept::getCode).collect(toSet());
|
||||
List<String> notMatchedValues = theValues.stream()
|
||||
.filter(v -> ! matchedCodes.contains (v)) .collect(toList());
|
||||
.filter(v -> !matchedCodes.contains(v)).collect(toList());
|
||||
|
||||
return "Invalid filter criteria - No TermConcept(s) were found for the requested codes: [" +
|
||||
join(",", notMatchedValues + "]");
|
||||
}
|
||||
|
||||
|
||||
private void logFilteringValueOnProperty(String theValue, String theProperty) {
|
||||
ourLog.debug(" * Filtering with value={} on property {}", theValue, theProperty);
|
||||
}
|
||||
|
@ -1560,7 +1526,7 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
Collection<TermConceptDesignation> designations = next
|
||||
.getDesignation()
|
||||
.stream()
|
||||
.map(t->new TermConceptDesignation()
|
||||
.map(t -> new TermConceptDesignation()
|
||||
.setValue(t.getValue())
|
||||
.setLanguage(t.getLanguage())
|
||||
.setUseCode(t.getUse().getCode())
|
||||
|
@ -1765,7 +1731,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Optional<TermConcept> findCode(String theCodeSystem, String theCode) {
|
||||
/*
|
||||
|
@ -1790,12 +1755,13 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
@Transactional(propagation = Propagation.MANDATORY)
|
||||
public List<TermConcept> findCodes(String theCodeSystem, List<String> theCodeList) {
|
||||
TermCodeSystemVersionDetails csv = getCurrentCodeSystemVersion(theCodeSystem);
|
||||
if (csv == null) { return Collections.emptyList(); }
|
||||
if (csv == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return myConceptDao.findByCodeSystemAndCodeList(csv.myPid, theCodeList);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private TermCodeSystemVersionDetails getCurrentCodeSystemVersion(String theCodeSystemIdentifier) {
|
||||
String version = getVersionFromIdentifier(theCodeSystemIdentifier);
|
||||
|
@ -1821,19 +1787,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
private static class TermCodeSystemVersionDetails {
|
||||
|
||||
|
||||
private final long myPid;
|
||||
private final String myCodeSystemVersionId;
|
||||
|
||||
public TermCodeSystemVersionDetails(long thePid, String theCodeSystemVersionId) {
|
||||
myPid = thePid;
|
||||
myCodeSystemVersionId = theCodeSystemVersionId;
|
||||
}
|
||||
}
|
||||
|
||||
private String getVersionFromIdentifier(String theUri) {
|
||||
String retVal = null;
|
||||
if (StringUtils.isNotEmpty((theUri))) {
|
||||
|
@ -2021,14 +1974,13 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
myCachingValidationSupport.invalidateCaches();
|
||||
}
|
||||
|
||||
private synchronized void setPreExpandingValueSets(boolean thePreExpandingValueSets) {
|
||||
myPreExpandingValueSets = thePreExpandingValueSets;
|
||||
}
|
||||
|
||||
private synchronized boolean isPreExpandingValueSets() {
|
||||
return myPreExpandingValueSets;
|
||||
}
|
||||
|
||||
private synchronized void setPreExpandingValueSets(boolean thePreExpandingValueSets) {
|
||||
myPreExpandingValueSets = thePreExpandingValueSets;
|
||||
}
|
||||
|
||||
private boolean isNotSafeToPreExpandValueSets() {
|
||||
return myDeferredStorageSvc != null && !myDeferredStorageSvc.isStorageQueueEmpty(true);
|
||||
|
@ -2430,7 +2382,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
private FhirVersionIndependentConcept toConcept(IPrimitiveType<String> theCodeType, IPrimitiveType<String> theCodeSystemIdentifierType, IBaseCoding theCodingType) {
|
||||
String code = theCodeType != null ? theCodeType.getValueAsString() : null;
|
||||
|
@ -2446,7 +2397,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return new FhirVersionIndependentConcept(system, code, null, systemVersion);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* When the search is for unversioned loinc system it uses the forcedId to obtain the current
|
||||
* version, as it is not necessarily the last one anymore.
|
||||
|
@ -2471,16 +2421,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return Optional.of(termValueSetList.get(0));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String createMessageAppendForDisplayMismatch(String theCodeSystemUrl, String theDisplay, String theExpectedDisplay) {
|
||||
return " - Concept Display \"" + theDisplay + "\" does not match expected \"" + theExpectedDisplay + "\" for CodeSystem: " + theCodeSystemUrl;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String createMessageAppendForCodeNotFoundInCodeSystem(String theCodeSystemUrl) {
|
||||
return " - Code is not found in CodeSystem: " + theCodeSystemUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<IBaseResource> readCodeSystemByForcedId(String theForcedId) {
|
||||
@SuppressWarnings("unchecked")
|
||||
|
@ -2498,11 +2438,6 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return Optional.of(cs);
|
||||
}
|
||||
|
||||
|
||||
private static final int SECONDS_IN_MINUTE = 60;
|
||||
private static final int INDEXED_ROOTS_LOGGING_COUNT = 50_000;
|
||||
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public ReindexTerminologyResult reindexTerminology() throws InterruptedException {
|
||||
|
@ -2523,14 +2458,14 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
try {
|
||||
SearchSession searchSession = getSearchSession();
|
||||
searchSession
|
||||
.massIndexer( TermConcept.class )
|
||||
.dropAndCreateSchemaOnStart( true )
|
||||
.purgeAllOnStart( false )
|
||||
.batchSizeToLoadObjects( 100 )
|
||||
.cacheMode( CacheMode.IGNORE )
|
||||
.threadsToLoadObjects( 6 )
|
||||
.transactionTimeout( 60 * SECONDS_IN_MINUTE )
|
||||
.monitor( new PojoMassIndexingLoggingMonitor(INDEXED_ROOTS_LOGGING_COUNT) )
|
||||
.massIndexer(TermConcept.class)
|
||||
.dropAndCreateSchemaOnStart(true)
|
||||
.purgeAllOnStart(false)
|
||||
.batchSizeToLoadObjects(100)
|
||||
.cacheMode(CacheMode.IGNORE)
|
||||
.threadsToLoadObjects(6)
|
||||
.transactionTimeout(60 * SECONDS_IN_MINUTE)
|
||||
.monitor(new PojoMassIndexingLoggingMonitor(INDEXED_ROOTS_LOGGING_COUNT))
|
||||
.startAndWait();
|
||||
} finally {
|
||||
myDeferredStorageSvc.setProcessDeferred(true);
|
||||
|
@ -2539,13 +2474,11 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return ReindexTerminologyResult.SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
boolean isBatchTerminologyTasksRunning() {
|
||||
return isNotSafeToPreExpandValueSets() || isPreExpandingValueSets();
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
int calculateObjectLoadingThreadNumber() {
|
||||
IConnectionPoolInfoProvider connectionPoolInfoProvider =
|
||||
|
@ -2563,12 +2496,136 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return objectThreads;
|
||||
}
|
||||
|
||||
|
||||
@VisibleForTesting
|
||||
SearchSession getSearchSession() {
|
||||
return Search.session( myEntityManager );
|
||||
return Search.session(myEntityManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
ValueSet canonicalInput = myVersionCanonicalizer.valueSetToCanonical(theValueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = expandValueSet(theExpansionOptions, canonicalInput);
|
||||
return new ValueSetExpansionOutcome(myVersionCanonicalizer.valueSetFromCanonical(expandedR4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theInput) {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = myVersionCanonicalizer.valueSetToCanonical(theInput);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = expandValueSet(theExpansionOptions, valueSetToExpand);
|
||||
return myVersionCanonicalizer.valueSetFromCanonical(valueSetR4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = myVersionCanonicalizer.valueSetToCanonical(theValueSetToExpand);
|
||||
expandValueSet(theExpansionOptions, valueSetToExpand, theValueSetCodeAccumulator);
|
||||
}
|
||||
|
||||
private org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) {
|
||||
Class<? extends IBaseResource> type = getFhirContext().getResourceDefinition("ValueSet").getImplementingClass();
|
||||
IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").toResource(type, theResourceTable, null, false);
|
||||
return myVersionCanonicalizer.valueSetToCanonical(valueSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCodeIsInPreExpandedValueSet(ConceptValidationOptions theOptions, IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null");
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = myVersionCanonicalizer.valueSetToCanonical(theValueSet);
|
||||
org.hl7.fhir.r4.model.Coding codingR4 = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCoding);
|
||||
org.hl7.fhir.r4.model.CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConcept);
|
||||
|
||||
return validateCodeIsInPreExpandedValueSet(theOptions, valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConcept);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) {
|
||||
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null");
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = myVersionCanonicalizer.valueSetToCanonical(theValueSet);
|
||||
return isValueSetPreExpandedForCodeValidation(valueSetR4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
|
||||
return lookupCode(theSystem, theCode, theDisplayLanguage);
|
||||
}
|
||||
|
||||
private static class TermCodeSystemVersionDetails {
|
||||
|
||||
|
||||
private final long myPid;
|
||||
private final String myCodeSystemVersionId;
|
||||
|
||||
public TermCodeSystemVersionDetails(long thePid, String theCodeSystemVersionId) {
|
||||
myPid = thePid;
|
||||
myCodeSystemVersionId = theCodeSystemVersionId;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Job implements HapiJob {
|
||||
@Autowired
|
||||
private ITermReadSvc myTerminologySvc;
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext theContext) {
|
||||
myTerminologySvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties returned from method buildSearchScroll
|
||||
*/
|
||||
private static final class SearchProperties {
|
||||
private SearchScroll<EntityReference> mySearchScroll;
|
||||
private Optional<PredicateFinalStep> myExpansionStepOpt;
|
||||
private List<String> myIncludeOrExcludeCodes;
|
||||
|
||||
public SearchScroll<EntityReference> getSearchScroll() {
|
||||
return mySearchScroll;
|
||||
}
|
||||
|
||||
public void setSearchScroll(SearchScroll<EntityReference> theSearchScroll) {
|
||||
mySearchScroll = theSearchScroll;
|
||||
}
|
||||
|
||||
public Optional<PredicateFinalStep> getExpansionStepOpt() {
|
||||
return myExpansionStepOpt;
|
||||
}
|
||||
|
||||
public void setExpansionStepOpt(Optional<PredicateFinalStep> theExpansionStepOpt) {
|
||||
myExpansionStepOpt = theExpansionStepOpt;
|
||||
}
|
||||
|
||||
public List<String> getIncludeOrExcludeCodes() {
|
||||
return myIncludeOrExcludeCodes;
|
||||
}
|
||||
|
||||
public void setIncludeOrExcludeCodes(List<String> theIncludeOrExcludeCodes) {
|
||||
myIncludeOrExcludeCodes = theIncludeOrExcludeCodes;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isValueSetDisplayLanguageMatch(ValueSetExpansionOptions theExpansionOptions, String theStoredLang) {
|
||||
if (theExpansionOptions == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (theExpansionOptions.getTheDisplayLanguage() == null || theStoredLang == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return theExpansionOptions.getTheDisplayLanguage().equalsIgnoreCase(theStoredLang);
|
||||
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String createMessageAppendForDisplayMismatch(String theCodeSystemUrl, String theDisplay, String theExpectedDisplay) {
|
||||
return " - Concept Display \"" + theDisplay + "\" does not match expected \"" + theExpectedDisplay + "\" for CodeSystem: " + theCodeSystemUrl;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String createMessageAppendForCodeNotFoundInCodeSystem(String theCodeSystemUrl) {
|
||||
return " - Code is not found in CodeSystem: " + theCodeSystemUrl;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public static void setForceDisableHibernateSearchForUnitTest(boolean theForceDisableHibernateSearchForUnitTest) {
|
||||
|
@ -2669,101 +2726,5 @@ public class TermReadSvcImpl implements ITermReadSvc {
|
|||
return theReqLang.equalsIgnoreCase(theStoredLang);
|
||||
}
|
||||
|
||||
public static class Job implements HapiJob {
|
||||
@Autowired
|
||||
private ITermReadSvc myTerminologySvc;
|
||||
|
||||
@Override
|
||||
public void execute(JobExecutionContext theContext) {
|
||||
myTerminologySvc.preExpandDeferredValueSetsToTerminologyTables();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Properties returned from method buildSearchScroll
|
||||
*/
|
||||
private static final class SearchProperties {
|
||||
private SearchScroll<EntityReference> mySearchScroll;
|
||||
private Optional<PredicateFinalStep> myExpansionStepOpt;
|
||||
private List<String> myIncludeOrExcludeCodes;
|
||||
|
||||
public SearchScroll<EntityReference> getSearchScroll() {
|
||||
return mySearchScroll;
|
||||
}
|
||||
|
||||
public void setSearchScroll(SearchScroll<EntityReference> theSearchScroll) {
|
||||
mySearchScroll = theSearchScroll;
|
||||
}
|
||||
|
||||
public Optional<PredicateFinalStep> getExpansionStepOpt() {
|
||||
return myExpansionStepOpt;
|
||||
}
|
||||
|
||||
public void setExpansionStepOpt(Optional<PredicateFinalStep> theExpansionStepOpt) {
|
||||
myExpansionStepOpt = theExpansionStepOpt;
|
||||
}
|
||||
|
||||
public List<String> getIncludeOrExcludeCodes() {
|
||||
return myIncludeOrExcludeCodes;
|
||||
}
|
||||
|
||||
public void setIncludeOrExcludeCodes(List<String> theIncludeOrExcludeCodes) {
|
||||
myIncludeOrExcludeCodes = theIncludeOrExcludeCodes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueSetExpansionOutcome expandValueSet(ValidationSupportContext theValidationSupportContext, ValueSetExpansionOptions theExpansionOptions, @Nonnull IBaseResource theValueSetToExpand) {
|
||||
ValueSet canonicalInput = myVersionCanonicalizer.valueSetToCanonical(theValueSetToExpand);
|
||||
org.hl7.fhir.r4.model.ValueSet expandedR4 = expandValueSet(theExpansionOptions, canonicalInput);
|
||||
return new ValueSetExpansionOutcome(myVersionCanonicalizer.valueSetFromCanonical(expandedR4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBaseResource expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theInput) {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = myVersionCanonicalizer.valueSetToCanonical(theInput);
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = expandValueSet(theExpansionOptions, valueSetToExpand);
|
||||
return myVersionCanonicalizer.valueSetFromCanonical(valueSetR4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expandValueSet(ValueSetExpansionOptions theExpansionOptions, IBaseResource theValueSetToExpand, IValueSetConceptAccumulator theValueSetCodeAccumulator) {
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetToExpand = myVersionCanonicalizer.valueSetToCanonical(theValueSetToExpand);
|
||||
expandValueSet(theExpansionOptions, valueSetToExpand, theValueSetCodeAccumulator);
|
||||
}
|
||||
|
||||
private org.hl7.fhir.r4.model.ValueSet getValueSetFromResourceTable(ResourceTable theResourceTable) {
|
||||
Class<? extends IBaseResource> type = getFhirContext().getResourceDefinition("ValueSet").getImplementingClass();
|
||||
IBaseResource valueSet = myDaoRegistry.getResourceDao("ValueSet").toResource(type, theResourceTable, null, false);
|
||||
return myVersionCanonicalizer.valueSetToCanonical(valueSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCodeIsInPreExpandedValueSet(ConceptValidationOptions theOptions, IBaseResource theValueSet, String theSystem, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null");
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = myVersionCanonicalizer.valueSetToCanonical(theValueSet);
|
||||
org.hl7.fhir.r4.model.Coding codingR4 = myVersionCanonicalizer.codingToCanonical((IBaseCoding) theCoding);
|
||||
org.hl7.fhir.r4.model.CodeableConcept codeableConcept = myVersionCanonicalizer.codeableConceptToCanonical(theCodeableConcept);
|
||||
|
||||
return validateCodeIsInPreExpandedValueSet(theOptions, valueSetR4, theSystem, theCode, theDisplay, codingR4, codeableConcept);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private VersionCanonicalizer myVersionCanonicalizer;
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet) {
|
||||
ValidateUtil.isNotNullOrThrowUnprocessableEntity(theValueSet, "ValueSet must not be null");
|
||||
org.hl7.fhir.r4.model.ValueSet valueSetR4 = myVersionCanonicalizer.valueSetToCanonical(theValueSet);
|
||||
return isValueSetPreExpandedForCodeValidation(valueSetR4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LookupCodeResult lookupCode(ValidationSupportContext theValidationSupportContext, String theSystem, String theCode, String theDisplayLanguage) {
|
||||
return lookupCode(theSystem, theCode, theDisplayLanguage);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -160,11 +160,17 @@
|
|||
</exclusions>
|
||||
</dependency>
|
||||
<!-- test -->
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
|
|
|
@ -48,6 +48,12 @@
|
|||
<groupId>org.hl7.fhir.testcases</groupId>
|
||||
<artifactId>fhir-test-cases</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
|
|
|
@ -42,6 +42,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
|
|
|
@ -116,8 +116,15 @@
|
|||
|
||||
<!-- Caffeine -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
|
|
|
@ -132,6 +132,12 @@
|
|||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
|
@ -154,6 +160,7 @@
|
|||
</dependency>
|
||||
|
||||
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
|
|
|
@ -24,9 +24,9 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -93,10 +93,7 @@ public class SearchParamExtractorR4 extends BaseSearchParamExtractor implements
|
|||
myFhirPathEngine = new FHIRPathEngine(worker);
|
||||
myFhirPathEngine.setHostServices(new SearchParamExtractorR4HostServices());
|
||||
|
||||
myParsedFhirPathCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
myParsedFhirPathCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(10));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -93,10 +93,7 @@ public class SearchParamExtractorR4B extends BaseSearchParamExtractor implements
|
|||
myFhirPathEngine = new FHIRPathEngine(worker);
|
||||
myFhirPathEngine.setHostServices(new SearchParamExtractorR4BHostServices());
|
||||
|
||||
myParsedFhirPathCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
myParsedFhirPathCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(10));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.PathEngineException;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
|
@ -80,11 +80,8 @@ public class SearchParamExtractorR5 extends BaseSearchParamExtractor implements
|
|||
IWorkerContext worker = new HapiWorkerContext(getContext(), getContext().getValidationSupport());
|
||||
myFhirPathEngine = new FHIRPathEngine(worker);
|
||||
myFhirPathEngine.setHostServices(new SearchParamExtractorR5HostServices());
|
||||
|
||||
myParsedFhirPathCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
myParsedFhirPathCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(10));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -72,6 +72,12 @@
|
|||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -25,7 +25,7 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
|
|
|
@ -21,6 +21,13 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -51,6 +51,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.hl7.fhir.testcases</groupId>
|
||||
|
@ -178,6 +184,7 @@
|
|||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.jayway.jsonpath</groupId>
|
||||
<artifactId>json-path-assert</artifactId>
|
||||
|
|
|
@ -3,8 +3,8 @@ package ca.uhn.fhir.jpa.util;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
|
||||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
|
@ -77,7 +77,7 @@ class MemoryCacheServiceTest {
|
|||
}
|
||||
|
||||
void withCacheOfSize(int theMaxSize) {
|
||||
myCache = Caffeine.newBuilder().expireAfterWrite(60, TimeUnit.MINUTES).maximumSize(theMaxSize).build();
|
||||
myCache = CacheFactory.build(TimeUnit.MINUTES.toMillis(60), theMaxSize);
|
||||
}
|
||||
|
||||
void fillCacheWithRange(int theStart, int theEnd) {
|
||||
|
|
|
@ -107,8 +107,14 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -25,6 +25,13 @@
|
|||
<artifactId>org.hl7.fhir.utilities</artifactId>
|
||||
<version>${fhir_core_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Server -->
|
||||
<dependency>
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.3.0-PRE4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HAPI FHIR - ServiceLoaders - Caching API</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
34
hapi-fhir-serviceloaders/hapi-fhir-caching-api/src/main/java/ca/uhn/fhir/sl/cache/Cache.java
vendored
Normal file
34
hapi-fhir-serviceloaders/hapi-fhir-caching-api/src/main/java/ca/uhn/fhir/sl/cache/Cache.java
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
package ca.uhn.fhir.sl.cache;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* This interface is a blend between
|
||||
* <a href="https://github.com/ben-manes/caffeine">Caffeine's Cache</a> and
|
||||
* <a href="https://github.com/google/guava/wiki/CachesExplained"></a>Guava's Cache</a>.
|
||||
*
|
||||
* Please check their documentation for information in the methods below.
|
||||
*/
|
||||
public interface Cache<K, V> {
|
||||
V getIfPresent(K key);
|
||||
|
||||
V get(K key, Function<? super K, ? extends V> mappingFunction);
|
||||
|
||||
Map<K, V> getAllPresent(Iterable<? extends K> keys);
|
||||
|
||||
void put(K key, V value);
|
||||
|
||||
void putAll(Map<? extends K, ? extends V> map);
|
||||
|
||||
void invalidate(K key);
|
||||
|
||||
void invalidateAll(Iterable<? extends K> keys);
|
||||
|
||||
void invalidateAll();
|
||||
|
||||
long estimatedSize();
|
||||
|
||||
void cleanUp();
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package ca.uhn.fhir.sl.cache;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class CacheFactory {
|
||||
|
||||
static ServiceLoader<CacheProvider> loader = ServiceLoader.load(CacheProvider.class);
|
||||
|
||||
public static Iterator<CacheProvider> providers(boolean refresh) {
|
||||
if (refresh) {
|
||||
loader.reload();
|
||||
}
|
||||
return loader.iterator();
|
||||
}
|
||||
|
||||
public static <K, V> Cache<K, V> build(long timeoutMillis) {
|
||||
if (providers(false).hasNext()) {
|
||||
return providers(false).next().create(timeoutMillis);
|
||||
}
|
||||
throw new RuntimeException(Msg.code(2200) + "No Cache Service Providers found. Choose between hapi-fhir-caching-caffeine (Default) and hapi-fhir-caching-guava (Android)");
|
||||
}
|
||||
|
||||
public static <K, V> LoadingCache<K, V> build(long timeoutMillis, CacheLoader<K, V> cacheLoader) {
|
||||
if (providers(false).hasNext()) {
|
||||
return providers(false).next().create(timeoutMillis, cacheLoader);
|
||||
}
|
||||
throw new RuntimeException(Msg.code(2201) + "No Cache Service Providers found. Choose between hapi-fhir-caching-caffeine (Default) and hapi-fhir-caching-guava (Android)");
|
||||
}
|
||||
|
||||
public static <K, V> Cache<K, V> build(long timeoutMillis, long maximumSize) {
|
||||
if (providers(false).hasNext()) {
|
||||
return providers(false).next().create(timeoutMillis, maximumSize);
|
||||
}
|
||||
throw new RuntimeException(Msg.code(2202) + "No Cache Service Providers found. Choose between hapi-fhir-caching-caffeine (Default) and hapi-fhir-caching-guava (Android)");
|
||||
}
|
||||
|
||||
public static <K, V> LoadingCache<K, V> build(long timeoutMillis, long maximumSize, CacheLoader<K, V> cacheLoader) {
|
||||
if (providers(false).hasNext()) {
|
||||
return providers(false).next().create(timeoutMillis, maximumSize, cacheLoader);
|
||||
}
|
||||
throw new RuntimeException(Msg.code(2203) + "No Cache Service Providers found. Choose between hapi-fhir-caching-caffeine (Default) and hapi-fhir-caching-guava (Android)");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package ca.uhn.fhir.sl.cache;
|
||||
|
||||
public interface CacheLoader<K, V> {
|
||||
V load(K var1) throws Exception;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package ca.uhn.fhir.sl.cache;
|
||||
|
||||
public interface CacheProvider<K,V> {
|
||||
Cache create(long timeoutMillis);
|
||||
|
||||
Cache create(long timeoutMillis, long maximumSize);
|
||||
|
||||
LoadingCache create(long timeoutMillis, CacheLoader<K,V> cacheLoader);
|
||||
|
||||
LoadingCache create(long timeoutMillis, long maximumSize, CacheLoader<K,V> cacheLoader);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package ca.uhn.fhir.sl.cache;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* This interface is a blend between
|
||||
* <a href="https://github.com/ben-manes/caffeine">Caffeine's LoadingCache</a> and
|
||||
* <a href="https://github.com/google/guava/wiki/CachesExplained"></a>Guava's LoadingCache</a>.
|
||||
*
|
||||
* Please check their documentation for information in the methods below.
|
||||
*/
|
||||
public interface LoadingCache<K, V> extends Cache<K, V> {
|
||||
V get(K key);
|
||||
|
||||
Map<K, V> getAll(Iterable<? extends K> keys);
|
||||
|
||||
void refresh(K key);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.3.0-PRE4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-caching-caffeine</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HAPI FHIR - ServiceLoaders - Caching Caffeine</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>6.3.0-PRE4-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,59 @@
|
|||
package ca.uhn.fhir.sl.cache.caffeine;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CacheDelegator<K, V> implements ca.uhn.fhir.sl.cache.Cache<K, V> {
|
||||
|
||||
com.github.benmanes.caffeine.cache.Cache<K, V> cache;
|
||||
|
||||
public CacheDelegator(com.github.benmanes.caffeine.cache.Cache<K, V> impl) {
|
||||
this.cache = impl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getIfPresent(K key) {
|
||||
return cache.getIfPresent(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key, Function<? super K, ? extends V> mappingFunction) {
|
||||
return cache.get(key, mappingFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> getAllPresent(Iterable<? extends K> keys) { return cache.getAllPresent(keys); }
|
||||
|
||||
@Override
|
||||
public void put(K key, V value) {
|
||||
cache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
cache.putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(K key) { cache.invalidate(key); }
|
||||
|
||||
@Override
|
||||
public void invalidateAll(Iterable<? extends K> keys) {
|
||||
cache.invalidateAll(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateAll() {
|
||||
cache.invalidateAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimatedSize() {
|
||||
return cache.estimatedSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp(){
|
||||
cache.cleanUp();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package ca.uhn.fhir.sl.cache.caffeine;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheLoader;
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
|
||||
public class CacheProvider<K,V> implements ca.uhn.fhir.sl.cache.CacheProvider<K,V> {
|
||||
|
||||
public Cache<K,V> create(long timeoutMillis) {
|
||||
return new CacheDelegator<K,V>(
|
||||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public LoadingCache<K,V> create(long timeoutMillis, CacheLoader<K,V> loading) {
|
||||
return new LoadingCacheDelegator<K,V>(
|
||||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.build(loading::load)
|
||||
);
|
||||
}
|
||||
|
||||
public Cache<K,V> create(long timeoutMillis, long maximumSize) {
|
||||
return new CacheDelegator<K,V>(
|
||||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.maximumSize(maximumSize)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public LoadingCache<K,V> create(long timeoutMillis, long maximumSize, CacheLoader<K,V> loading) {
|
||||
return new LoadingCacheDelegator<K,V>(
|
||||
Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.maximumSize(maximumSize)
|
||||
.build(loading::load)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.uhn.fhir.sl.cache.caffeine;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
|
||||
public class LoadingCacheDelegator<K, V> extends CacheDelegator<K, V> implements LoadingCache<K,V> {
|
||||
|
||||
public LoadingCacheDelegator(com.github.benmanes.caffeine.cache.LoadingCache<K, V> impl) {
|
||||
super(impl);
|
||||
}
|
||||
|
||||
public com.github.benmanes.caffeine.cache.LoadingCache<K, V> getCache() {
|
||||
return (com.github.benmanes.caffeine.cache.LoadingCache<K, V>) cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
return getCache().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> getAll(Iterable<? extends K> keys) {
|
||||
return getCache().getAll(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(K key) { getCache().refresh(key); }
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
ca.uhn.fhir.sl.cache.caffeine.CacheProvider
|
|
@ -0,0 +1,17 @@
|
|||
package ca.uhn.fhir.sl.cache.caffeine;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class CacheLoaderTest {
|
||||
@Test
|
||||
void loaderReturnsNullTest() {
|
||||
LoadingCache<String, String> cache = CacheFactory.build(1000, key -> {
|
||||
return null;
|
||||
});
|
||||
assertNull(cache.get("1"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.uhn.fhir.sl.cache.caffeine;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ServiceLoaderTest {
|
||||
@Test
|
||||
void loaderIsAvailable() {
|
||||
assertNotNull(CacheFactory.build(1000));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.3.0-PRE4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-caching-guava</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HAPI FHIR - ServiceLoaders - Caching Guava</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,78 @@
|
|||
package ca.uhn.fhir.sl.cache.guava;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
|
||||
public class CacheDelegator<K, V> implements ca.uhn.fhir.sl.cache.Cache<K, V> {
|
||||
|
||||
com.google.common.cache.Cache<K, V> cache;
|
||||
|
||||
public CacheDelegator(com.google.common.cache.Cache<K, V> impl) {
|
||||
this.cache = impl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getIfPresent(K key) {
|
||||
return cache.getIfPresent(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key, Function<? super K, ? extends V> mappingFunction) {
|
||||
try {
|
||||
return cache.get(key, () -> mappingFunction.apply(key));
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(Msg.code(2206) + "Failed to red from cache", e);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
if (e.getCause() instanceof RuntimeException) {
|
||||
// Unwrap exception to match Caffeine
|
||||
throw (RuntimeException)e.getCause();
|
||||
}
|
||||
throw e;
|
||||
} catch (CacheLoader.InvalidCacheLoadException e) {
|
||||
// If the entry is not found or load as null, returns null instead of an exception
|
||||
// This matches the behaviour of Caffeine
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> getAllPresent(Iterable<? extends K> keys) { return cache.getAllPresent(keys); }
|
||||
|
||||
@Override
|
||||
public void put(K key, V value) {
|
||||
cache.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends V> map) {
|
||||
cache.putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(K key) { cache.invalidate(key); }
|
||||
|
||||
@Override
|
||||
public void invalidateAll(Iterable<? extends K> keys) {
|
||||
cache.invalidateAll(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateAll() {
|
||||
cache.invalidateAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long estimatedSize() {
|
||||
return cache.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanUp(){
|
||||
cache.cleanUp();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package ca.uhn.fhir.sl.cache.guava;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheLoader;
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
|
||||
public class CacheProvider<K,V> implements ca.uhn.fhir.sl.cache.CacheProvider<K,V> {
|
||||
|
||||
public Cache<K,V> create(long timeoutMillis) {
|
||||
return new CacheDelegator<K,V>(
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public LoadingCache<K,V> create(long timeoutMillis, CacheLoader<K,V> loading) {
|
||||
return new LoadingCacheDelegator<K,V>(
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.build(new com.google.common.cache.CacheLoader<>() {
|
||||
@Override
|
||||
public V load(K k) throws Exception {
|
||||
return loading.load(k);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public Cache<K,V> create(long timeoutMillis, long maximumSize) {
|
||||
return new CacheDelegator<K,V>(
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.maximumSize(maximumSize)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
public LoadingCache<K,V> create(long timeoutMillis, long maximumSize, CacheLoader<K,V> loading) {
|
||||
return new LoadingCacheDelegator<K,V>(
|
||||
CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.maximumSize(maximumSize)
|
||||
.build(new com.google.common.cache.CacheLoader<>() {
|
||||
@Override
|
||||
public V load(K k) throws Exception {
|
||||
return loading.load(k);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package ca.uhn.fhir.sl.cache.guava;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
|
||||
public class LoadingCacheDelegator<K, V> extends CacheDelegator<K, V> implements LoadingCache<K,V> {
|
||||
|
||||
public LoadingCacheDelegator(com.google.common.cache.LoadingCache<K, V> impl) { super(impl); }
|
||||
|
||||
public com.google.common.cache.LoadingCache<K, V> getCache() {
|
||||
return (com.google.common.cache.LoadingCache<K, V>) cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
try {
|
||||
return getCache().get(key);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
if (e.getCause() instanceof RuntimeException) {
|
||||
// Unwrap exception to match Caffeine
|
||||
throw (RuntimeException)e.getCause();
|
||||
}
|
||||
throw e;
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(Msg.code(2204) + "Failed to red from cache", e);
|
||||
} catch (CacheLoader.InvalidCacheLoadException e) {
|
||||
// If the entry is not found or load as null, returns null instead of an exception.
|
||||
// This matches the behaviour of Caffeine
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> getAll(Iterable<? extends K> keys) {
|
||||
try {
|
||||
return getCache().getAll(keys);
|
||||
} catch (UncheckedExecutionException e) {
|
||||
if (e.getCause() instanceof RuntimeException) {
|
||||
// Unwrap exception to match Caffeine
|
||||
throw (RuntimeException)e.getCause();
|
||||
}
|
||||
throw e;
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException(Msg.code(2205) + "Failed to red from cache", e);
|
||||
} catch (CacheLoader.InvalidCacheLoadException e) {
|
||||
// If the entry is not found or load as null, returns null instead of an exception
|
||||
// This matches the behaviour of Caffeine
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(K key) { getCache().refresh(key); }
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
ca.uhn.fhir.sl.cache.guava.CacheProvider
|
|
@ -0,0 +1,17 @@
|
|||
package ca.uhn.fhir.sl.cache.guava;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class CacheLoaderTest {
|
||||
@Test
|
||||
void loaderReturnsNullTest() {
|
||||
LoadingCache<String, String> cache = CacheFactory.build(1000, key -> {
|
||||
return null;
|
||||
});
|
||||
assertNull(cache.get("1"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.uhn.fhir.sl.cache.guava;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class ServiceLoaderTest {
|
||||
@Test
|
||||
void loaderIsAvailable() {
|
||||
assertNotNull(CacheFactory.build(1000));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.3.0-PRE4-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<!-- This module exists to facilitate running tests with caffeine and guava.
|
||||
By changing the dependency below, all projects switch to the preferred lib -->
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>HAPI FHIR - ServiceLoaders - Caching Testing</name>
|
||||
|
||||
<dependencies>
|
||||
<!-- Change here (and run mvn install, mvn test -U) to run test cases with different cache library -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-caffeine</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,24 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.3.0-PRE4-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>HAPI FHIR - ServiceLoaders</name>
|
||||
|
||||
<modules>
|
||||
<module>hapi-fhir-caching-api</module>
|
||||
<module>hapi-fhir-caching-guava</module>
|
||||
<module>hapi-fhir-caching-caffeine</module>
|
||||
<module>hapi-fhir-caching-testing</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
|
@ -16,10 +16,6 @@
|
|||
<description>Tooling for migrating SQL schemas.</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-jdbc</artifactId>
|
||||
|
@ -85,6 +81,8 @@
|
|||
<artifactId>sqlbuilder</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- test -->
|
||||
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-test-utilities</artifactId>
|
||||
|
|
|
@ -59,6 +59,11 @@
|
|||
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<!-- TODO KHS remove jpa stuff from here -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
|
|
|
@ -23,10 +23,11 @@ package ca.uhn.fhir.jpa.util;
|
|||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.model.TranslationQuery;
|
||||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
@ -35,6 +36,7 @@ import javax.annotation.Nonnull;
|
|||
import javax.annotation.PostConstruct;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
|
@ -87,10 +89,7 @@ public class MemoryCacheService {
|
|||
break;
|
||||
}
|
||||
|
||||
Cache<Object, Object> nextCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutSeconds, SECONDS)
|
||||
.maximumSize(maximumSize)
|
||||
.build();
|
||||
Cache<Object, Object> nextCache = CacheFactory.build(SECONDS.toMillis(timeoutSeconds), maximumSize);
|
||||
|
||||
myCaches.put(next, nextCache);
|
||||
}
|
||||
|
|
|
@ -126,6 +126,11 @@
|
|||
<artifactId>org.hl7.fhir.dstu3</artifactId>
|
||||
<version>${fhir_core_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fhir</groupId>
|
||||
<artifactId>ucum</artifactId>
|
||||
|
@ -197,15 +202,14 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Used by the validator -->
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<optional>true</optional>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-core</artifactId>
|
||||
|
|
|
@ -7,10 +7,10 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.dstu3.context.IWorkerContext;
|
||||
import org.hl7.fhir.dstu3.formats.IParser;
|
||||
import org.hl7.fhir.dstu3.formats.ParserType;
|
||||
|
@ -43,7 +43,6 @@ import java.util.Collections;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -64,7 +63,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
|
|||
if (System.getProperties().containsKey(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS)) {
|
||||
timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS));
|
||||
}
|
||||
myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build();
|
||||
myFetchedResourceCache = CacheFactory.build(timeoutMillis);
|
||||
|
||||
// Set a default locale
|
||||
setValidationMessageLanguage(getLocale());
|
||||
|
|
|
@ -40,6 +40,11 @@
|
|||
<artifactId>org.hl7.fhir.r4</artifactId>
|
||||
<version>${fhir_core_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fhir</groupId>
|
||||
<artifactId>ucum</artifactId>
|
||||
|
@ -76,12 +81,6 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Used by the validator -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Test dependencies on other optional parts of HAPI
|
||||
|
@ -174,6 +173,13 @@
|
|||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-core</artifactId>
|
||||
|
@ -259,6 +265,7 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- UNIT TEST DEPENDENCIES -->
|
||||
<dependency>
|
||||
<groupId>net.sf.json-lib</groupId>
|
||||
|
|
|
@ -7,11 +7,11 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.fhir.ucum.UcumService;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||
|
@ -66,7 +66,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
|
|||
timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS));
|
||||
}
|
||||
|
||||
myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build();
|
||||
myFetchedResourceCache = CacheFactory.build(timeoutMillis);
|
||||
|
||||
// Set a default locale
|
||||
setValidationMessageLanguage(getLocale());
|
||||
|
|
|
@ -16,12 +16,13 @@
|
|||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
@ -76,13 +77,6 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Used by the validator -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Test dependencies on other optional parts of HAPI
|
||||
-->
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
package org.hl7.fhir.r4b.hapi.ctx;
|
||||
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.fhir.ucum.UcumService;
|
||||
|
@ -49,7 +49,6 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -71,7 +70,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
|
|||
timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS));
|
||||
}
|
||||
|
||||
myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build();
|
||||
myFetchedResourceCache = CacheFactory.build(timeoutMillis);
|
||||
|
||||
// Set a default locale
|
||||
setValidationMessageLanguage(getLocale());
|
||||
|
|
|
@ -35,6 +35,11 @@
|
|||
<artifactId>org.hl7.fhir.r5</artifactId>
|
||||
<version>${fhir_core_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fhir</groupId>
|
||||
<artifactId>ucum</artifactId>
|
||||
|
@ -76,13 +81,6 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Used by the validator -->
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
Test dependencies on other optional parts of HAPI
|
||||
-->
|
||||
|
@ -154,6 +152,12 @@
|
|||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-core</artifactId>
|
||||
|
|
|
@ -7,11 +7,11 @@ import ca.uhn.fhir.context.support.ValidationSupportContext;
|
|||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.fhir.ucum.UcumService;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.exceptions.TerminologyServiceException;
|
||||
import org.hl7.fhir.r5.context.IWorkerContext;
|
||||
|
@ -71,7 +71,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
|
|||
timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS));
|
||||
}
|
||||
|
||||
myFetchedResourceCache = Caffeine.newBuilder().expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS).build();
|
||||
myFetchedResourceCache = CacheFactory.build(timeoutMillis);
|
||||
|
||||
// Set a default locale
|
||||
setValidationMessageLanguage(getLocale());
|
||||
|
|
|
@ -26,6 +26,17 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-caffeine</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>org.hl7.fhir.validation</artifactId>
|
||||
|
@ -75,15 +86,11 @@
|
|||
<version>${fhir_core_version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.fhir</groupId>
|
||||
<artifactId>ucum</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.fhir</groupId>
|
||||
<artifactId>ucum</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.findbugs</groupId>
|
||||
<artifactId>jsr305</artifactId>
|
||||
|
|
|
@ -6,10 +6,10 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
|||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||
import com.github.benmanes.caffeine.cache.Cache;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -63,31 +63,11 @@ public class CachingValidationSupport extends BaseValidationSupportWrapper imple
|
|||
*/
|
||||
public CachingValidationSupport(IValidationSupport theWrap, CacheTimeouts theCacheTimeouts) {
|
||||
super(theWrap.getFhirContext(), theWrap);
|
||||
myExpandValueSetCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(theCacheTimeouts.getExpandValueSetMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(100)
|
||||
.build();
|
||||
myValidateCodeCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(theCacheTimeouts.getValidateCodeMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
myLookupCodeCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(theCacheTimeouts.getLookupCodeMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
myTranslateCodeCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(theCacheTimeouts.getTranslateCodeMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
myCache = Caffeine
|
||||
.newBuilder()
|
||||
.expireAfterWrite(theCacheTimeouts.getMiscMillis(), TimeUnit.MILLISECONDS)
|
||||
.maximumSize(5000)
|
||||
.build();
|
||||
myExpandValueSetCache = CacheFactory.build(theCacheTimeouts.getExpandValueSetMillis(), 100);
|
||||
myValidateCodeCache = CacheFactory.build(theCacheTimeouts.getValidateCodeMillis(), 5000);
|
||||
myLookupCodeCache = CacheFactory.build(theCacheTimeouts.getLookupCodeMillis(), 5000);
|
||||
myTranslateCodeCache = CacheFactory.build(theCacheTimeouts.getTranslateCodeMillis(), 5000);
|
||||
myCache = CacheFactory.build(theCacheTimeouts.getMiscMillis(), 5000);
|
||||
myNonExpiringCache = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
LinkedBlockingQueue<Runnable> executorQueue = new LinkedBlockingQueue<>(1000);
|
||||
|
|
|
@ -9,13 +9,14 @@ import ca.uhn.fhir.i18n.Msg;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.fhir.ucum.UcumService;
|
||||
import ca.uhn.fhir.sl.cache.Cache;
|
||||
import ca.uhn.fhir.sl.cache.CacheFactory;
|
||||
import ca.uhn.fhir.sl.cache.LoadingCache;
|
||||
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_50;
|
||||
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
|
@ -75,10 +76,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
|
|||
timeoutMillis = Long.parseLong(System.getProperty(Constants.TEST_SYSTEM_PROP_VALIDATION_RESOURCE_CACHES_MS));
|
||||
}
|
||||
|
||||
myFetchResourceCache = Caffeine.newBuilder()
|
||||
.expireAfterWrite(timeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.maximumSize(10000)
|
||||
.build(key -> {
|
||||
myFetchResourceCache = CacheFactory.build(timeoutMillis, 10000, key -> {
|
||||
|
||||
String fetchResourceName = key.getResourceName();
|
||||
if (myValidationSupportContext.getRootValidationSupport().getFhirContext().getVersion().getVersion() == FhirVersionEnum.DSTU2) {
|
||||
|
|
20
pom.xml
20
pom.xml
|
@ -92,6 +92,7 @@
|
|||
<module>hapi-fhir-structures-r5</module>
|
||||
<module>hapi-fhir-validation-resources-r5</module>
|
||||
<module>hapi-fhir-jpa</module>
|
||||
<module>hapi-fhir-serviceloaders</module>
|
||||
<module>hapi-fhir-storage</module>
|
||||
<module>hapi-fhir-storage-batch2</module>
|
||||
<module>hapi-fhir-storage-batch2-jobs</module>
|
||||
|
@ -824,6 +825,11 @@
|
|||
<developer>
|
||||
<id>dyoung-work</id>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>vitorpamplona</id>
|
||||
<name>Vitor Pamplona</name>
|
||||
<organization>PathCheck Foundation / EyeNetra Inc</organization>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<licenses>
|
||||
|
@ -1300,7 +1306,7 @@
|
|||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>8.0.28</version>
|
||||
<version>8.0.31</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
|
@ -2073,7 +2079,7 @@
|
|||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
|
@ -2091,6 +2097,11 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
|
@ -2183,11 +2194,6 @@
|
|||
<artifactId>maven-plugin-plugin</artifactId>
|
||||
<version>3.6.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
|
|
Loading…
Reference in New Issue