diff --git a/common/src/main/java/org/opencds/cqf/common/config/FhirServerConfig.java b/common/src/main/java/org/opencds/cqf/common/config/FhirServerConfig.java index 6d03856a5..3f03153b5 100644 --- a/common/src/main/java/org/opencds/cqf/common/config/FhirServerConfig.java +++ b/common/src/main/java/org/opencds/cqf/common/config/FhirServerConfig.java @@ -2,10 +2,22 @@ import java.lang.reflect.InvocationTargetException; import java.sql.Driver; +import java.time.Duration; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; import org.apache.commons.dbcp2.BasicDataSource; +import org.cqframework.cql.cql2elm.model.Model; +import org.cqframework.cql.elm.execution.Library; +import org.cqframework.cql.elm.execution.VersionedIdentifier; +import org.hl7.fhir.instance.model.api.IIdType; import org.opencds.cqf.cds.providers.ProviderConfiguration; import org.opencds.cqf.cql.engine.fhir.searchparam.SearchParameterResolver; +import org.opencds.cqf.cql.engine.runtime.Code; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -14,8 +26,12 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.cache.IResourceChangeEvent; +import ca.uhn.fhir.jpa.cache.IResourceChangeListener; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.ModelConfig; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; @@ -143,4 +159,43 @@ public ProviderConfiguration providerConfiguration() { public SearchParameterResolver searchParameterResolver(FhirContext fhirContext) { return new SearchParameterResolver(fhirContext); } + + @Bean + public IResourceChangeListener valueSetChangeListener(IResourceChangeListenerRegistry resourceChangeListenerRegistry, Map> terminologyCache) { + IResourceChangeListener listener = new IResourceChangeListener(){ + + @Override + public void handleInit(Collection theResourceIds) { + // Intentionally empty + } + + // TODO: Selectively clear by url. Requires a lookup on the resource + @Override + public void handleChange(IResourceChangeEvent theResourceChangeEvent) { + terminologyCache.clear(); + } + + }; + + resourceChangeListenerRegistry.registerResourceResourceChangeListener("ValueSet", SearchParameterMap.newSynchronous(), listener, 1000); + + return listener; + } + + @Bean + public Map> terminologyCache() { + Cache> cache = Caffeine.newBuilder().maximumSize(100).expireAfterAccess(Duration.ofMinutes(60)).build(); + return cache.asMap(); + } + + + @Bean(name="globalModelCache") + Map globalModelCache() { + return new ConcurrentHashMap(); + } + + @Bean(name="globalLibraryCache") + Map globalLibraryCache() { + return new ConcurrentHashMap(); + } } diff --git a/common/src/main/java/org/opencds/cqf/common/evaluation/LibraryLoader.java b/common/src/main/java/org/opencds/cqf/common/evaluation/LibraryLoader.java deleted file mode 100644 index 74f30bcea..000000000 --- a/common/src/main/java/org/opencds/cqf/common/evaluation/LibraryLoader.java +++ /dev/null @@ -1,107 +0,0 @@ -package org.opencds.cqf.common.evaluation; - -import static org.opencds.cqf.common.helpers.TranslatorHelper.errorsToString; -import static org.opencds.cqf.common.helpers.TranslatorHelper.getTranslator; -import static org.opencds.cqf.common.helpers.TranslatorHelper.readLibrary; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import javax.xml.bind.JAXBException; - -import org.cqframework.cql.cql2elm.CqlTranslator; -import org.cqframework.cql.cql2elm.CqlTranslatorException; -import org.cqframework.cql.cql2elm.CqlTranslatorOptions; -import org.cqframework.cql.cql2elm.LibraryManager; -import org.cqframework.cql.cql2elm.ModelManager; -import org.cqframework.cql.elm.execution.Library; -import org.cqframework.cql.elm.execution.VersionedIdentifier; - -public class LibraryLoader implements org.opencds.cqf.cql.engine.execution.LibraryLoader { - - private LibraryManager libraryManager; - private ModelManager modelManager; - private Map libraries = new HashMap<>(); - - // private static final Logger logger = - // LoggerFactory.getLogger(LibraryLoader.class); - - public Collection getLibraries() { - return this.libraries.values(); - } - - public LibraryManager getLibraryManager() { - return this.libraryManager; - } - - public ModelManager getModelManager() { - return this.modelManager; - } - - public LibraryLoader(LibraryManager libraryManager, ModelManager modelManager) { - this.libraryManager = libraryManager; - this.modelManager = modelManager; - } - - private Library resolveLibrary(VersionedIdentifier libraryIdentifier) { - if (libraryIdentifier == null) { - throw new IllegalArgumentException("Library identifier is null."); - } - - if (libraryIdentifier.getId() == null) { - throw new IllegalArgumentException("Library identifier id is null."); - } - - String mangledId = this.mangleIdentifer(libraryIdentifier); - - Library library = libraries.get(mangledId); - if (library == null) { - library = loadLibrary(libraryIdentifier); - libraries.put(mangledId, library); - } - - return library; - } - - private String mangleIdentifer(VersionedIdentifier libraryIdentifier) { - String id = libraryIdentifier.getId(); - String version = libraryIdentifier.getVersion(); - - return version == null ? id : id + "-" + version; - } - - private Library loadLibrary(VersionedIdentifier libraryIdentifier) { - org.hl7.elm.r1.VersionedIdentifier identifier = new org.hl7.elm.r1.VersionedIdentifier() - .withId(libraryIdentifier.getId()).withSystem(libraryIdentifier.getSystem()) - .withVersion(libraryIdentifier.getVersion()); - - ArrayList errors = new ArrayList<>(); - org.hl7.elm.r1.Library translatedLibrary = libraryManager.resolveLibrary(identifier, CqlTranslatorOptions.defaultOptions(), errors).getLibrary(); - - if (CqlTranslatorException.HasErrors(errors)) { - throw new IllegalArgumentException(errorsToString(errors)); - } - try { - CqlTranslator translator = getTranslator("", libraryManager, modelManager); - - if (translator.getErrors().size() > 0) { - throw new IllegalArgumentException(errorsToString(translator.getErrors())); - } - - return readLibrary(new ByteArrayInputStream( - CqlTranslator.convertToXml(translatedLibrary).getBytes(StandardCharsets.UTF_8))); - } catch (JAXBException e) { - throw new IllegalArgumentException(String.format("Errors occurred translating library %s%s.", - identifier.getId(), identifier.getVersion() != null ? ("-" + identifier.getVersion()) : "")); - } - } - - @Override - public Library load(VersionedIdentifier versionedIdentifier) { - return resolveLibrary(versionedIdentifier); - } -} diff --git a/common/src/main/java/org/opencds/cqf/common/providers/CacheAwareTerminologyProvider.java b/common/src/main/java/org/opencds/cqf/common/providers/CacheAwareTerminologyProvider.java new file mode 100644 index 000000000..ce4214302 --- /dev/null +++ b/common/src/main/java/org/opencds/cqf/common/providers/CacheAwareTerminologyProvider.java @@ -0,0 +1,56 @@ +package org.opencds.cqf.common.providers; + +import java.util.Map; +import org.opencds.cqf.cql.engine.runtime.Code; +import org.opencds.cqf.cql.engine.terminology.CodeSystemInfo; +import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; +import org.opencds.cqf.cql.engine.terminology.ValueSetInfo; + +public class CacheAwareTerminologyProvider implements TerminologyProvider { + + private TerminologyProvider innerTerminologyProvider; + + private Map> globalCodeCacheByUrl; + + public CacheAwareTerminologyProvider(Map> globalCodeCacheByUrl, TerminologyProvider innerTerminologyProvider) { + this.globalCodeCacheByUrl = globalCodeCacheByUrl; + this.innerTerminologyProvider = innerTerminologyProvider; + } + + @Override + public boolean in(Code code, ValueSetInfo valueSet) { + Iterable codes = this.expand(valueSet); + if (codes == null) { + return false; + } + + for (Code c : codes) { + if (c.equivalent(code)) { + return true; + } + } + + return false; + } + + @Override + public Iterable expand(ValueSetInfo valueSet) { + if (this.globalCodeCacheByUrl.containsKey(valueSet.getId())) { + return this.globalCodeCacheByUrl.get(valueSet.getId()); + } + + Iterable codes = this.innerTerminologyProvider.expand(valueSet); + + if (codes != null) { + this.globalCodeCacheByUrl.put(valueSet.getId(), codes); + } + + return codes; + } + + @Override + public Code lookup(Code code, CodeSystemInfo codeSystem) { + return this.innerTerminologyProvider.lookup(code, codeSystem); + } + +} diff --git a/common/src/main/java/org/opencds/cqf/common/providers/InMemoryLibraryResourceProvider.java b/common/src/main/java/org/opencds/cqf/common/providers/InMemoryLibraryResourceProvider.java index e788d38d0..dba86d407 100644 --- a/common/src/main/java/org/opencds/cqf/common/providers/InMemoryLibraryResourceProvider.java +++ b/common/src/main/java/org/opencds/cqf/common/providers/InMemoryLibraryResourceProvider.java @@ -7,6 +7,8 @@ import java.util.function.Function; import java.util.stream.Collectors; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; + public class InMemoryLibraryResourceProvider implements LibraryResolutionProvider { private Map libraries = new HashMap<>(); diff --git a/common/src/main/java/org/opencds/cqf/common/providers/LibrarySourceProvider.java b/common/src/main/java/org/opencds/cqf/common/providers/LibraryContentProvider.java similarity index 71% rename from common/src/main/java/org/opencds/cqf/common/providers/LibrarySourceProvider.java rename to common/src/main/java/org/opencds/cqf/common/providers/LibraryContentProvider.java index 5196d04f2..cb92fb6a0 100644 --- a/common/src/main/java/org/opencds/cqf/common/providers/LibrarySourceProvider.java +++ b/common/src/main/java/org/opencds/cqf/common/providers/LibraryContentProvider.java @@ -6,12 +6,15 @@ import org.cqframework.cql.cql2elm.FhirLibrarySourceProvider; import org.hl7.elm.r1.VersionedIdentifier; +import org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentType; + +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; /** * Created by Christopher on 1/12/2017. */ -public class LibrarySourceProvider - implements org.cqframework.cql.cql2elm.LibrarySourceProvider { +public class LibraryContentProvider + implements org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider { private FhirLibrarySourceProvider innerProvider; private LibraryResolutionProvider provider; @@ -19,7 +22,7 @@ public class LibrarySourceProvider private Function getContentType; private Function getContent; - public LibrarySourceProvider(LibraryResolutionProvider provider, + public LibraryContentProvider(LibraryResolutionProvider provider, Function> getAttachments, Function getContentType, Function getContent) { @@ -46,4 +49,14 @@ public InputStream getLibrarySource(VersionedIdentifier versionedIdentifier) { return this.innerProvider.getLibrarySource(versionedIdentifier); } + + @Override + public InputStream getLibraryContent(VersionedIdentifier libraryIdentifier, LibraryContentType libraryContentType) { + + if (libraryContentType == LibraryContentType.CQL) { + return this.getLibrarySource(libraryIdentifier); + } + + return null; + } } \ No newline at end of file diff --git a/common/src/main/java/org/opencds/cqf/common/providers/LibraryResolutionProvider.java b/common/src/main/java/org/opencds/cqf/common/providers/LibraryResolutionProvider.java deleted file mode 100644 index e278047f2..000000000 --- a/common/src/main/java/org/opencds/cqf/common/providers/LibraryResolutionProvider.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.opencds.cqf.common.providers; - -import java.util.function.Function; - -public interface LibraryResolutionProvider { - - public LibraryType resolveLibraryById(String libraryId); - - public LibraryType resolveLibraryByName(String libraryName, String libraryVersion); - - public LibraryType resolveLibraryByCanonicalUrl(String libraryUrl); - - - // Hmmm... Probably need to think through this use case a bit more. - // Should we throw an exception? Should this be a different interface? - public void update(LibraryType library); - - - // This function assumes that you're selecting from a set of libraries with the same name. - // It returns the closest matching version, or the max version if no version is specified. - static LibraryType selectFromList(Iterable libraries, String libraryVersion, Function getVersion) { - LibraryType library = null; - LibraryType maxVersion = null; - for (LibraryType l : libraries) { - String currentVersion = getVersion.apply(l); - if ((libraryVersion != null && currentVersion.equals(libraryVersion)) || - (libraryVersion == null && currentVersion == null)) - { - library = l; - } - - if (maxVersion == null || compareVersions( - getVersion.apply(maxVersion), - getVersion.apply(l) ) < 0){ - maxVersion = l; - } - } - - // If we were not given a version, return the highest found - if (libraryVersion == null && maxVersion != null) { - return maxVersion; - } - - return library; - } - - public static int compareVersions(String version1, String version2) - { - // Treat null as MAX VERSION - if (version1 == null && version2 == null) { - return 0; - } - - if (version1 != null && version2 == null) { - return -1; - } - - if (version1 == null && version2 != null) { - return 1; - } - - String[] string1Vals = version1.split("\\."); - String[] string2Vals = version2.split("\\."); - - int length = Math.max(string1Vals.length, string2Vals.length); - - for (int i = 0; i < length; i++) - { - Integer v1 = (i < string1Vals.length)?Integer.parseInt(string1Vals[i]):0; - Integer v2 = (i < string2Vals.length)?Integer.parseInt(string2Vals[i]):0; - - //Making sure Version1 bigger than version2 - if (v1 > v2) - { - return 1; - } - //Making sure Version1 smaller than version2 - else if(v1 < v2) - { - return -1; - } - } - - //Both are equal - return 0; - } -} \ No newline at end of file diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/config/FhirServerConfigDstu3.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/config/FhirServerConfigDstu3.java index 07f8d3edd..39a360697 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/config/FhirServerConfigDstu3.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/config/FhirServerConfigDstu3.java @@ -2,12 +2,22 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; +import org.cqframework.cql.cql2elm.CqlTranslatorOptions; +import org.cqframework.cql.cql2elm.model.Model; +import org.cqframework.cql.elm.execution.Library; +import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.common.config.HapiProperties; +import org.opencds.cqf.common.providers.CacheAwareTerminologyProvider; +import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; +import org.opencds.cqf.cql.evaluator.engine.model.CachingModelResolverDecorator; import org.opencds.cqf.dstu3.providers.ActivityDefinitionApplyProvider; import org.opencds.cqf.dstu3.providers.ApplyCqlOperationProvider; import org.opencds.cqf.dstu3.providers.CacheValueSetsProvider; @@ -23,16 +33,19 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.ParserOptions; +import ca.uhn.fhir.cql.dstu3.listener.ElmCacheResourceChangeListener; import ca.uhn.fhir.cql.dstu3.provider.JpaTerminologyProvider; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; @Configuration @ComponentScan(basePackages = "org.opencds.cqf.dstu3") @@ -44,17 +57,6 @@ public FhirServerConfigDstu3(DataSource myDataSource) { this.myDataSource = myDataSource; } - @Override - public FhirContext fhirContextDstu3() { - FhirContext retVal = FhirContext.forDstu3(); - - // Don't strip versions in some places - ParserOptions parserOptions = retVal.getParserOptions(); - parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.reference"); - - return retVal; - } - /** * We override the paging provider definition so that we can customize the * default/max page sizes for search results. You can set these however you @@ -126,7 +128,38 @@ public NarrativeProvider narrativeProvider() { } @Bean - public TerminologyProvider terminologyProvider(ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3 theTerminologySvc, ca.uhn.fhir.jpa.api.dao.DaoRegistry theDaoRegistry, ca.uhn.fhir.context.support.IValidationSupport theValidationSupport) { + public JpaTerminologyProvider terminologyProvider(ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3 theTerminologySvc, ca.uhn.fhir.jpa.api.dao.DaoRegistry theDaoRegistry, ca.uhn.fhir.context.support.IValidationSupport theValidationSupport) { return new JpaTerminologyProvider(theTerminologySvc, theDaoRegistry, theValidationSupport); } + + @Bean + @Primary + public TerminologyProvider terminologyProvider(Map> terminologyCache, JpaTerminologyProvider jpaTerminologyProvider) { + return new CacheAwareTerminologyProvider(terminologyCache, jpaTerminologyProvider); + } + + @Bean + public ModelResolver modelResolver() { + return new CachingModelResolverDecorator(new Dstu3FhirModelResolver()); + } + + + @Lazy + @Bean + public org.opencds.cqf.dstu3.helpers.LibraryHelper libraryHelper(Map globalModelCache, Map globalLibraryCache, CqlTranslatorOptions cqlTranslatorOptions) { + return new org.opencds.cqf.dstu3.helpers.LibraryHelper(globalModelCache, globalLibraryCache, cqlTranslatorOptions); + } + + + @Bean + public CqlTranslatorOptions cqlTranslatorOptions() { + return CqlTranslatorOptions.defaultOptions(); //.withCompatibilityLevel("1.3"); + } + + @Bean + public ElmCacheResourceChangeListener elmCacheResourceChangeListener(IResourceChangeListenerRegistry resourceChangeListenerRegistry, IFhirResourceDao libraryDao, Map globalLibraryCache) { + ElmCacheResourceChangeListener listener = new ElmCacheResourceChangeListener(libraryDao, globalLibraryCache); + resourceChangeListenerRegistry.registerResourceResourceChangeListener("Library", SearchParameterMap.newSynchronous(), listener, 1000); + return listener; + } } diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/MeasureEvaluationSeed.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/MeasureEvaluationSeed.java index 59d2189cc..44d1224f9 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/MeasureEvaluationSeed.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/MeasureEvaluationSeed.java @@ -10,14 +10,15 @@ import org.opencds.cqf.common.helpers.DateHelper; import org.opencds.cqf.common.helpers.LoggingHelper; import org.opencds.cqf.common.helpers.UsingHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.data.DataProvider; import org.opencds.cqf.cql.engine.execution.Context; import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; -import org.opencds.cqf.dstu3.helpers.LibraryHelper; + +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import ca.uhn.fhir.cql.dstu3.helper.LibraryHelper; public class MeasureEvaluationSeed { private Measure measure; @@ -27,12 +28,14 @@ public class MeasureEvaluationSeed { private LibraryResolutionProvider libraryResourceProvider; private EvaluationProviderFactory providerFactory; private DataProvider dataProvider; + private LibraryHelper libraryHelper; public MeasureEvaluationSeed(EvaluationProviderFactory providerFactory, LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { + LibraryResolutionProvider libraryResourceProvider, LibraryHelper libraryHelper) { this.providerFactory = providerFactory; this.libraryLoader = libraryLoader; this.libraryResourceProvider = libraryResourceProvider; + this.libraryHelper = libraryHelper; } public Measure getMeasure() { @@ -55,13 +58,14 @@ public void setup(Measure measure, String periodStart, String periodEnd, String String user, String pass) { this.measure = measure; - LibraryHelper.loadLibraries(measure, this.libraryLoader, this.libraryResourceProvider); + this.libraryHelper.loadLibraries(measure, libraryLoader, libraryResourceProvider); // resolve primary library - Library library = LibraryHelper.resolvePrimaryLibrary(measure, libraryLoader, this.libraryResourceProvider); + Library library = this.libraryHelper.resolvePrimaryLibrary(measure, libraryLoader, this.libraryResourceProvider); // resolve execution context context = new Context(library); + context.setExpressionCaching(true); context.setDebugMap(LoggingHelper.getDebugMap()); context.registerLibraryLoader(libraryLoader); @@ -102,7 +106,5 @@ public void setup(Measure measure, String periodStart, String periodEnd, String if (productLine != null) { context.setParameter(null, "Product Line", productLine); } - - context.setExpressionCaching(true); } } diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/ProviderFactory.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/ProviderFactory.java index ca81df565..206dfb1af 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/ProviderFactory.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/evaluation/ProviderFactory.java @@ -8,13 +8,14 @@ import org.opencds.cqf.common.retrieve.JpaFhirRetrieveProvider; import org.opencds.cqf.cql.engine.data.CompositeDataProvider; import org.opencds.cqf.cql.engine.data.DataProvider; -import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver; import org.opencds.cqf.cql.engine.fhir.searchparam.SearchParameterResolver; import org.opencds.cqf.cql.engine.fhir.terminology.Dstu3FhirTerminologyProvider; +import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; import org.springframework.stereotype.Component; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; @@ -29,12 +30,15 @@ public class ProviderFactory implements EvaluationProviderFactory { FhirContext fhirContext; ISearchParamRegistry searchParamRegistry; + ModelResolver modelResolver; + @Inject public ProviderFactory(FhirContext fhirContext, DaoRegistry registry, - TerminologyProvider defaultTerminologyProvider) { + TerminologyProvider defaultTerminologyProvider, ModelResolver modelResolver) { this.defaultTerminologyProvider = defaultTerminologyProvider; this.registry = registry; this.fhirContext = fhirContext; + this.modelResolver = modelResolver; } public DataProvider createDataProvider(String model, String version) { @@ -48,7 +52,6 @@ public DataProvider createDataProvider(String model, String version, String url, public DataProvider createDataProvider(String model, String version, TerminologyProvider terminologyProvider) { if (model.equals("FHIR") && version.startsWith("3")) { - Dstu3FhirModelResolver modelResolver = new Dstu3FhirModelResolver(); JpaFhirRetrieveProvider retrieveProvider = new JpaFhirRetrieveProvider(this.registry, new SearchParameterResolver(this.fhirContext)); retrieveProvider.setTerminologyProvider(terminologyProvider); @@ -64,7 +67,7 @@ public DataProvider createDataProvider(String model, String version, Terminology public TerminologyProvider createTerminologyProvider(String model, String version, String url, String user, String pass) { if (url != null && !url.isEmpty()) { - IGenericClient client = ClientHelper.getClient(FhirContext.forDstu3(), url, user, pass); + IGenericClient client = ClientHelper.getClient(FhirContext.forCached(FhirVersionEnum.DSTU3), url, user, pass); if (url.contains("apelon.com")) { return new Dstu3ApelonFhirTerminologyProvider(client); } diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/helpers/LibraryHelper.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/helpers/LibraryHelper.java index be38ecc9d..204c905a9 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/helpers/LibraryHelper.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/helpers/LibraryHelper.java @@ -1,177 +1,111 @@ package org.opencds.cqf.dstu3.helpers; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; +import org.cqframework.cql.cql2elm.CqlTranslatorOptions; import org.cqframework.cql.cql2elm.LibraryManager; import org.cqframework.cql.cql2elm.ModelManager; +import org.cqframework.cql.cql2elm.model.Model; import org.cqframework.cql.elm.execution.Library; -import org.cqframework.cql.elm.execution.VersionedIdentifier; -import org.hl7.fhir.dstu3.model.*; -import org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType; -import org.opencds.cqf.common.evaluation.LibraryLoader; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; -import org.opencds.cqf.common.providers.LibrarySourceProvider; - -/** - * Created by Christopher on 1/11/2017. - */ -public class LibraryHelper { - - public static LibraryLoader createLibraryLoader( - LibraryResolutionProvider provider) { - ModelManager modelManager = new ModelManager(); - LibraryManager libraryManager = new LibraryManager(modelManager); - libraryManager.getLibrarySourceLoader().clearProviders(); - - libraryManager.getLibrarySourceLoader().registerProvider( - new LibrarySourceProvider( - provider, x -> x.getContent(), x -> x.getContentType(), x -> x.getData())); - - return new LibraryLoader(libraryManager, modelManager); +import org.hl7.elm.r1.VersionedIdentifier; +import org.hl7.fhir.dstu3.model.Attachment; +import org.hl7.fhir.dstu3.model.PlanDefinition; +import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager; +import org.opencds.cqf.cql.evaluator.engine.execution.CacheAwareLibraryLoaderDecorator; +import org.opencds.cqf.cql.evaluator.engine.execution.TranslatingLibraryLoader; + +import ca.uhn.fhir.cql.common.provider.LibraryContentProvider; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; + +public class LibraryHelper extends ca.uhn.fhir.cql.dstu3.helper.LibraryHelper { + + + protected Map modelCache; + protected Map libraryCache; + protected CqlTranslatorOptions translatorOptions; + + public LibraryHelper(Map modelCache, + Map libraryCache, + CqlTranslatorOptions translatorOptions) { + super(modelCache, libraryCache, translatorOptions); + this.modelCache = modelCache; + this.libraryCache = libraryCache; + this.translatorOptions = translatorOptions; } - public static List loadLibraries(Measure measure, - org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { - List libraries = new ArrayList(); - - // load libraries - //TODO: if there's a bad measure argument, this blows up for an obscure error - for (Reference ref : measure.getLibrary()) { - // if library is contained in measure, load it into server - if (ref.getReferenceElement().getIdPart().startsWith("#")) { - for (Resource resource : measure.getContained()) { - if (resource instanceof org.hl7.fhir.dstu3.model.Library && resource.getIdElement().getIdPart() - .equals(ref.getReferenceElement().getIdPart().substring(1))) { - libraryResourceProvider.update((org.hl7.fhir.dstu3.model.Library) resource); - } - } - } - - // We just loaded it into the server so we can access it by Id - String id = ref.getReferenceElement().getIdPart(); - if (id.startsWith("#")) { - id = id.substring(1); - } - org.hl7.fhir.dstu3.model.Library library = libraryResourceProvider.resolveLibraryById(id); - if (library != null && isLogicLibrary(library)) { - libraries.add(libraryLoader - .load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()))); - } - } - - if (libraries.isEmpty()) { - throw new IllegalArgumentException(String - .format("Could not load library source for libraries referenced in Measure/%s.", measure.getId())); - } - - VersionedIdentifier primaryLibraryId = libraries.get(0).getIdentifier(); - org.hl7.fhir.dstu3.model.Library primaryLibrary = libraryResourceProvider.resolveLibraryByName(primaryLibraryId.getId(), primaryLibraryId.getVersion()); - for (RelatedArtifact artifact : primaryLibrary.getRelatedArtifact()) { - if (artifact.hasType() && artifact.getType().equals(RelatedArtifactType.DEPENDSON) && artifact.hasResource() && artifact.getResource().hasReference()) { - if (artifact.getResource().getReferenceElement().getResourceType().equals("Library")) { - org.hl7.fhir.dstu3.model.Library library = libraryResourceProvider.resolveLibraryById(artifact.getResource().getReferenceElement().getIdPart()); - - if (library != null && isLogicLibrary(library)) { - libraries.add( - libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())) - ); - } - } - } - } - - return libraries; + public ModelManager getModelManager() { + return new CacheAwareModelManager(this.modelCache); } - private static boolean isLogicLibrary(org.hl7.fhir.dstu3.model.Library library) { - if (library == null) { - return false; + public LibraryManager getLibraryManager(LibraryResolutionProvider provider) { + List contentProviders = Collections.singletonList(new LibraryContentProvider( + provider, x -> x.getContent(), x -> x.getContentType(), x -> x.getData())); + + LibraryManager libraryManager = new LibraryManager(this.getModelManager()); + for (org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider contentProvider : contentProviders) { + libraryManager.getLibrarySourceLoader().registerProvider(contentProvider); } - if (!library.hasType()) { - // If no type is specified, assume it is a logic library based on whether there is a CQL content element. - if (library.hasContent()) { - for (Attachment a : library.getContent()) { - if (a.hasContentType() && (a.getContentType().equals("text/cql") - || a.getContentType().equals("application/elm+xml") - || a.getContentType().equals("application/elm+json"))) { - return true; - } - } - } - return false; - } - - if (!library.getType().hasCoding()) { - return false; - } - - for (Coding c : library.getType().getCoding()) { - if (c.hasSystem() && c.getSystem().equals("http://hl7.org/fhir/library-type") - && c.hasCode() && c.getCode().equals("logic-library")) { - return true; - } - } + return libraryManager; + } - return false; - } - - public static Library resolveLibraryById(String libraryId, + public Library resolvePrimaryLibrary(PlanDefinition planDefinition, org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, LibraryResolutionProvider libraryResourceProvider) { - // Library library = null; - - org.hl7.fhir.dstu3.model.Library fhirLibrary = libraryResourceProvider.resolveLibraryById(libraryId); - return libraryLoader - .load(new VersionedIdentifier().withId(fhirLibrary.getName()).withVersion(fhirLibrary.getVersion())); - - // for (Library l : libraryLoader.getLibraries()) { - // VersionedIdentifier vid = l.getIdentifier(); - // if (vid.getId().equals(fhirLibrary.getName()) && - // LibraryResourceHelper.compareVersions(fhirLibrary.getVersion(), - // vid.getVersion()) == 0) { - // library = l; - // break; - // } - // } - - // if (library == null) { - - // } - - // return library; - } - - public static Library resolvePrimaryLibrary(Measure measure, - org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { - // default is the first library reference - String id = measure.getLibraryFirstRep().getReferenceElement().getIdPart(); + String id = planDefinition.getLibrary().get(0).getReference(); Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider); if (library == null) { - throw new IllegalArgumentException(String.format("Could not resolve primary library for Measure/%s.", - measure.getIdElement().getIdPart())); + throw new IllegalArgumentException(String.format("Could not resolve primary library for PlanDefinition/%s", + planDefinition.getIdElement().getIdPart())); } return library; } - public static Library resolvePrimaryLibrary(PlanDefinition planDefinition, org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { - String id = planDefinition.getLibraryFirstRep().getReferenceElement().getIdPart(); - Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider); + @Override + public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader( + LibraryResolutionProvider provider) { + ModelManager modelManager = new CacheAwareModelManager(this.modelCache); + LibraryManager libraryManager = new LibraryManager(modelManager); + libraryManager.getLibrarySourceLoader().clearProviders(); + List contentProviders = Collections + .singletonList(new LibraryContentProvider(provider, + x -> x.getContent(), x -> x.getContentType(), x -> x.getData())); - if (library == null) { - throw new IllegalArgumentException(String.format("Could not resolve primary library for PlanDefinition/%s", - planDefinition.getIdElement().getIdPart())); - } + TranslatingLibraryLoader translatingLibraryLoader = new TranslatingLibraryLoader(modelManager, contentProviders, + translatorOptions); - return library; + return new CacheAwareLibraryLoaderDecorator(translatingLibraryLoader, libraryCache) { + @Override + protected Boolean translatorOptionsMatch(Library library) { + return true; + } + }; } + + // @Override + // public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader( + // org.cqframework.cql.cql2elm.LibrarySourceProvider provider) { + // ModelManager modelManager = new CacheAwareModelManager(this.modelCache); + // LibraryManager libraryManager = new LibraryManager(modelManager); + // libraryManager.getLibrarySourceLoader().clearProviders(); + + // libraryManager.getLibrarySourceLoader().registerProvider(provider); + + // TranslatingLibraryLoader translatingLibraryLoader = new TranslatingLibraryLoader(modelManager, null, + // translatorOptions); + + // return new CacheAwareLibraryLoaderDecorator(translatingLibraryLoader, libraryCache) { + // @Override + // protected Boolean translatorOptionsMatch(Library library) { + // return true; + // } + // }; + // } } diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/ActivityDefinitionApplyProvider.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/ActivityDefinitionApplyProvider.java index df82c6fb5..6d1b30b15 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/ActivityDefinitionApplyProvider.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/ActivityDefinitionApplyProvider.java @@ -24,7 +24,6 @@ import org.hl7.fhir.dstu3.model.SupplyRequest; import org.hl7.fhir.exceptions.FHIRException; import org.opencds.cqf.common.exceptions.ActivityDefinitionApplyException; -import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.dstu3.helpers.Helper; import org.springframework.stereotype.Component; @@ -48,8 +47,8 @@ public class ActivityDefinitionApplyProvider { @Inject public ActivityDefinitionApplyProvider(FhirContext fhirContext, CqlExecutionProvider executionProvider, - IFhirResourceDao activityDefinitionDao) { - this.modelResolver = new Dstu3FhirModelResolver(); + IFhirResourceDao activityDefinitionDao, ModelResolver modelResolver) { + this.modelResolver = modelResolver; this.executionProvider = executionProvider; this.activityDefinitionDao = activityDefinitionDao; diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/CqlExecutionProvider.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/CqlExecutionProvider.java index 111d887ed..d2dabce0a 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/CqlExecutionProvider.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/CqlExecutionProvider.java @@ -27,21 +27,21 @@ import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.Type; import org.opencds.cqf.common.evaluation.EvaluationProviderFactory; -import org.opencds.cqf.common.evaluation.LibraryLoader; import org.opencds.cqf.common.helpers.DateHelper; import org.opencds.cqf.common.helpers.LoggingHelper; import org.opencds.cqf.common.helpers.TranslatorHelper; import org.opencds.cqf.common.helpers.UsingHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.data.DataProvider; import org.opencds.cqf.cql.engine.execution.Context; +import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; import org.opencds.cqf.dstu3.helpers.FhirMeasureBundler; -import org.opencds.cqf.dstu3.helpers.LibraryHelper; import org.springframework.stereotype.Component; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import org.opencds.cqf.dstu3.helpers.LibraryHelper; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -52,12 +52,14 @@ public class CqlExecutionProvider { private EvaluationProviderFactory providerFactory; private LibraryResolutionProvider libraryResolutionProvider; + private LibraryHelper libraryHelper; @Inject public CqlExecutionProvider(LibraryResolutionProvider libraryResolutionProvider, - EvaluationProviderFactory providerFactory) { + EvaluationProviderFactory providerFactory, LibraryHelper libraryHelper) { this.providerFactory = providerFactory; this.libraryResolutionProvider = libraryResolutionProvider; + this.libraryHelper = libraryHelper; } private LibraryResolutionProvider getLibraryResourceProvider() { @@ -179,10 +181,10 @@ public Object evaluateInContext(DomainResource instance, String cql, String pati // buildIncludes(libraries), instance.fhirType(), instance.fhirType(), // instance.fhirType(), cql); - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(this.libraryResolutionProvider); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.libraryResolutionProvider); org.cqframework.cql.elm.execution.Library library = TranslatorHelper.translateLibrary(source, - libraryLoader.getLibraryManager(), libraryLoader.getModelManager()); + this.libraryHelper.getLibraryManager(this.libraryResolutionProvider), this.libraryHelper.getModelManager()); Context context = new Context(library); context.setDebugMap(LoggingHelper.getDebugMap()); context.setParameter(null, instance.fhirType(), instance); @@ -215,13 +217,13 @@ public Bundle evaluate(@OperationParam(name = "code") String code, CqlTranslator translator; FhirMeasureBundler bundler = new FhirMeasureBundler(); - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); List results = new ArrayList<>(); try { - translator = TranslatorHelper.getTranslator(code, libraryLoader.getLibraryManager(), - libraryLoader.getModelManager()); + translator = TranslatorHelper.getTranslator(code, this.libraryHelper.getLibraryManager(this.libraryResolutionProvider), + this.libraryHelper.getModelManager()); if (translator.getErrors().size() > 0) { for (CqlTranslatorException cte : translator.getErrors()) { diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/DataRequirementsProvider.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/DataRequirementsProvider.java index 4853a5176..47d7d0507 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/DataRequirementsProvider.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/DataRequirementsProvider.java @@ -14,6 +14,8 @@ import java.util.Map.Entry; import java.util.stream.Collectors; +import javax.inject.Inject; + import com.google.common.base.Strings; import com.vladsch.flexmark.ext.autolink.AutolinkExtension; import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension; @@ -60,19 +62,28 @@ import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.dstu3.model.Type; import org.opencds.cqf.common.helpers.TranslatorHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.execution.LibraryLoader; -import org.opencds.cqf.dstu3.helpers.LibraryHelper; import org.opencds.cqf.tooling.measure.stu3.CodeTerminologyRef; import org.opencds.cqf.tooling.measure.stu3.CqfMeasure; import org.opencds.cqf.tooling.measure.stu3.TerminologyRef; import org.opencds.cqf.tooling.measure.stu3.TerminologyRef.TerminologyRefType; import org.springframework.stereotype.Component; + +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import ca.uhn.fhir.cql.dstu3.helper.LibraryHelper; + import org.opencds.cqf.tooling.measure.stu3.VersionedTerminologyRef; @Component public class DataRequirementsProvider { + private LibraryHelper libraryHelper; + + @Inject + public DataRequirementsProvider(LibraryHelper libraryHelper) { + this.libraryHelper = libraryHelper; + } + // For creating the CQF measure we need to: // 1. Find the Primary Library Resource // 2. Load the Primary Library as ELM. This will recursively load the dependent @@ -91,8 +102,8 @@ public CqfMeasure createCqfMeasure(Measure measure, private Map> createLibraryMap(Measure measure, LibraryResolutionProvider libraryResourceProvider) { - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(libraryResourceProvider); - List libraries = LibraryHelper.loadLibraries(measure, libraryLoader, libraryResourceProvider); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(libraryResourceProvider); + List libraries = this.libraryHelper.loadLibraries(measure, libraryLoader, libraryResourceProvider); Map> libraryMap = new HashMap<>(); for (Library library : libraries) { diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/HQMFProvider.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/HQMFProvider.java index 948955d04..a7e5b0abb 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/HQMFProvider.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/HQMFProvider.java @@ -1,20 +1,11 @@ package org.opencds.cqf.dstu3.providers; -import java.io.File; -import java.io.FileReader; -import java.io.PrintWriter; -import java.io.Reader; import java.io.StringWriter; -import java.net.URI; -import java.nio.file.Path; -import java.nio.file.Paths; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; @@ -30,19 +21,12 @@ import org.hl7.fhir.dstu3.model.Identifier; import org.hl7.fhir.dstu3.model.Library; import org.hl7.fhir.dstu3.model.MarkdownType; -import org.hl7.fhir.dstu3.model.Measure; import org.hl7.fhir.dstu3.model.Measure.MeasureGroupComponent; import org.hl7.fhir.dstu3.model.Measure.MeasureGroupPopulationComponent; import org.hl7.fhir.dstu3.model.Measure.MeasureSupplementalDataComponent; -import org.hl7.fhir.dstu3.model.Narrative; import org.hl7.fhir.dstu3.model.Period; import org.hl7.fhir.dstu3.model.RelatedArtifact; import org.hl7.fhir.dstu3.model.RelatedArtifact.RelatedArtifactType; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.jsoup.Jsoup; -import org.opencds.cqf.common.providers.InMemoryLibraryResourceProvider; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; -import org.opencds.cqf.tooling.library.stu3.NarrativeProvider; import org.opencds.cqf.tooling.measure.stu3.CodeTerminologyRef; import org.opencds.cqf.tooling.measure.stu3.CqfMeasure; import org.opencds.cqf.tooling.measure.stu3.TerminologyRef; @@ -50,9 +34,6 @@ import org.springframework.stereotype.Component; import org.w3c.dom.Document; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.parser.IParser; - @Component public class HQMFProvider { @@ -602,142 +583,4 @@ private String writeDocument(Document d) { return null; } } - - // private boolean validateHQMF(String xml) { - // try { - // return this.validateXML(this.loadHQMFSchema(), xml); - // } - // catch (SAXException e) { - // return false; - // } - // } - - // private boolean validateXML(Schema schema, String xml){ - // try { - // Validator validator = schema.newValidator(); - // validator.validate(new StreamSource(new StringReader(xml))); - // } catch (IOException | SAXException e) { - // System.out.println("Exception: " + e.getMessage()); - // return false; - // } - // return true; - // } - - // private Schema loadHQMFSchema() throws SAXException { - // SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - // URL hqmfSchema = ClassLoader.getSystemClassLoader().getResource("hqmf/schemas/EMeasure_N1.xsd"); - // return factory.newSchema(hqmfSchema); - // } - - // args[0] == relative path to json measure -> i.e. measure/measure-demo.json (optional) - // args[1] == path to resource output -> i.e. library/library-demo.json(optional) - // args[2] == path to hqmf output -> i.e. hqmf.xml(optional) - // args[3] == path to narrative output -> i.e. output.html(optional) - public static void main(String[] args) { - - try { - List strings = Arrays.asList( - "hqmf/examples/input/measure-ann.json", - "hqmf/examples/input/library-common.json", - "hqmf/examples/input/library-ann.json" - ); - - List paths = strings.stream().map(x -> Paths.get(toUri(x))).collect(Collectors.toList()); - - // Path pathToLibrary = Paths.get(HQMFProvider.class.getClassLoader().getResource("narratives/examples/library/CMS146.json").toURI()); - Path pathToOutput = Paths.get("src/main/resources/hqmf/hqmf.xml").toAbsolutePath(); - Path pathToNarrativeOutput = Paths.get("src/main/resources/narratives/output.html").toAbsolutePath(); - - Path pathToProp = Paths.get( - NarrativeProvider.class.getClassLoader().getResource("narratives/narrative.properties").toURI()); - - if (args.length >= 4) { - pathToNarrativeOutput = Paths.get(new URI(args[3])); - } - - if (args.length >= 3) { - pathToOutput = Paths.get(new URI(args[2])); - } - - // if (args.length >= 2) { - // pathToLibrary = Paths.get(new URI(args[1])); - // } - - // if (args.length >= 1) { - // pathToMeasure = Paths.get(new URI(args[0])); - // } - - HQMFProvider provider = new HQMFProvider(); - DataRequirementsProvider dataRequirementsProvider = new DataRequirementsProvider(); - NarrativeProvider narrativeProvider = new NarrativeProvider(pathToProp.toUri().toString());; - - FhirContext context = FhirContext.forDstu3(); - - //IParser parser = pathToMeasure.toString().endsWith("json") ? context.newJsonParser() : context.newXmlParser(); - - IParser parser = context.newJsonParser(); - - List resources = paths.stream() - .map(x -> toReader(x)) - .filter(x -> x != null) - .map(x -> parser.parseResource(x)) - .collect(Collectors.toList()); - - Measure measure = (Measure)resources.stream().filter(x -> (x instanceof Measure)).findFirst().get(); - List libraries = resources.stream().filter(x -> (x instanceof Library)).map(x -> (Library)x).collect(Collectors.toList()); - - LibraryResolutionProvider lrp = - new InMemoryLibraryResourceProvider(libraries, x -> x.getIdElement().getIdPart(), x -> x.getName(), x -> x.getVersion()); - - CqfMeasure cqfMeasure = dataRequirementsProvider.createCqfMeasure(measure, lrp); - - String result = provider.generateHQMF(cqfMeasure); - - PrintWriter writer = new PrintWriter(new File(pathToOutput.toString()), "UTF-8"); - writer.println(result); - writer.println(); - writer.close(); - - - - Narrative narrative = narrativeProvider.getNarrative(context, cqfMeasure); - String narrativeContent = narrative.getDivAsString(); - - Path pathToHTML = Paths.get(toUri("narratives/templates/hqmf.html")); - org.jsoup.nodes.Document htmlDoc = Jsoup.parse(pathToHTML.toFile(), "UTF-8"); - - htmlDoc.title(measure.getName()); - htmlDoc.body().html(narrativeContent); - - writer = new PrintWriter(new File(pathToNarrativeOutput.toString()), "UTF-8"); - writer.write(htmlDoc.outerHtml()); - writer.close(); - } - catch (Exception e) - { - e.printStackTrace(); - return; - } - } - - public static Reader toReader(Path p) { - try { - return new FileReader(p.toFile()); - } - catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - - public static URI toUri(String s) { - try { - return HQMFProvider.class.getClassLoader().getResource(s).toURI(); - } - catch (Exception e) { - e.printStackTrace(); - return null; - } - } } diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/LibraryOperationsProvider.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/LibraryOperationsProvider.java index 830af0706..4d67121d4 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/LibraryOperationsProvider.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/LibraryOperationsProvider.java @@ -17,11 +17,11 @@ import org.hl7.fhir.dstu3.model.Parameters; import org.hl7.fhir.dstu3.model.StringType; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; -import org.opencds.cqf.common.providers.LibrarySourceProvider; +import org.opencds.cqf.common.providers.LibraryContentProvider; import org.opencds.cqf.tooling.library.stu3.NarrativeProvider; import org.springframework.stereotype.Component; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; import ca.uhn.fhir.jpa.rp.dstu3.LibraryResourceProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.annotation.IdParam; @@ -35,7 +35,7 @@ import ca.uhn.fhir.rest.param.UriParam; @Component -public class LibraryOperationsProvider implements org.opencds.cqf.common.providers.LibraryResolutionProvider { +public class LibraryOperationsProvider implements LibraryResolutionProvider { private NarrativeProvider narrativeProvider; private DataRequirementsProvider dataRequirementsProvider; @@ -43,9 +43,9 @@ public class LibraryOperationsProvider implements org.opencds.cqf.common.provide @Inject public LibraryOperationsProvider(LibraryResourceProvider libraryResourceProvider, - NarrativeProvider narrativeProvider) { + NarrativeProvider narrativeProvider, DataRequirementsProvider dataRequirementsProvider) { this.narrativeProvider = narrativeProvider; - this.dataRequirementsProvider = new DataRequirementsProvider(); + this.dataRequirementsProvider = dataRequirementsProvider; this.libraryResourceProvider = libraryResourceProvider; } @@ -61,11 +61,11 @@ private LibraryManager getLibraryManager(ModelManager modelManager) { return libraryManager; } - private LibrarySourceProvider librarySourceProvider; + private LibraryContentProvider librarySourceProvider; - private LibrarySourceProvider getLibrarySourceProvider() { + private LibraryContentProvider getLibrarySourceProvider() { if (librarySourceProvider == null) { - librarySourceProvider = new LibrarySourceProvider(this.getLibraryResolutionProvider(), + librarySourceProvider = new LibraryContentProvider(this.getLibraryResolutionProvider(), x -> x.getContent(), x -> x.getContentType(), x -> x.getData()); } return librarySourceProvider; diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/MeasureOperationsProvider.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/MeasureOperationsProvider.java index 533739bd4..9ef83d5d2 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/MeasureOperationsProvider.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/MeasureOperationsProvider.java @@ -42,7 +42,6 @@ import org.opencds.cqf.common.config.HapiProperties; import org.opencds.cqf.common.evaluation.EvaluationProviderFactory; import org.opencds.cqf.common.helpers.DateHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.data.DataProvider; import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.dstu3.evaluation.MeasureEvaluation; @@ -57,6 +56,7 @@ import org.springframework.stereotype.Component; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; @@ -83,6 +83,7 @@ public class MeasureOperationsProvider { private MeasureResourceProvider measureResourceProvider; private DaoRegistry registry; private EvaluationProviderFactory factory; + private LibraryHelper libraryHelper; private String serverAddress = HapiProperties.getServerAddress(); @@ -92,15 +93,16 @@ public class MeasureOperationsProvider { public MeasureOperationsProvider(DaoRegistry registry, EvaluationProviderFactory factory, NarrativeProvider narrativeProvider, HQMFProvider hqmfProvider, LibraryResolutionProvider libraryResolutionProvider, - MeasureResourceProvider measureResourceProvider) { + MeasureResourceProvider measureResourceProvider, DataRequirementsProvider dataRequirementsProvider, LibraryHelper libraryHelper) { this.registry = registry; this.factory = factory; this.libraryResolutionProvider = libraryResolutionProvider; this.narrativeProvider = narrativeProvider; this.hqmfProvider = hqmfProvider; - this.dataRequirementsProvider = new DataRequirementsProvider(); + this.dataRequirementsProvider = dataRequirementsProvider; this.measureResourceProvider = measureResourceProvider; + this.libraryHelper = libraryHelper; } @Operation(name = "$hqmf", idempotent = true, type = Measure.class) @@ -186,9 +188,9 @@ public MeasureReport evaluateMeasure(@IdParam IdType theId, @OperationParam(name = "lastReceivedOn") String lastReceivedOn, @OperationParam(name = "source") String source, @OperationParam(name = "user") String user, @OperationParam(name = "pass") String pass) throws InternalErrorException, FHIRException { - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(this.libraryResolutionProvider); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.libraryResolutionProvider); MeasureEvaluationSeed seed = new MeasureEvaluationSeed(this.factory, libraryLoader, - this.libraryResolutionProvider); + this.libraryResolutionProvider, this.libraryHelper); Measure measure = this.measureResourceProvider.getDao().read(theId); if (measure == null) { diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/PlanDefinitionApplyProvider.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/PlanDefinitionApplyProvider.java index 9b789c83c..2aa9e0392 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/PlanDefinitionApplyProvider.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/providers/PlanDefinitionApplyProvider.java @@ -23,7 +23,6 @@ import org.hl7.fhir.exceptions.FHIRException; import org.opencds.cqf.common.config.HapiProperties; import org.opencds.cqf.cql.engine.execution.Context; -import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.dstu3.builders.AttachmentBuilder; @@ -65,9 +64,9 @@ public class PlanDefinitionApplyProvider { public PlanDefinitionApplyProvider(FhirContext fhirContext, ActivityDefinitionApplyProvider activityDefinitionApplyProvider, IFhirResourceDao planDefinitionDao, - IFhirResourceDao activityDefinitionDao, CqlExecutionProvider executionProvider) { + IFhirResourceDao activityDefinitionDao, CqlExecutionProvider executionProvider, ModelResolver modelResolver) { this.executionProvider = executionProvider; - this.modelResolver = new Dstu3FhirModelResolver(); + this.modelResolver = modelResolver; this.activityDefinitionApplyProvider = activityDefinitionApplyProvider; this.planDefinitionDao = planDefinitionDao; this.activityDefinitionDao = activityDefinitionDao; diff --git a/dstu3/src/main/java/org/opencds/cqf/dstu3/servlet/CdsHooksServlet.java b/dstu3/src/main/java/org/opencds/cqf/dstu3/servlet/CdsHooksServlet.java index aef1285d8..0a7395d6a 100644 --- a/dstu3/src/main/java/org/opencds/cqf/dstu3/servlet/CdsHooksServlet.java +++ b/dstu3/src/main/java/org/opencds/cqf/dstu3/servlet/CdsHooksServlet.java @@ -38,16 +38,14 @@ import org.opencds.cqf.common.config.HapiProperties; import org.opencds.cqf.common.exceptions.InvalidRequestException; import org.opencds.cqf.common.helpers.LoggingHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.common.retrieve.JpaFhirRetrieveProvider; import org.opencds.cqf.cql.engine.data.CompositeDataProvider; -import org.opencds.cqf.cql.engine.debug.DebugMap; import org.opencds.cqf.cql.engine.exception.CqlException; import org.opencds.cqf.cql.engine.execution.Context; import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.cql.engine.fhir.exception.DataProviderException; -import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver; -import org.opencds.cqf.dstu3.helpers.LibraryHelper; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; import org.opencds.cqf.dstu3.providers.PlanDefinitionApplyProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,9 +53,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cql.dstu3.provider.JpaTerminologyProvider; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import org.opencds.cqf.dstu3.helpers.LibraryHelper; + @WebServlet(name = "cds-services") public class CdsHooksServlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -70,10 +70,14 @@ public class CdsHooksServlet extends HttpServlet { private JpaFhirRetrieveProvider fhirRetrieveProvider; - private JpaTerminologyProvider jpaTerminologyProvider; + private TerminologyProvider serverTerminologyProvider; private ProviderConfiguration providerConfiguration; + private ModelResolver modelResolver; + + private LibraryHelper libraryHelper; + @SuppressWarnings("unchecked") @Override public void init() { @@ -85,7 +89,9 @@ public void init() { this.planDefinitionProvider = appCtx.getBean(PlanDefinitionApplyProvider.class); this.libraryResolutionProvider = (LibraryResolutionProvider)appCtx.getBean(LibraryResolutionProvider.class); this.fhirRetrieveProvider = appCtx.getBean(JpaFhirRetrieveProvider.class); - this.jpaTerminologyProvider = appCtx.getBean(JpaTerminologyProvider.class); + this.serverTerminologyProvider = appCtx.getBean(TerminologyProvider.class); + this.modelResolver = appCtx.getBean(ModelResolver.class); + this.libraryHelper = appCtx.getBean(LibraryHelper.class); } protected ProviderConfiguration getProviderConfiguration() { @@ -167,11 +173,10 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) if(!planDefinitionHookMatchesRequestHook.get()){ throw new ServletException("ERROR: Request hook does not match the service called."); } - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(libraryResolutionProvider); - Library library = LibraryHelper.resolvePrimaryLibrary(planDefinition, libraryLoader, libraryResolutionProvider); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(libraryResolutionProvider); + Library library = this.libraryHelper.resolvePrimaryLibrary(planDefinition, libraryLoader, libraryResolutionProvider); - Dstu3FhirModelResolver resolver = new Dstu3FhirModelResolver(); - CompositeDataProvider provider = new CompositeDataProvider(resolver, fhirRetrieveProvider); + CompositeDataProvider provider = new CompositeDataProvider(this.modelResolver, fhirRetrieveProvider); Context context = new Context(library); @@ -179,20 +184,20 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) context.registerDataProvider("http://hl7.org/fhir", provider); // TODO make sure tooling handles remote // provider case - context.registerTerminologyProvider(jpaTerminologyProvider); + context.registerTerminologyProvider(serverTerminologyProvider); context.registerLibraryLoader(libraryLoader); context.setContextValue("Patient", hook.getRequest().getContext().getPatientId().replace("Patient/", "")); context.setExpressionCaching(true); - EvaluationContext evaluationContext = new Stu3EvaluationContext(hook, version, FhirContext.forDstu3().newRestfulGenericClient(baseUrl), - jpaTerminologyProvider, context, library, - planDefinition, this.getProviderConfiguration()); + EvaluationContext evaluationContext = new Stu3EvaluationContext(hook, version, FhirContext.forCached(FhirVersionEnum.DSTU3).newRestfulGenericClient(baseUrl), + serverTerminologyProvider, context, library, + planDefinition, this.getProviderConfiguration(), this.modelResolver); this.setAccessControlHeaders(response); response.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()); - Stu3HookEvaluator evaluator = new Stu3HookEvaluator(); + Stu3HookEvaluator evaluator = new Stu3HookEvaluator(this.modelResolver); String jsonResponse = toJsonResponse(evaluator.evaluate(evaluationContext)); @@ -289,7 +294,7 @@ private JsonObject getService(String service) { private JsonObject getServices() { DiscoveryResolutionStu3 discoveryResolutionStu3 = new DiscoveryResolutionStu3( - FhirContext.forDstu3().newRestfulGenericClient(HapiProperties.getServerAddress())); + FhirContext.forCached(FhirVersionEnum.DSTU3).newRestfulGenericClient(HapiProperties.getServerAddress())); discoveryResolutionStu3.setMaxUriLength(this.getProviderConfiguration().getMaxUriLength()); return discoveryResolutionStu3.resolve() .getAsJson(); diff --git a/dstu3/src/main/resources/hapi.properties b/dstu3/src/main/resources/hapi.properties index 7dc78bd06..b07ffe475 100644 --- a/dstu3/src/main/resources/hapi.properties +++ b/dstu3/src/main/resources/hapi.properties @@ -34,10 +34,6 @@ logger.name=fhirtest.access logger.format=Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}] logger.error_format=ERROR - ${requestVerb} ${requestUrl} logger.log_exceptions=true -datasource.driver=org.h2.Driver -datasource.url=jdbc:h2:file:./target/database/h2/dstu3 -datasource.username= -datasource.password= server.name=Local Tester server.id=home test.port= @@ -74,7 +70,27 @@ fhirpath_interceptor.enabled=false ################################################### # Database Settings ################################################### -hibernate.dialect=org.hibernate.dialect.H2Dialect +# datasource.driver=org.h2.Driver +# datasource.url=jdbc:h2:file:./target/database/h2/dstu3 +# hibernate.dialect=org.hibernate.dialect.H2Dialect +# datasource.username= +# datasource.password= + + +datasource.driver=org.apache.derby.jdbc.EmbeddedDriver +datasource.url=jdbc:derby:target/derby;create=true +hibernate.dialect=org.hibernate.dialect.DerbyTenSevenDialect +spring.jpa.hibernate.ddl-auto=update +datasource.username= +datasource.password= + + +# datasource.driver=org.postgresql.Driver +# datasource.url=jdbc:postgresql://localhost:5432/hapi +# hibernate.dialect=org.hibernate.dialect.PostgreSQL95Dialect +# datasource.username=admin +# datasource.password=admin + hibernate.search.enabled=true hibernate.search.backend.type=lucene hibernate.search.backend.analysis.configurer=ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer @@ -163,9 +179,9 @@ oauth.enabled=false oauth.securityCors=true oauth.securityUrl=http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris oauth.securityExtAuthUrl=authorize -oauth.securityExtAuthValueUri=http://launch.smarthealthit.org/v/r4/auth/authorize +oauth.securityExtAuthValueUri=http://launch.smarthealthit.org/v/dstu3/auth/authorize oauth.securityExtTokenUrl=token -oauth.securityExtTokenValueUri=http://launch.smarthealthit.org/v/r4/auth/token +oauth.securityExtTokenValueUri=http://launch.smarthealthit.org/v/dstu3/auth/token oauth.serviceSystem=http://hl7.org/fhir/restful-security-service oauth.serviceCode=SMART-on-FHIR oauth.serviceDisplay=SMART-on-FHIR diff --git a/pom.xml b/pom.xml index e23105719..62a72e0fe 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ 5.4.1 5.4.5 1.3.1-SNAPSHOT - 1.5.1 + 1.5.2-SNAPSHOT 1.2.1-SNAPSHOT 1.5.3 1.3.1-SNAPSHOT @@ -345,6 +345,12 @@ h2 + + org.apache.derby + derby + 10.14.2.0 + + org.postgresql postgresql diff --git a/r4/src/main/java/org/opencds/cqf/r4/config/FhirServerConfigR4.java b/r4/src/main/java/org/opencds/cqf/r4/config/FhirServerConfigR4.java index 9ebca7155..255d379d4 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/config/FhirServerConfigR4.java +++ b/r4/src/main/java/org/opencds/cqf/r4/config/FhirServerConfigR4.java @@ -2,12 +2,22 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import javax.persistence.EntityManagerFactory; import javax.sql.DataSource; +import org.cqframework.cql.cql2elm.CqlTranslatorOptions; +import org.cqframework.cql.cql2elm.model.Model; +import org.cqframework.cql.elm.execution.Library; +import org.hl7.elm.r1.VersionedIdentifier; import org.opencds.cqf.common.config.HapiProperties; +import org.opencds.cqf.common.providers.CacheAwareTerminologyProvider; +import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; +import org.opencds.cqf.cql.evaluator.engine.model.CachingModelResolverDecorator; import org.opencds.cqf.r4.providers.ActivityDefinitionApplyProvider; import org.opencds.cqf.r4.providers.ApplyCqlOperationProvider; import org.opencds.cqf.r4.providers.CacheValueSetsProvider; @@ -24,16 +34,19 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.ParserOptions; +import ca.uhn.fhir.cql.r4.listener.ElmCacheResourceChangeListener; import ca.uhn.fhir.cql.r4.provider.JpaTerminologyProvider; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; @Configuration @ComponentScan(basePackages = "org.opencds.cqf.r4") @@ -45,17 +58,6 @@ public FhirServerConfigR4(DataSource myDataSource) { this.myDataSource = myDataSource; } - @Override - public FhirContext fhirContextR4() { - FhirContext retVal = FhirContext.forR4(); - - // Don't strip versions in some places - ParserOptions parserOptions = retVal.getParserOptions(); - parserOptions.setDontStripVersionsFromReferencesAtPaths("AuditEvent.entity.what"); - - return retVal; - } - /** * We override the paging provider definition so that we can customize the * default/max page sizes for search results. You can set these however you @@ -128,7 +130,39 @@ public NarrativeProvider narrativeProvider() { } @Bean - public TerminologyProvider terminologyProvider(ca.uhn.fhir.jpa.term.api.ITermReadSvcR4 theTerminologySvc, ca.uhn.fhir.jpa.api.dao.DaoRegistry theDaoRegistry, ca.uhn.fhir.context.support.IValidationSupport theValidationSupport) { + public JpaTerminologyProvider jpaTerminologyProvider(ca.uhn.fhir.jpa.term.api.ITermReadSvcR4 theTerminologySvc, ca.uhn.fhir.jpa.api.dao.DaoRegistry theDaoRegistry, ca.uhn.fhir.context.support.IValidationSupport theValidationSupport) { return new JpaTerminologyProvider(theTerminologySvc, theDaoRegistry, theValidationSupport); } + + @Bean + @Primary + public TerminologyProvider terminologyProvider(Map> terminologyCache, JpaTerminologyProvider jpaTerminologyProvider) { + return new CacheAwareTerminologyProvider(terminologyCache, jpaTerminologyProvider); + } + + @Bean + public ModelResolver modelResolver() { + return new CachingModelResolverDecorator(new R4FhirModelResolver()); + } + + @Lazy + @Bean + public org.opencds.cqf.r4.helpers.LibraryHelper libraryHelper(Map globalModelCache, + Map globalLibraryCache, + CqlTranslatorOptions cqlTranslatorOptions) { + return new org.opencds.cqf.r4.helpers.LibraryHelper(globalModelCache, globalLibraryCache, cqlTranslatorOptions); + } + + @Lazy + @Bean + public CqlTranslatorOptions cqlTranslatorOptions() { + return CqlTranslatorOptions.defaultOptions(); + } + + @Bean + public ElmCacheResourceChangeListener elmCacheResourceChangeListener(IResourceChangeListenerRegistry resourceChangeListenerRegistry, IFhirResourceDao libraryDao, Map globalLibraryCache) { + ElmCacheResourceChangeListener listener = new ElmCacheResourceChangeListener(libraryDao, globalLibraryCache); + resourceChangeListenerRegistry.registerResourceResourceChangeListener("Library", SearchParameterMap.newSynchronous(), listener, 1000); + return listener; + } } diff --git a/r4/src/main/java/org/opencds/cqf/r4/evaluation/MeasureEvaluationSeed.java b/r4/src/main/java/org/opencds/cqf/r4/evaluation/MeasureEvaluationSeed.java index 33b696018..3d2e3d01f 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/evaluation/MeasureEvaluationSeed.java +++ b/r4/src/main/java/org/opencds/cqf/r4/evaluation/MeasureEvaluationSeed.java @@ -10,15 +10,15 @@ import org.opencds.cqf.common.helpers.DateHelper; import org.opencds.cqf.common.helpers.LoggingHelper; import org.opencds.cqf.common.helpers.UsingHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.data.DataProvider; -import org.opencds.cqf.cql.engine.debug.DebugMap; import org.opencds.cqf.cql.engine.execution.Context; import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; -import org.opencds.cqf.r4.helpers.LibraryHelper; + +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import ca.uhn.fhir.cql.r4.helper.LibraryHelper; public class MeasureEvaluationSeed { private Measure measure; @@ -28,12 +28,14 @@ public class MeasureEvaluationSeed { private LibraryResolutionProvider libraryResourceProvider; private EvaluationProviderFactory providerFactory; private DataProvider dataProvider; + private LibraryHelper libraryHelper; public MeasureEvaluationSeed(EvaluationProviderFactory providerFactory, LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { + LibraryResolutionProvider libraryResourceProvider, LibraryHelper libraryHelper) { this.providerFactory = providerFactory; this.libraryLoader = libraryLoader; this.libraryResourceProvider = libraryResourceProvider; + this.libraryHelper = libraryHelper; } public Measure getMeasure() { @@ -56,13 +58,14 @@ public void setup(Measure measure, String periodStart, String periodEnd, String String user, String pass) { this.measure = measure; - LibraryHelper.loadLibraries(measure, this.libraryLoader, this.libraryResourceProvider); + this.libraryHelper.loadLibraries(measure, this.libraryLoader, this.libraryResourceProvider); // resolve primary library - Library library = LibraryHelper.resolvePrimaryLibrary(measure, libraryLoader, this.libraryResourceProvider); + Library library = this.libraryHelper.resolvePrimaryLibrary(measure, libraryLoader, this.libraryResourceProvider); // resolve execution context context = new Context(library); + context.setExpressionCaching(true); context.setDebugMap(LoggingHelper.getDebugMap()); context.registerLibraryLoader(libraryLoader); @@ -103,8 +106,5 @@ public void setup(Measure measure, String periodStart, String periodEnd, String if (productLine != null) { context.setParameter(null, "Product Line", productLine); } - - context.setExpressionCaching(true); - context.setDebugMap(LoggingHelper.getDebugMap()); } } diff --git a/r4/src/main/java/org/opencds/cqf/r4/evaluation/ProviderFactory.java b/r4/src/main/java/org/opencds/cqf/r4/evaluation/ProviderFactory.java index ed0631c0a..a9499d17e 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/evaluation/ProviderFactory.java +++ b/r4/src/main/java/org/opencds/cqf/r4/evaluation/ProviderFactory.java @@ -8,13 +8,14 @@ import org.opencds.cqf.common.retrieve.JpaFhirRetrieveProvider; import org.opencds.cqf.cql.engine.data.CompositeDataProvider; import org.opencds.cqf.cql.engine.data.DataProvider; -import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver; import org.opencds.cqf.cql.engine.fhir.searchparam.SearchParameterResolver; import org.opencds.cqf.cql.engine.fhir.terminology.R4FhirTerminologyProvider; +import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; import org.springframework.stereotype.Component; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.rest.client.api.IGenericClient; @@ -26,13 +27,15 @@ public class ProviderFactory implements EvaluationProviderFactory { DaoRegistry registry; TerminologyProvider defaultTerminologyProvider; FhirContext fhirContext; + ModelResolver modelResolver; @Inject public ProviderFactory(FhirContext fhirContext, DaoRegistry registry, - TerminologyProvider defaultTerminologyProvider) { + TerminologyProvider defaultTerminologyProvider, ModelResolver modelResolver) { this.defaultTerminologyProvider = defaultTerminologyProvider; this.registry = registry; this.fhirContext = fhirContext; + this.modelResolver = modelResolver; } public DataProvider createDataProvider(String model, String version) { @@ -46,7 +49,6 @@ public DataProvider createDataProvider(String model, String version, String url, public DataProvider createDataProvider(String model, String version, TerminologyProvider terminologyProvider) { if (model.equals("FHIR") && version.startsWith("4")) { - R4FhirModelResolver modelResolver = new R4FhirModelResolver(); JpaFhirRetrieveProvider retrieveProvider = new JpaFhirRetrieveProvider(this.registry, new SearchParameterResolver(this.fhirContext)); retrieveProvider.setTerminologyProvider(terminologyProvider); @@ -62,7 +64,7 @@ public DataProvider createDataProvider(String model, String version, Terminology public TerminologyProvider createTerminologyProvider(String model, String version, String url, String user, String pass) { if (url != null && !url.isEmpty()) { - IGenericClient client = ClientHelper.getClient(FhirContext.forR4(), url, user, pass); + IGenericClient client = ClientHelper.getClient(FhirContext.forCached(FhirVersionEnum.R4), url, user, pass); if (url.contains("apelon.com")) { return new R4ApelonFhirTerminologyProvider(client); } diff --git a/r4/src/main/java/org/opencds/cqf/r4/helpers/LibraryHelper.java b/r4/src/main/java/org/opencds/cqf/r4/helpers/LibraryHelper.java index 8ef378574..971142be6 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/helpers/LibraryHelper.java +++ b/r4/src/main/java/org/opencds/cqf/r4/helpers/LibraryHelper.java @@ -1,201 +1,102 @@ package org.opencds.cqf.r4.helpers; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; +import org.cqframework.cql.cql2elm.CqlTranslatorOptions; import org.cqframework.cql.cql2elm.LibraryManager; import org.cqframework.cql.cql2elm.ModelManager; +import org.cqframework.cql.cql2elm.model.Model; import org.cqframework.cql.elm.execution.Library; -import org.cqframework.cql.elm.execution.VersionedIdentifier; -import org.hl7.fhir.r4.model.*; -import org.opencds.cqf.common.evaluation.LibraryLoader; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; -import org.opencds.cqf.common.providers.LibrarySourceProvider; - -/** - * Created by Christopher on 1/11/2017. - */ -public class LibraryHelper { - - public static LibraryLoader createLibraryLoader(LibraryResolutionProvider provider) { - ModelManager modelManager = new ModelManager(); - LibraryManager libraryManager = new LibraryManager(modelManager); - libraryManager.getLibrarySourceLoader().clearProviders(); +import org.hl7.elm.r1.VersionedIdentifier; +import org.hl7.fhir.r4.model.Attachment; +import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager; - libraryManager.getLibrarySourceLoader().registerProvider( - new LibrarySourceProvider(provider, - x -> x.getContent(), x -> x.getContentType(), x -> x.getData())); +import ca.uhn.fhir.cql.common.provider.LibraryContentProvider; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; - return new LibraryLoader(libraryManager, modelManager); - } +import org.opencds.cqf.cql.evaluator.engine.execution.CacheAwareLibraryLoaderDecorator; +import org.opencds.cqf.cql.evaluator.engine.execution.TranslatingLibraryLoader; - public static LibraryLoader createLibraryLoader(org.cqframework.cql.cql2elm.LibrarySourceProvider provider) { - ModelManager modelManager = new ModelManager(); - LibraryManager libraryManager = new LibraryManager(modelManager); - libraryManager.getLibrarySourceLoader().clearProviders(); +public class LibraryHelper extends ca.uhn.fhir.cql.r4.helper.LibraryHelper { - libraryManager.getLibrarySourceLoader().registerProvider(provider); + protected Map modelCache; + private Map libraryCache; + private CqlTranslatorOptions translatorOptions; - return new LibraryLoader(libraryManager, modelManager); + public LibraryHelper(Map modelCache, + Map libraryCache, + CqlTranslatorOptions translatorOptions) { + super(modelCache, libraryCache, translatorOptions); + this.modelCache = modelCache; + this.libraryCache = libraryCache; + this.translatorOptions = translatorOptions; } - public static org.hl7.fhir.r4.model.Library resolveLibraryReference(LibraryResolutionProvider libraryResourceProvider, String reference) { - // Raw references to Library/libraryId or libraryId - if (reference.startsWith("Library/") || !reference.contains("/")) { - return libraryResourceProvider.resolveLibraryById(reference.replace("Library/", "")); - } - // Full url (e.g. http://hl7.org/fhir/us/Library/FHIRHelpers) - else if (reference.contains(("/Library/"))) { - return libraryResourceProvider.resolveLibraryByCanonicalUrl(reference); - } - - return null; + public ModelManager getModelManager() { + return new CacheAwareModelManager(this.modelCache); } - public static List loadLibraries(Measure measure, - org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { - List libraries = new ArrayList(); - - // load libraries - //TODO: if there's a bad measure argument, this blows up for an obscure error - org.hl7.fhir.r4.model.Library primaryLibrary = null; - for (CanonicalType ref : measure.getLibrary()) { - // if library is contained in measure, load it into server - String id = ref.getValue(); //CanonicalHelper.getId(ref); - if (id.startsWith("#")) { - id = id.substring(1); - for (Resource resource : measure.getContained()) { - if (resource instanceof org.hl7.fhir.r4.model.Library - && resource.getIdElement().getIdPart().equals(id)) { - libraryResourceProvider.update((org.hl7.fhir.r4.model.Library) resource); - } - } - } - - // We just loaded it into the server so we can access it by Id - org.hl7.fhir.r4.model.Library library = resolveLibraryReference(libraryResourceProvider, id); - if (primaryLibrary == null) { - primaryLibrary = library; - } - - if (library != null && isLogicLibrary(library)) { - libraries.add( - libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())) - ); - } - } - - if (libraries.isEmpty()) { - throw new IllegalArgumentException(String - .format("Could not load library source for libraries referenced in Measure/%s.", measure.getId())); - } + public LibraryManager getLibraryManager(LibraryResolutionProvider provider) { + List contentProviders = Collections + .singletonList(new LibraryContentProvider(provider, + x -> x.getContent(), x -> x.getContentType(), x -> x.getData())); - //VersionedIdentifier primaryLibraryId = libraries.get(0).getIdentifier(); - //org.hl7.fhir.r4.model.Library primaryLibrary = libraryResourceProvider.resolveLibraryByName(primaryLibraryId.getId(), primaryLibraryId.getVersion()); - for (RelatedArtifact artifact : primaryLibrary.getRelatedArtifact()) { - if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON) && artifact.hasResource()) { - org.hl7.fhir.r4.model.Library library = null; - library = resolveLibraryReference(libraryResourceProvider, artifact.getResource()); - - if (library != null && isLogicLibrary(library)) { - libraries.add( - libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())) - ); - } - } + LibraryManager libraryManager = new LibraryManager(this.getModelManager()); + for (org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider contentProvider : contentProviders) { + libraryManager.getLibrarySourceLoader().registerProvider(contentProvider); } - return libraries; + return libraryManager; } - private static boolean isLogicLibrary(org.hl7.fhir.r4.model.Library library) { - if (library == null) { - return false; - } - - if (!library.hasType()) { - // If no type is specified, assume it is a logic library based on whether there is a CQL content element. - if (library.hasContent()) { - for (Attachment a : library.getContent()) { - if (a.hasContentType() && (a.getContentType().equals("text/cql") - || a.getContentType().equals("application/elm+xml") - || a.getContentType().equals("application/elm+json"))) { - return true; - } - } - } - return false; - } - - if (!library.getType().hasCoding()) { - return false; - } - - for (Coding c : library.getType().getCoding()) { - if (c.hasSystem() && c.getSystem().equals("http://terminology.hl7.org/CodeSystem/library-type") - && c.hasCode() && c.getCode().equals("logic-library")) { - return true; - } - } - - return false; + public CqlTranslatorOptions getTranslatorOptions() { + return this.translatorOptions; } - public static Library resolveLibraryById(String libraryId, - org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { - // Library library = null; - - org.hl7.fhir.r4.model.Library fhirLibrary = libraryResourceProvider.resolveLibraryById(libraryId); - return libraryLoader - .load(new VersionedIdentifier().withId(fhirLibrary.getName()).withVersion(fhirLibrary.getVersion())); - - // for (Library l : libraryLoader.getLibraries()) { - // VersionedIdentifier vid = l.getIdentifier(); - // if (vid.getId().equals(fhirLibrary.getName()) && - // LibraryResourceHelper.compareVersions(fhirLibrary.getVersion(), - // vid.getVersion()) == 0) { - // library = l; - // break; - // } - // } - - // if (library == null) { - - // } - - // return library; + public Map getLibraryCache() { + return this.libraryCache; } - public static Library resolvePrimaryLibrary(Measure measure, - org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { - // default is the first library reference - String id = CanonicalHelper.getId(measure.getLibrary().get(0)); - - Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider); + @Override + public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader( + LibraryResolutionProvider provider) { + ModelManager modelManager = new CacheAwareModelManager(this.modelCache); + LibraryManager libraryManager = new LibraryManager(modelManager); + libraryManager.getLibrarySourceLoader().clearProviders(); + List contentProviders = Collections + .singletonList(new LibraryContentProvider(provider, + x -> x.getContent(), x -> x.getContentType(), x -> x.getData())); - if (library == null) { - throw new IllegalArgumentException(String.format("Could not resolve primary library for Measure/%s.", - measure.getIdElement().getIdPart())); - } + TranslatingLibraryLoader translatingLibraryLoader = new TranslatingLibraryLoader(modelManager, contentProviders, + translatorOptions); - return library; + return new CacheAwareLibraryLoaderDecorator(translatingLibraryLoader, libraryCache) { + @Override + protected Boolean translatorOptionsMatch(Library library) { + return true; + } + }; } - public static Library resolvePrimaryLibrary(PlanDefinition planDefinition, - org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader, - LibraryResolutionProvider libraryResourceProvider) { - String id = CanonicalHelper.getId(planDefinition.getLibrary().get(0)); + @Override + public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader( + org.cqframework.cql.cql2elm.LibrarySourceProvider provider) { + ModelManager modelManager = new CacheAwareModelManager(this.modelCache); + LibraryManager libraryManager = new LibraryManager(modelManager); + libraryManager.getLibrarySourceLoader().clearProviders(); - Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider); + libraryManager.getLibrarySourceLoader().registerProvider(provider); - if (library == null) { - throw new IllegalArgumentException(String.format("Could not resolve primary library for PlanDefinition/%s", - planDefinition.getIdElement().getIdPart())); - } + TranslatingLibraryLoader translatingLibraryLoader = new TranslatingLibraryLoader(modelManager, null, + translatorOptions); - return library; + return new CacheAwareLibraryLoaderDecorator(translatingLibraryLoader, libraryCache) { + @Override + protected Boolean translatorOptionsMatch(Library library) { + return true; + } + }; } } diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/ActivityDefinitionApplyProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/ActivityDefinitionApplyProvider.java index abd4e8822..bbeb39d78 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/ActivityDefinitionApplyProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/ActivityDefinitionApplyProvider.java @@ -27,7 +27,6 @@ import org.hl7.fhir.r4.model.SupplyRequest; import org.hl7.fhir.r4.model.Task; import org.opencds.cqf.common.exceptions.ActivityDefinitionApplyException; -import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.r4.builders.JavaDateBuilder; @@ -57,8 +56,8 @@ public class ActivityDefinitionApplyProvider { @Inject public ActivityDefinitionApplyProvider(FhirContext fhirContext, CqlExecutionProvider executionProvider, - IFhirResourceDao activityDefinitionDao) { - this.modelResolver = new R4FhirModelResolver(); + IFhirResourceDao activityDefinitionDao, ModelResolver modelResolver) { + this.modelResolver = modelResolver; this.executionProvider = executionProvider; this.activityDefinitionDao = activityDefinitionDao; } diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/CqlExecutionProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/CqlExecutionProvider.java index b79963b0e..17aae28a6 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/CqlExecutionProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/CqlExecutionProvider.java @@ -26,23 +26,23 @@ import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.Type; import org.opencds.cqf.common.evaluation.EvaluationProviderFactory; -import org.opencds.cqf.common.evaluation.LibraryLoader; import org.opencds.cqf.common.helpers.DateHelper; import org.opencds.cqf.common.helpers.LoggingHelper; import org.opencds.cqf.common.helpers.TranslatorHelper; import org.opencds.cqf.common.helpers.UsingHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.data.DataProvider; import org.opencds.cqf.cql.engine.execution.Context; +import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; import org.opencds.cqf.r4.helpers.CanonicalHelper; import org.opencds.cqf.r4.helpers.FhirMeasureBundler; -import org.opencds.cqf.r4.helpers.LibraryHelper; import org.springframework.stereotype.Component; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import org.opencds.cqf.r4.helpers.LibraryHelper; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; @@ -54,13 +54,15 @@ public class CqlExecutionProvider { private EvaluationProviderFactory providerFactory; private LibraryResolutionProvider libraryResourceProvider; private FhirContext context; + private LibraryHelper libraryHelper; @Inject public CqlExecutionProvider(LibraryResolutionProvider libraryResourceProvider, - EvaluationProviderFactory providerFactory, FhirContext context) { + EvaluationProviderFactory providerFactory, FhirContext context, LibraryHelper libraryHelper) { this.providerFactory = providerFactory; this.libraryResourceProvider = libraryResourceProvider; this.context = context; + this.libraryHelper = libraryHelper; } private LibraryResolutionProvider getLibraryResourceProvider() { @@ -176,10 +178,10 @@ public Object evaluateInContext(DomainResource instance, String cql, String pati "library LocalLibrary using FHIR version '" + fhirVersion + "' include FHIRHelpers version '"+ fhirVersion +"' called FHIRHelpers %s parameter %s %s parameter \"%%context\" %s define Expression: %s", buildIncludes(libraries), instance.fhirType(), instance.fhirType(), instance.fhirType(), cql); - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); org.cqframework.cql.elm.execution.Library library = TranslatorHelper.translateLibrary(source, - libraryLoader.getLibraryManager(), libraryLoader.getModelManager()); + this.libraryHelper.getLibraryManager(this.getLibraryResourceProvider()), this.libraryHelper.getModelManager()); // resolve execution context Context context = setupContext(instance, patientId, libraryLoader, library); @@ -195,9 +197,9 @@ public Object evaluateInContext(DomainResource instance, String cql, String pati if (lib == null) { throw new RuntimeException("Library with id " + reference.getIdBase() + "not found"); } - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); // resolve primary library - org.cqframework.cql.elm.execution.Library library = LibraryHelper.resolveLibraryById(lib.getId(), + org.cqframework.cql.elm.execution.Library library = this.libraryHelper.resolveLibraryById(lib.getId(), libraryLoader, this.libraryResourceProvider); // resolve execution context @@ -251,13 +253,13 @@ public Bundle evaluate(@OperationParam(name = "code") String code, CqlTranslator translator; FhirMeasureBundler bundler = new FhirMeasureBundler(); - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.getLibraryResourceProvider()); List results = new ArrayList<>(); try { - translator = TranslatorHelper.getTranslator(code, libraryLoader.getLibraryManager(), - libraryLoader.getModelManager()); + translator = TranslatorHelper.getTranslator(code, this.libraryHelper.getLibraryManager(this.getLibraryResourceProvider()), + this.libraryHelper.getModelManager()); if (translator.getErrors().size() > 0) { for (CqlTranslatorException cte : translator.getErrors()) { diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/DataRequirementsProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/DataRequirementsProvider.java index 8605d7292..fbbc32674 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/DataRequirementsProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/DataRequirementsProvider.java @@ -15,6 +15,8 @@ import java.util.Objects; import java.util.stream.Collectors; +import javax.inject.Inject; + import com.google.common.base.Strings; import com.vladsch.flexmark.ext.autolink.AutolinkExtension; import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughExtension; @@ -55,20 +57,29 @@ import org.hl7.fhir.r4.model.RelatedArtifact; import org.hl7.fhir.r4.model.StringType; import org.opencds.cqf.common.helpers.TranslatorHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.tooling.measure.r4.CodeTerminologyRef; import org.opencds.cqf.tooling.measure.r4.CqfMeasure; import org.opencds.cqf.tooling.measure.r4.TerminologyRef; import org.opencds.cqf.tooling.measure.r4.TerminologyRef.TerminologyRefType; import org.springframework.stereotype.Component; + +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import ca.uhn.fhir.cql.r4.helper.LibraryHelper; + import org.opencds.cqf.tooling.measure.r4.VersionedTerminologyRef; import org.opencds.cqf.r4.helpers.CanonicalHelper; -import org.opencds.cqf.r4.helpers.LibraryHelper; @Component public class DataRequirementsProvider { + private LibraryHelper libraryHelper; + + @Inject + public DataRequirementsProvider(LibraryHelper libraryHelper) { + this.libraryHelper = libraryHelper; + + } // For creating the CQF measure we need to: // 1. Find the Primary Library Resource // 2. Load the Primary Library as ELM. This will recursively load the dependent @@ -87,8 +98,8 @@ public CqfMeasure createCqfMeasure(Measure measure, private Map> createLibraryMap(Measure measure, LibraryResolutionProvider libraryResourceProvider) { - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(libraryResourceProvider); - List libraries = LibraryHelper.loadLibraries(measure, libraryLoader, libraryResourceProvider); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(libraryResourceProvider); + List libraries = this.libraryHelper.loadLibraries(measure, libraryLoader, libraryResourceProvider); Map> libraryMap = new HashMap<>(); for (Library library : libraries) { diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/HQMFProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/HQMFProvider.java index f5fe84798..820d7f8d2 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/HQMFProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/HQMFProvider.java @@ -1,20 +1,11 @@ package org.opencds.cqf.r4.providers; -import java.io.File; -import java.io.FileReader; -import java.io.PrintWriter; -import java.io.Reader; import java.io.StringWriter; -import java.net.URI; -import java.nio.file.Path; -import java.nio.file.Paths; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; -import java.util.stream.Collectors; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; @@ -24,24 +15,17 @@ import com.jamesmurty.utils.XMLBuilder2; -import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.ContactDetail; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Library; import org.hl7.fhir.r4.model.MarkdownType; -import org.hl7.fhir.r4.model.Measure; import org.hl7.fhir.r4.model.Measure.MeasureGroupComponent; import org.hl7.fhir.r4.model.Measure.MeasureGroupPopulationComponent; import org.hl7.fhir.r4.model.Measure.MeasureSupplementalDataComponent; -import org.hl7.fhir.r4.model.Narrative; import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.RelatedArtifact; import org.hl7.fhir.r4.model.RelatedArtifact.RelatedArtifactType; -import org.jsoup.Jsoup; -import org.opencds.cqf.common.providers.InMemoryLibraryResourceProvider; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; -import org.opencds.cqf.tooling.library.r4.NarrativeProvider; import org.opencds.cqf.tooling.measure.r4.CodeTerminologyRef; import org.opencds.cqf.tooling.measure.r4.CqfMeasure; import org.opencds.cqf.tooling.measure.r4.TerminologyRef; @@ -49,9 +33,6 @@ import org.springframework.stereotype.Component; import org.w3c.dom.Document; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.parser.IParser; - @Component public class HQMFProvider { @@ -604,134 +585,4 @@ private String writeDocument(Document d) { return null; } } - - // private boolean validateHQMF(String xml) { - // try { - // return this.validateXML(this.loadHQMFSchema(), xml); - // } catch (SAXException e) { - // return false; - // } - // } - - // private boolean validateXML(Schema schema, String xml) { - // try { - // Validator validator = schema.newValidator(); - // validator.validate(new StreamSource(new StringReader(xml))); - // } catch (IOException | SAXException e) { - // System.out.println("Exception: " + e.getMessage()); - // return false; - // } - // return true; - // } - - // private Schema loadHQMFSchema() throws SAXException { - // SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - // URL hqmfSchema = ClassLoader.getSystemClassLoader().getResource("hqmf/schemas/EMeasure_N1.xsd"); - // return factory.newSchema(hqmfSchema); - // } - - // args[0] == relative path to json measure -> i.e. measure/measure-demo.json - // (optional) - // args[1] == path to resource output -> i.e. - // library/library-demo.json(optional) - // args[2] == path to hqmf output -> i.e. hqmf.xml(optional) - // args[3] == path to narrative output -> i.e. output.html(optional) - public static void main(String[] args) { - - try { - List strings = Arrays.asList("hqmf/examples/input/measure-ann.json", - "hqmf/examples/input/library-common.json", "hqmf/examples/input/library-ann.json"); - - List paths = strings.stream().map(x -> Paths.get(toUri(x))).collect(Collectors.toList()); - - // Path pathToLibrary = - // Paths.get(HQMFProvider.class.getClassLoader().getResource("narratives/examples/library/CMS146.json").toURI()); - Path pathToOutput = Paths.get("src/main/resources/hqmf/hqmf.xml").toAbsolutePath(); - Path pathToNarrativeOutput = Paths.get("src/main/resources/narratives/output.html").toAbsolutePath(); - - Path pathToProp = Paths.get( - NarrativeProvider.class.getClassLoader().getResource("narratives/narrative.properties").toURI()); - - if (args.length >= 4) { - pathToNarrativeOutput = Paths.get(new URI(args[3])); - } - - if (args.length >= 3) { - pathToOutput = Paths.get(new URI(args[2])); - } - - // if (args.length >= 2) { - // pathToLibrary = Paths.get(new URI(args[1])); - // } - - // if (args.length >= 1) { - // pathToMeasure = Paths.get(new URI(args[0])); - // } - - HQMFProvider provider = new HQMFProvider(); - DataRequirementsProvider dataRequirementsProvider = new DataRequirementsProvider(); - NarrativeProvider narrativeProvider = new NarrativeProvider(pathToProp.toUri().toString()); - ; - - FhirContext context = FhirContext.forDstu3(); - - // IParser parser = pathToMeasure.toString().endsWith("json") ? - // context.newJsonParser() : context.newXmlParser(); - - IParser parser = context.newJsonParser(); - - List resources = paths.stream().map(x -> toReader(x)).filter(x -> x != null) - .map(x -> parser.parseResource(x)).collect(Collectors.toList()); - - Measure measure = (Measure) resources.stream().filter(x -> (x instanceof Measure)).findFirst().get(); - List libraries = resources.stream().filter(x -> (x instanceof Library)).map(x -> (Library) x) - .collect(Collectors.toList()); - - LibraryResolutionProvider lrp = new InMemoryLibraryResourceProvider(libraries, - x -> x.getIdElement().getIdPart(), x -> x.getName(), x -> x.getVersion()); - - CqfMeasure cqfMeasure = dataRequirementsProvider.createCqfMeasure(measure, lrp); - - String result = provider.generateHQMF(cqfMeasure); - - PrintWriter writer = new PrintWriter(new File(pathToOutput.toString()), "UTF-8"); - writer.println(result); - writer.println(); - writer.close(); - - Narrative narrative = narrativeProvider.getNarrative(context, cqfMeasure); - String narrativeContent = narrative.getDivAsString(); - - Path pathToHTML = Paths.get(toUri("narratives/templates/hqmf.html")); - org.jsoup.nodes.Document htmlDoc = Jsoup.parse(pathToHTML.toFile(), "UTF-8"); - - htmlDoc.title(measure.getName()); - htmlDoc.body().html(narrativeContent); - - writer = new PrintWriter(new File(pathToNarrativeOutput.toString()), "UTF-8"); - writer.write(htmlDoc.outerHtml()); - writer.close(); - } catch (Exception e) { - e.printStackTrace(); - return; - } - } - - public static Reader toReader(Path p) { - try { - return new FileReader(p.toFile()); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } - - public static URI toUri(String s) { - try { - return HQMFProvider.class.getClassLoader().getResource(s).toURI(); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } } diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/LibraryOperationsProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/LibraryOperationsProvider.java index cff062f71..40ea130c6 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/LibraryOperationsProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/LibraryOperationsProvider.java @@ -29,18 +29,17 @@ import org.hl7.fhir.r4.model.StringType; import org.hl7.fhir.r4.model.Type; import org.opencds.cqf.cds.providers.PriorityRetrieveProvider; -import org.opencds.cqf.common.evaluation.LibraryLoader; import org.opencds.cqf.common.helpers.ClientHelperDos; import org.opencds.cqf.common.helpers.DateHelper; import org.opencds.cqf.common.helpers.LoggingHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; -import org.opencds.cqf.common.providers.LibrarySourceProvider; +import org.opencds.cqf.common.providers.LibraryContentProvider; import org.opencds.cqf.common.providers.R4ApelonFhirTerminologyProvider; import org.opencds.cqf.common.retrieve.JpaFhirRetrieveProvider; import org.opencds.cqf.cql.engine.data.CompositeDataProvider; import org.opencds.cqf.cql.engine.data.DataProvider; import org.opencds.cqf.cql.engine.execution.CqlEngine; import org.opencds.cqf.cql.engine.execution.EvaluationResult; +import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.cql.engine.fhir.model.FhirModelResolver; import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver; import org.opencds.cqf.cql.engine.fhir.retrieve.RestFhirRetrieveProvider; @@ -49,12 +48,15 @@ import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; +import org.opencds.cqf.cql.evaluator.engine.execution.CacheAwareLibraryLoaderDecorator; +import org.opencds.cqf.cql.evaluator.engine.execution.TranslatingLibraryLoader; import org.opencds.cqf.cql.evaluator.engine.retrieve.BundleRetrieveProvider; import org.opencds.cqf.tooling.library.r4.NarrativeProvider; import org.springframework.stereotype.Component; import org.opencds.cqf.r4.helpers.FhirMeasureBundler; -import org.opencds.cqf.r4.helpers.LibraryHelper; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import org.opencds.cqf.r4.helpers.LibraryHelper; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.rp.r4.LibraryResourceProvider; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; @@ -77,15 +79,17 @@ public class LibraryOperationsProvider implements LibraryResolutionProvider librarySourceProvider; + private LibraryContentProvider librarySourceProvider; - private LibrarySourceProvider getLibrarySourceProvider() { + private LibraryContentProvider getLibrarySourceProvider() { if (librarySourceProvider == null) { - librarySourceProvider = new LibrarySourceProvider( + librarySourceProvider = new LibraryContentProvider( getLibraryResourceProvider(), x -> x.getContent(), x -> x.getContentType(), x -> x.getData()); } return librarySourceProvider; @@ -271,12 +275,15 @@ public Bundle evaluate(@IdParam IdType theId, @OperationParam(name = "patientId" } } - org.cqframework.cql.cql2elm.LibrarySourceProvider bundleLibraryProvider = new R4BundleLibrarySourceProvider(libraryBundle); - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(bundleLibraryProvider); - LibraryResolutionProvider provider = this.getLibraryResourceProvider(); - libraryLoader.getLibraryManager().getLibrarySourceLoader().registerProvider( - new LibrarySourceProvider(provider, - x -> x.getContent(), x -> x.getContentType(), x -> x.getData())); + + ModelManager modelManager = this.libraryHelper.getModelManager(); + org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider bundleLibraryProvider = new R4BundleLibraryContentProvider(libraryBundle); + org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider sourceProvider = new LibraryContentProvider(this.getLibraryResourceProvider(), + x -> x.getContent(), x -> x.getContentType(), x -> x.getData()); + + List sourceProviders = Arrays.asList(bundleLibraryProvider, sourceProvider); + + LibraryLoader libraryLoader = new CacheAwareLibraryLoaderDecorator(new TranslatingLibraryLoader(modelManager, sourceProviders, this.libraryHelper.getTranslatorOptions())); CqlEngine engine = new CqlEngine(libraryLoader, Collections.singletonMap("http://hl7.org/fhir", dataProvider), terminologyProvider); diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/MeasureOperationsProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/MeasureOperationsProvider.java index 638686120..6aab78451 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/MeasureOperationsProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/MeasureOperationsProvider.java @@ -49,11 +49,9 @@ import org.opencds.cqf.common.config.HapiProperties; import org.opencds.cqf.common.evaluation.EvaluationProviderFactory; import org.opencds.cqf.common.helpers.DateHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.r4.evaluation.MeasureEvaluation; import org.opencds.cqf.r4.evaluation.MeasureEvaluationSeed; -import org.opencds.cqf.r4.helpers.LibraryHelper; import org.opencds.cqf.tooling.library.r4.NarrativeProvider; import org.opencds.cqf.tooling.measure.r4.CqfMeasure; import org.slf4j.Logger; @@ -61,6 +59,8 @@ import org.springframework.stereotype.Component; import ca.uhn.fhir.context.BaseRuntimeChildDefinition; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; +import ca.uhn.fhir.cql.r4.helper.LibraryHelper; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.rp.r4.MeasureResourceProvider; @@ -90,21 +90,24 @@ public class MeasureOperationsProvider { private EvaluationProviderFactory factory; private String serverAddress = HapiProperties.getServerAddress(); + private LibraryHelper libraryHelper; + private static final Logger logger = LoggerFactory.getLogger(MeasureOperationsProvider.class); @Inject public MeasureOperationsProvider(DaoRegistry registry, EvaluationProviderFactory factory, NarrativeProvider narrativeProvider, HQMFProvider hqmfProvider, LibraryResolutionProvider libraryResolutionProvider, - MeasureResourceProvider measureResourceProvider) { + MeasureResourceProvider measureResourceProvider, DataRequirementsProvider dataRequirementsProvider, LibraryHelper libraryHelper) { this.registry = registry; this.factory = factory; this.libraryResolutionProvider = libraryResolutionProvider; this.narrativeProvider = narrativeProvider; this.hqmfProvider = hqmfProvider; - this.dataRequirementsProvider = new DataRequirementsProvider(); + this.dataRequirementsProvider = dataRequirementsProvider; this.measureResourceProvider = measureResourceProvider; + this.libraryHelper = libraryHelper; } @Operation(name = "$hqmf", idempotent = true, type = Measure.class) @@ -189,9 +192,9 @@ public MeasureReport evaluateMeasure(@IdParam IdType theId, @OperationParam(name = "lastReceivedOn") String lastReceivedOn, @OperationParam(name = "source") String source, @OperationParam(name = "user") String user, @OperationParam(name = "pass") String pass) throws InternalErrorException, FHIRException { - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(this.libraryResolutionProvider); + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.libraryResolutionProvider); MeasureEvaluationSeed seed = new MeasureEvaluationSeed(this.factory, libraryLoader, - this.libraryResolutionProvider); + this.libraryResolutionProvider, this.libraryHelper); Measure measure = this.measureResourceProvider.getDao().read(theId); if (measure == null) { diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/PlanDefinitionApplyProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/PlanDefinitionApplyProvider.java index f5330eb0e..3881f4e35 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/PlanDefinitionApplyProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/PlanDefinitionApplyProvider.java @@ -24,7 +24,6 @@ import org.hl7.fhir.r4.model.StringType; import org.opencds.cqf.common.config.HapiProperties; import org.opencds.cqf.cql.engine.execution.Context; -import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.r4.builders.AttachmentBuilder; @@ -67,9 +66,9 @@ public class PlanDefinitionApplyProvider { public PlanDefinitionApplyProvider(FhirContext fhirContext, ActivityDefinitionApplyProvider activityDefinitionApplyProvider, IFhirResourceDao planDefinitionDao, - IFhirResourceDao activityDefinitionDao, CqlExecutionProvider executionProvider) { + IFhirResourceDao activityDefinitionDao, CqlExecutionProvider executionProvider, ModelResolver modelResolver) { this.executionProvider = executionProvider; - this.modelResolver = new R4FhirModelResolver(); + this.modelResolver = modelResolver; this.activityDefinitionApplyProvider = activityDefinitionApplyProvider; this.planDefinitionDao = planDefinitionDao; this.activityDefinitionDao = activityDefinitionDao; diff --git a/r4/src/main/java/org/opencds/cqf/r4/providers/R4BundleLibrarySourceProvider.java b/r4/src/main/java/org/opencds/cqf/r4/providers/R4BundleLibraryContentProvider.java similarity index 74% rename from r4/src/main/java/org/opencds/cqf/r4/providers/R4BundleLibrarySourceProvider.java rename to r4/src/main/java/org/opencds/cqf/r4/providers/R4BundleLibraryContentProvider.java index 233780b4b..f07ae5422 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/providers/R4BundleLibrarySourceProvider.java +++ b/r4/src/main/java/org/opencds/cqf/r4/providers/R4BundleLibraryContentProvider.java @@ -7,11 +7,14 @@ import org.hl7.elm.r1.VersionedIdentifier; import org.hl7.fhir.r4.model.*; import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentType; -public class R4BundleLibrarySourceProvider extends VersionComparingLibrarySourceProvider { + +// TODO: Add support for ELM +public class R4BundleLibraryContentProvider extends VersionComparingLibrarySourceProvider implements org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider { Bundle bundle; - public R4BundleLibrarySourceProvider(Bundle bundle) { + public R4BundleLibraryContentProvider(Bundle bundle) { this.bundle = bundle; } @@ -55,4 +58,13 @@ private InputStream getCqlStream(Library library) { return null; } + + @Override + public InputStream getLibraryContent(VersionedIdentifier libraryIdentifier, LibraryContentType libraryContentType) { + if (libraryContentType == LibraryContentType.CQL) { + return this.getLibrarySource(libraryIdentifier); + } + + return null; + } } \ No newline at end of file diff --git a/r4/src/main/java/org/opencds/cqf/r4/servlet/CdsHooksServlet.java b/r4/src/main/java/org/opencds/cqf/r4/servlet/CdsHooksServlet.java index 30c4584ce..9856bbbe0 100644 --- a/r4/src/main/java/org/opencds/cqf/r4/servlet/CdsHooksServlet.java +++ b/r4/src/main/java/org/opencds/cqf/r4/servlet/CdsHooksServlet.java @@ -38,16 +38,14 @@ import org.opencds.cqf.common.config.HapiProperties; import org.opencds.cqf.common.exceptions.InvalidRequestException; import org.opencds.cqf.common.helpers.LoggingHelper; -import org.opencds.cqf.common.providers.LibraryResolutionProvider; import org.opencds.cqf.common.retrieve.JpaFhirRetrieveProvider; import org.opencds.cqf.cql.engine.data.CompositeDataProvider; -import org.opencds.cqf.cql.engine.debug.DebugMap; import org.opencds.cqf.cql.engine.exception.CqlException; import org.opencds.cqf.cql.engine.execution.Context; import org.opencds.cqf.cql.engine.execution.LibraryLoader; import org.opencds.cqf.cql.engine.fhir.exception.DataProviderException; -import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver; -import org.opencds.cqf.r4.helpers.LibraryHelper; +import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; import org.opencds.cqf.r4.providers.PlanDefinitionApplyProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,9 +53,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.cql.r4.provider.JpaTerminologyProvider; +import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import org.opencds.cqf.r4.helpers.LibraryHelper; + @WebServlet(name = "cds-services") public class CdsHooksServlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -70,10 +70,14 @@ public class CdsHooksServlet extends HttpServlet { private JpaFhirRetrieveProvider fhirRetrieveProvider; - private JpaTerminologyProvider jpaTerminologyProvider; + private TerminologyProvider serverTerminologyProvider; private ProviderConfiguration providerConfiguration; + private ModelResolver modelResolver; + + private LibraryHelper libraryHelper; + @SuppressWarnings("unchecked") @Override public void init() { @@ -85,7 +89,9 @@ public void init() { this.planDefinitionProvider = appCtx.getBean(PlanDefinitionApplyProvider.class); this.libraryResolutionProvider = (LibraryResolutionProvider)appCtx.getBean(LibraryResolutionProvider.class); this.fhirRetrieveProvider = appCtx.getBean(JpaFhirRetrieveProvider.class); - this.jpaTerminologyProvider = appCtx.getBean(JpaTerminologyProvider.class); + this.serverTerminologyProvider = appCtx.getBean(TerminologyProvider.class); + this.modelResolver = appCtx.getBean(ModelResolver.class); + this.libraryHelper = appCtx.getBean(LibraryHelper.class); } protected ProviderConfiguration getProviderConfiguration() { @@ -170,12 +176,11 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) if(!planDefinitionHookMatchesRequestHook.get()){ throw new ServletException("ERROR: Request hook does not match the service called."); } - LibraryLoader libraryLoader = LibraryHelper.createLibraryLoader(libraryResolutionProvider); - Library library = LibraryHelper.resolvePrimaryLibrary(planDefinition, libraryLoader, + LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(libraryResolutionProvider); + Library library = this.libraryHelper.resolvePrimaryLibrary(planDefinition, libraryLoader, libraryResolutionProvider); - R4FhirModelResolver resolver = new R4FhirModelResolver(); - CompositeDataProvider provider = new CompositeDataProvider(resolver, fhirRetrieveProvider); + CompositeDataProvider provider = new CompositeDataProvider(this.modelResolver, fhirRetrieveProvider); Context context = new Context(library); @@ -183,20 +188,20 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response) context.registerDataProvider("http://hl7.org/fhir", provider); // TODO make sure tooling handles remote // provider case - context.registerTerminologyProvider(jpaTerminologyProvider); + context.registerTerminologyProvider(serverTerminologyProvider); context.registerLibraryLoader(libraryLoader); context.setContextValue("Patient", hook.getRequest().getContext().getPatientId().replace("Patient/", "")); context.setExpressionCaching(true); EvaluationContext evaluationContext = new R4EvaluationContext(hook, version, - FhirContext.forR4().newRestfulGenericClient(baseUrl), jpaTerminologyProvider, context, library, - planDefinition, this.getProviderConfiguration()); + FhirContext.forCached(FhirVersionEnum.R4).newRestfulGenericClient(baseUrl), serverTerminologyProvider, context, library, + planDefinition, this.getProviderConfiguration(), this.modelResolver); this.setAccessControlHeaders(response); response.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType()); - R4HookEvaluator evaluator = new R4HookEvaluator(); + R4HookEvaluator evaluator = new R4HookEvaluator(this.modelResolver); String jsonResponse = toJsonResponse(evaluator.evaluate(evaluationContext)); @@ -295,7 +300,7 @@ private JsonObject getService(String service) { private JsonObject getServices() { DiscoveryResolutionR4 discoveryResolutionR4 = new DiscoveryResolutionR4( - FhirContext.forR4().newRestfulGenericClient(HapiProperties.getServerAddress())); + FhirContext.forCached(FhirVersionEnum.R4).newRestfulGenericClient(HapiProperties.getServerAddress())); discoveryResolutionR4.setMaxUriLength(this.getProviderConfiguration().getMaxUriLength()); return discoveryResolutionR4.resolve() .getAsJson(); diff --git a/r4/src/main/resources/hapi.properties b/r4/src/main/resources/hapi.properties index 17f8eec26..18e9f7af7 100644 --- a/r4/src/main/resources/hapi.properties +++ b/r4/src/main/resources/hapi.properties @@ -34,10 +34,6 @@ logger.name=fhirtest.access logger.format=Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}] logger.error_format=ERROR - ${requestVerb} ${requestUrl} logger.log_exceptions=true -datasource.driver=org.h2.Driver -datasource.url=jdbc:h2:file:./target/database/h2/r4 -datasource.username= -datasource.password= server.name=Local Tester server.id=home test.port= @@ -74,7 +70,27 @@ fhirpath_interceptor.enabled=false ################################################### # Database Settings ################################################### -hibernate.dialect=org.hibernate.dialect.H2Dialect +# datasource.driver=org.h2.Driver +# datasource.url=jdbc:h2:file:./target/database/h2/r4 +# hibernate.dialect=org.hibernate.dialect.H2Dialect +# datasource.username= +# datasource.password= + + +datasource.driver=org.apache.derby.jdbc.EmbeddedDriver +datasource.url=jdbc:derby:target/derby;create=true +hibernate.dialect=org.hibernate.dialect.DerbyTenSevenDialect +spring.jpa.hibernate.ddl-auto=update +datasource.username= +datasource.password= + + +# datasource.driver=org.postgresql.Driver +# datasource.url=jdbc:postgresql://localhost:5432/hapi +# hibernate.dialect=org.hibernate.dialect.PostgreSQL95Dialect +# datasource.username=admin +# datasource.password=admin + hibernate.search.enabled=true hibernate.search.backend.type=lucene