diff --git a/Dockerfile b/Dockerfile index 806c542288a..7fefc679d3e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM docker.io/library/maven:3.9.9-eclipse-temurin-17 AS build-hapi WORKDIR /tmp/hapi-fhir-jpaserver-starter -ARG OPENTELEMETRY_JAVA_AGENT_VERSION=1.33.3 +ARG OPENTELEMETRY_JAVA_AGENT_VERSION=2.13.1 RUN curl -LSsO https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v${OPENTELEMETRY_JAVA_AGENT_VERSION}/opentelemetry-javaagent.jar COPY pom.xml . diff --git a/pom.xml b/pom.xml index 356ab2cf39a..44166e0e4dd 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ ca.uhn.hapi.fhir hapi-fhir - 8.2.0 + 8.3.7-SNAPSHOT hapi-fhir-jpaserver-starter diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 0095b538909..3f7e19b81f5 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -4,7 +4,7 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings.ClientIdStrategyEnum; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings.IdStrategyEnum; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel; -import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; +import ca.uhn.fhir.jpa.starter.ig.ExtendedPackageInstallationSpec; import ca.uhn.fhir.rest.api.EncodingEnum; import org.hl7.fhir.r4.model.Bundle; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -83,7 +83,9 @@ public class AppProperties { private Partitioning partitioning = null; private Boolean validate_resource_status_for_package_upload = true; private Boolean install_transitive_ig_dependencies = true; - private Map implementationGuides = null; + + private List install_additional_resources_from_ig_folders = new ArrayList<>(); + private Map implementationGuides = null; private String custom_content_path = null; private String app_content_path = null; private Boolean lastn_enabled = false; @@ -148,11 +150,11 @@ public void setDefer_indexing_for_codesystems_of_size(Integer defer_indexing_for this.defer_indexing_for_codesystems_of_size = defer_indexing_for_codesystems_of_size; } - public Map getImplementationGuides() { + public Map getImplementationGuides() { return implementationGuides; } - public void setImplementationGuides(Map implementationGuides) { + public void setImplementationGuides(Map implementationGuides) { this.implementationGuides = implementationGuides; } @@ -685,6 +687,15 @@ public void setResource_dbhistory_enabled(Boolean resource_dbhistory_enabled) { this.resource_dbhistory_enabled = resource_dbhistory_enabled; } + public List getInstall_additional_resources_from_ig_folders() { + return install_additional_resources_from_ig_folders; + } + + public void setInstall_additional_resources_from_ig_folders( + List install_additional_resources_from_ig_folders) { + this.install_additional_resources_from_ig_folders = install_additional_resources_from_ig_folders; + } + public Boolean getPre_expand_value_sets() { return this.pre_expand_value_sets; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java index e9f3549c518..76fdb6dd937 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java @@ -291,11 +291,6 @@ public PartitionSettings partitionSettings(AppProperties appProperties) { return retVal; } - @Bean - public PartitionModeConfigurer partitionModeConfigurer() { - return new PartitionModeConfigurer(); - } - @Primary @Bean public HibernatePropertiesProvider jpaStarterDialectProvider( diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/OnPartitionModeEnabled.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/OnPartitionModeEnabled.java new file mode 100644 index 00000000000..b695babe544 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/OnPartitionModeEnabled.java @@ -0,0 +1,18 @@ +package ca.uhn.fhir.jpa.starter.common; + +import ca.uhn.fhir.jpa.starter.AppProperties; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class OnPartitionModeEnabled implements Condition { + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + var appProperties = Binder.get(context.getEnvironment()) + .bind("hapi.fhir", AppProperties.class) + .orElse(null); + if (appProperties == null) return false; + return appProperties.getPartitioning() != null; + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/PartitionModeConfigurer.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/PartitionModeConfigurer.java index c83e8fccb89..dbf5f0f0d49 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/PartitionModeConfigurer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/PartitionModeConfigurer.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.jpa.starter.common; -import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.interceptor.PatientIdPartitionInterceptor; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.partition.PartitionManagementProvider; @@ -9,48 +8,36 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; -import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Component; +@Component +@Conditional({OnPartitionModeEnabled.class}) public class PartitionModeConfigurer { private static final Logger ourLog = LoggerFactory.getLogger(PartitionModeConfigurer.class); - @Autowired - private AppProperties myAppProperties; - - @Autowired - private FhirContext myFhirContext; - - @Autowired - private ISearchParamExtractor mySearchParamExtractor; - - @Autowired - private PartitionSettings myPartitionSettings; - - @Autowired - private RestfulServer myRestfulServer; - - @Autowired - private PartitionManagementProvider myPartitionManagementProvider; - - @PostConstruct - public void start() { - if (myAppProperties.getPartitioning() != null) { - if (myAppProperties.getPartitioning().getPatient_id_partitioning_mode() == Boolean.TRUE) { - ourLog.info("Partitioning mode enabled in: Patient ID partitioning mode"); - PatientIdPartitionInterceptor patientIdInterceptor = - new PatientIdPartitionInterceptor(myFhirContext, mySearchParamExtractor, myPartitionSettings); - myRestfulServer.registerInterceptor(patientIdInterceptor); - myPartitionSettings.setUnnamedPartitionMode(true); - } else if (myAppProperties.getPartitioning().getRequest_tenant_partitioning_mode() == Boolean.TRUE) { - ourLog.info("Partitioning mode enabled in: Request tenant partitioning mode"); - myRestfulServer.registerInterceptor(new RequestTenantPartitionInterceptor()); - myRestfulServer.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); - } - - myRestfulServer.registerProviders(myPartitionManagementProvider); + public PartitionModeConfigurer( + AppProperties myAppProperties, + ISearchParamExtractor mySearchParamExtractor, + PartitionSettings myPartitionSettings, + RestfulServer myRestfulServer, + PartitionManagementProvider myPartitionManagementProvider) { + + var partitioning = myAppProperties.getPartitioning(); + if (partitioning.getPatient_id_partitioning_mode()) { + ourLog.info("Partitioning mode enabled in: Patient ID partitioning mode"); + var patientIdInterceptor = new PatientIdPartitionInterceptor( + myRestfulServer.getFhirContext(), mySearchParamExtractor, myPartitionSettings); + myRestfulServer.registerInterceptor(patientIdInterceptor); + myPartitionSettings.setUnnamedPartitionMode(true); + } else if (partitioning.getRequest_tenant_partitioning_mode()) { + ourLog.info("Partitioning mode enabled in: Request tenant partitioning mode"); + myRestfulServer.registerInterceptor(new RequestTenantPartitionInterceptor()); + myRestfulServer.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); } + + myRestfulServer.registerProviders(myPartitionManagementProvider); } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java index 386c503504f..86b33f5334f 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.IDaoRegistry; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.ThreadPoolFactoryConfig; @@ -20,6 +21,7 @@ import ca.uhn.fhir.jpa.config.util.ResourceCountCacheUtil; import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; +import ca.uhn.fhir.jpa.dao.TransactionProcessor; import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl; import ca.uhn.fhir.jpa.dao.search.IHSearchSortHelper; import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc; @@ -29,8 +31,9 @@ import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor; import ca.uhn.fhir.jpa.ips.provider.IpsOperationProvider; import ca.uhn.fhir.jpa.model.config.SubscriptionSettings; +import ca.uhn.fhir.jpa.packages.AdditionalResourcesParser; +import ca.uhn.fhir.jpa.packages.IHapiPackageCacheManager; import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; -import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; import ca.uhn.fhir.jpa.provider.DaoRegistryResourceSupportedSvc; import ca.uhn.fhir.jpa.provider.DiffProvider; import ca.uhn.fhir.jpa.provider.IJpaSystemProvider; @@ -47,6 +50,7 @@ import ca.uhn.fhir.jpa.starter.annotations.OnCorsPresent; import ca.uhn.fhir.jpa.starter.annotations.OnImplementationGuidesPresent; import ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory; +import ca.uhn.fhir.jpa.starter.ig.ExtendedPackageInstallationSpec; import ca.uhn.fhir.jpa.starter.ig.IImplementationGuideOperationProvider; import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; @@ -55,6 +59,7 @@ import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.narrative2.NullNarrativeGenerator; import ca.uhn.fhir.rest.api.IResourceSupportedSvc; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.openapi.OpenApiInterceptor; import ca.uhn.fhir.rest.server.ApacheProxyAddressStrategy; import ca.uhn.fhir.rest.server.ETagSupportEnum; @@ -74,6 +79,7 @@ import ca.uhn.fhir.validation.ResultSeverityEnum; import com.google.common.base.Strings; import jakarta.persistence.EntityManagerFactory; +import org.hl7.fhir.instance.model.api.IBaseBundle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; @@ -93,11 +99,8 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.web.cors.CorsConfiguration; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.io.IOException; +import java.util.*; import javax.sql.DataSource; import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR; @@ -207,14 +210,18 @@ public LoggingInterceptor loggingInterceptor(AppProperties appProperties) { public IPackageInstallerSvc packageInstaller( AppProperties appProperties, IPackageInstallerSvc packageInstallerSvc, - Batch2JobRegisterer batch2JobRegisterer) { + Batch2JobRegisterer batch2JobRegisterer, + FhirContext fhirContext, + TransactionProcessor transactionProcessor, + IHapiPackageCacheManager iHapiPackageCacheManager) + throws IOException { batch2JobRegisterer.start(); if (appProperties.getImplementationGuides() != null) { - Map guides = appProperties.getImplementationGuides(); - for (Map.Entry guidesEntry : guides.entrySet()) { - PackageInstallationSpec packageInstallationSpec = guidesEntry.getValue(); + Map guides = appProperties.getImplementationGuides(); + for (Map.Entry guidesEntry : guides.entrySet()) { + ExtendedPackageInstallationSpec packageInstallationSpec = guidesEntry.getValue(); if (appProperties.getInstall_transitive_ig_dependencies()) { packageInstallationSpec @@ -223,7 +230,22 @@ public IPackageInstallerSvc packageInstaller( .addDependencyExclude("hl7.fhir.r4.core") .addDependencyExclude("hl7.fhir.r5.core"); } + packageInstallerSvc.install(packageInstallationSpec); + + Set extraResources = packageInstallationSpec.getAdditionalResourceFolders(); + packageInstallationSpec.setPackageContents(iHapiPackageCacheManager + .loadPackageContents(packageInstallationSpec.getName(), packageInstallationSpec.getVersion()) + .getBytes()); + + if (extraResources != null && !extraResources.isEmpty()) { + IBaseBundle transaction = AdditionalResourcesParser.bundleAdditionalResources( + extraResources, packageInstallationSpec, fhirContext); + transactionProcessor.transaction( + new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.defaultPartition()), + transaction, + false); + } } } return packageInstallerSvc; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ig/ExtendedPackageInstallationSpec.java b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ExtendedPackageInstallationSpec.java new file mode 100644 index 00000000000..73dc819779e --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/ig/ExtendedPackageInstallationSpec.java @@ -0,0 +1,24 @@ +package ca.uhn.fhir.jpa.starter.ig; + +import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.util.Set; + +public class ExtendedPackageInstallationSpec extends PackageInstallationSpec { + + public Set getAdditionalResourceFolders() { + return additionalResourceFolders; + } + + public void setAdditionalResourceFolders(Set additionalResourceFolders) { + this.additionalResourceFolders = additionalResourceFolders; + } + + @Schema( + description = + "If resources are being installed individually, this is list provides the resource types to install. By default, all conformance resources will be installed.") + @JsonProperty("additionalResourceFolders") + private Set additionalResourceFolders; +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index d4df357603c..b96cbe4a9d1 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -166,10 +166,14 @@ hapi: # reloadExisting: false # installMode: STORE_AND_INSTALL # example not from registry - # ips_1_0_0: - # packageUrl: https://build.fhir.org/ig/HL7/fhir-ips/package.tgz - # name: hl7.fhir.uv.ips - # version: 1.0.0 + # ips_1_0_0: + # packageUrl: https://costateixeira.github.io/smart-ips-pilgrimage-fulltest/package.tgz + # name: smart.who.int.ips-pilgrimage-test + # version: 0.1.0 + # installMode: STORE_AND_INSTALL + # additionalResourceFolders: + # - example + # - example2 # supported_resource_types: # - Patient # - Observation @@ -236,11 +240,14 @@ hapi: - https://unitsofmeasure.org/* - http://loinc.org/* - https://loinc.org/* - # partitioning: - # allow_references_across_partitions: false - # partitioning_include_in_search_hashes: false - # conditional_create_duplicate_identifiers_enabled: false - # request_tenant_partitioning_mode: true + #partitioning: + # allow_references_across_partitions: true + # partitioning_include_in_search_hashes: true + # conditional_create_duplicate_identifiers_enabled: true + # patient-id-partitioning-mode: true + # request-tenant-partitioning-mode: true + # database-partition-mode-enabled: true + # default-partition-id: 0 cors: allow_Credentials: true # These are allowed_origin patterns, see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/cors/CorsConfiguration.html#setAllowedOriginPatterns-java.util.List-