Merge remote-tracking branch 'origin/master' into rel_8_3_tracking

This commit is contained in:
dotasek
2025-08-05 13:47:31 -04:00
6 changed files with 106 additions and 16 deletions

View File

@@ -6,7 +6,7 @@
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<hapi.fhir.jpa.server.starter.revision>1</hapi.fhir.jpa.server.starter.revision> <hapi.fhir.jpa.server.starter.revision>1</hapi.fhir.jpa.server.starter.revision>
<clinical-reasoning.version>3.22.0</clinical-reasoning.version> <clinical-reasoning.version>3.23.0</clinical-reasoning.version>
</properties> </properties>
<!-- one-liner to take you to the cloud with settings form the application.yaml file: --> <!-- one-liner to take you to the cloud with settings form the application.yaml file: -->

View File

@@ -19,6 +19,8 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
@ConfigurationProperties(prefix = "hapi.fhir") @ConfigurationProperties(prefix = "hapi.fhir")
@Configuration @Configuration
@EnableConfigurationProperties @EnableConfigurationProperties
@@ -46,6 +48,7 @@ public class AppProperties {
private Boolean mass_ingestion_mode_enabled = false; private Boolean mass_ingestion_mode_enabled = false;
private Boolean language_search_parameter_enabled = false; private Boolean language_search_parameter_enabled = false;
private Boolean dao_scheduling_enabled = true; private Boolean dao_scheduling_enabled = true;
private Boolean delete_enabled = true;
private Boolean delete_expunge_enabled = false; private Boolean delete_expunge_enabled = false;
private Boolean enable_index_missing_fields = false; private Boolean enable_index_missing_fields = false;
private Boolean enable_index_contained_resource = false; private Boolean enable_index_contained_resource = false;
@@ -111,6 +114,8 @@ public class AppProperties {
JpaStorageSettings.StoreMetaSourceInformationEnum.NONE; JpaStorageSettings.StoreMetaSourceInformationEnum.NONE;
private Map<String, RemoteSystem> remote_terminology_service = null; private Map<String, RemoteSystem> remote_terminology_service = null;
private Boolean match_url_cache_enabled = false;
private Boolean index_storage_optimized = false;
public List<String> getCustomInterceptorClasses() { public List<String> getCustomInterceptorClasses() {
return custom_interceptor_classes; return custom_interceptor_classes;
@@ -376,6 +381,14 @@ public class AppProperties {
return delete_expunge_enabled; return delete_expunge_enabled;
} }
public boolean getDelete_enabled() {
return defaultIfNull(delete_enabled, true);
}
public void setDelete_enabled(boolean theDelete_enabled) {
delete_enabled = theDelete_enabled;
}
public void setDelete_expunge_enabled(Boolean delete_expunge_enabled) { public void setDelete_expunge_enabled(Boolean delete_expunge_enabled) {
this.delete_expunge_enabled = delete_expunge_enabled; this.delete_expunge_enabled = delete_expunge_enabled;
} }
@@ -745,6 +758,22 @@ public class AppProperties {
this.remote_terminology_service = remote_terminology_service; this.remote_terminology_service = remote_terminology_service;
} }
public boolean getMatch_url_cache_enabled() {
return defaultIfNull(match_url_cache_enabled, false);
}
public void setMatch_url_cache_enabled(boolean theMatchUrlCacheEnabled) {
match_url_cache_enabled = theMatchUrlCacheEnabled;
}
public boolean getIndex_storage_optimized() {
return defaultIfNull(index_storage_optimized, false);
}
public void setIndex_storage_optimized(boolean theIndex_storage_optimized) {
index_storage_optimized = theIndex_storage_optimized;
}
public JpaStorageSettings.StoreMetaSourceInformationEnum getStore_meta_source_information() { public JpaStorageSettings.StoreMetaSourceInformationEnum getStore_meta_source_information() {
return store_meta_source_information; return store_meta_source_information;
} }

View File

@@ -16,6 +16,8 @@ import ca.uhn.fhir.rest.server.mail.MailConfig;
import ca.uhn.fhir.rest.server.mail.MailSvc; import ca.uhn.fhir.rest.server.mail.MailSvc;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import org.hl7.fhir.r4.model.Bundle.BundleType; import org.hl7.fhir.r4.model.Bundle.BundleType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@@ -36,7 +38,7 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
@EnableTransactionManagement @EnableTransactionManagement
public class FhirServerConfigCommon { public class FhirServerConfigCommon {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class); private static final Logger ourLog = LoggerFactory.getLogger(FhirServerConfigCommon.class);
public FhirServerConfigCommon(AppProperties appProperties) { public FhirServerConfigCommon(AppProperties appProperties) {
ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny") ourLog.info("Server configured to " + (appProperties.getAllow_contains_searches() ? "allow" : "deny")
@@ -176,6 +178,9 @@ public class FhirServerConfigCommon {
jpaStorageSettings.setAllowMultipleDelete(appProperties.getAllow_multiple_delete()); jpaStorageSettings.setAllowMultipleDelete(appProperties.getAllow_multiple_delete());
jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references()); jpaStorageSettings.setAllowExternalReferences(appProperties.getAllow_external_references());
jpaStorageSettings.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled()); jpaStorageSettings.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled());
jpaStorageSettings.setIndexStorageOptimized(appProperties.getIndex_storage_optimized());
jpaStorageSettings.setMatchUrlCacheEnabled(appProperties.getMatch_url_cache_enabled());
jpaStorageSettings.setDeleteEnabled(appProperties.getDelete_enabled());
jpaStorageSettings.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled()); jpaStorageSettings.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled());
jpaStorageSettings.setExpungeEnabled(appProperties.getExpunge_enabled()); jpaStorageSettings.setExpungeEnabled(appProperties.getExpunge_enabled());
jpaStorageSettings.setLanguageSearchParameterEnabled(appProperties.getLanguage_search_parameter_enabled()); jpaStorageSettings.setLanguageSearchParameterEnabled(appProperties.getLanguage_search_parameter_enabled());
@@ -291,6 +296,19 @@ public class FhirServerConfigCommon {
} }
retVal.setConditionalCreateDuplicateIdentifiersEnabled( retVal.setConditionalCreateDuplicateIdentifiersEnabled(
appProperties.getPartitioning().getConditional_create_duplicate_identifiers_enabled()); appProperties.getPartitioning().getConditional_create_duplicate_identifiers_enabled());
ourLog.info(
"""
Partitioning is enabled on this server. Settings:
* Database Partition Mode Enabled: {}
* Default Partition ID : {}
* Cross-Partition References : {}""",
databasePartitionModeEnabled,
defaultPartitionId,
retVal.getAllowReferencesAcrossPartitions());
} else {
ourLog.info("Partitioning is not enabled on this server");
} }
return retVal; return retVal;

View File

@@ -2,31 +2,45 @@ package ca.uhn.fhir.jpa.starter.util;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider; import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
import ca.uhn.fhir.util.ReflectionUtil;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver; import org.hibernate.engine.jdbc.dialect.internal.StandardDialectResolver;
import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter; import org.hibernate.engine.jdbc.dialect.spi.DatabaseMetaDataDialectResolutionInfoAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import javax.sql.DataSource; import javax.sql.DataSource;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class JpaHibernatePropertiesProvider extends HibernatePropertiesProvider { public class JpaHibernatePropertiesProvider extends HibernatePropertiesProvider {
private static final Logger ourLog = LoggerFactory.getLogger(JpaHibernatePropertiesProvider.class);
private final Dialect dialect; private final Dialect myDialect;
public JpaHibernatePropertiesProvider(LocalContainerEntityManagerFactoryBean myEntityManagerFactory) { public JpaHibernatePropertiesProvider(LocalContainerEntityManagerFactoryBean theEntityManagerFactory) {
DataSource connection = myEntityManagerFactory.getDataSource(); String dialectClass =
try (Connection dbConnection = connection.getConnection()) { (String) theEntityManagerFactory.getJpaPropertyMap().get("hibernate.dialect");
dialect = new StandardDialectResolver() if (isNotBlank(dialectClass)) {
.resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(dbConnection.getMetaData())); myDialect = ReflectionUtil.newInstanceOrReturnNull(dialectClass, Dialect.class);
} catch (SQLException sqlException) { } else {
throw new ConfigurationException(sqlException.getMessage(), sqlException); ourLog.warn(
"'hibernate.dialect' not set in application configuration! Please explicitly specify a valid HAPI FHIR hibernate dialect.");
DataSource connection = theEntityManagerFactory.getDataSource();
try (Connection dbConnection = connection.getConnection()) {
myDialect = new StandardDialectResolver()
.resolveDialect(new DatabaseMetaDataDialectResolutionInfoAdapter(dbConnection.getMetaData()));
} catch (SQLException sqlException) {
throw new ConfigurationException(sqlException.getMessage(), sqlException);
}
} }
} }
@Override @Override
public Dialect getDialect() { public Dialect getDialect() {
return dialect; return myDialect;
} }
} }

View File

@@ -190,8 +190,12 @@ hapi:
# default_encoding: JSON # default_encoding: JSON
# default_pretty_print: true # default_pretty_print: true
# default_page_size: 20 # default_page_size: 20
# delete_enabled: true
# delete_expunge_enabled: true # delete_expunge_enabled: true
# match_url_cache_enabled: false
# enable_repository_validating_interceptor: true # enable_repository_validating_interceptor: true
### Reduce the size used by search indexes by not tagging every row with the resource type and parameter name (this setting makes manual inspection of the database more difficult, but does not impact HAPI FHIR functionality in any way)
# index_storage_optimized: false
# enable_index_missing_fields: false # enable_index_missing_fields: false
# enable_index_of_type: true # enable_index_of_type: true
# enable_index_contained_resource: false # enable_index_contained_resource: false
@@ -237,11 +241,26 @@ hapi:
- https://unitsofmeasure.org/* - https://unitsofmeasure.org/*
- http://loinc.org/* - http://loinc.org/*
- https://loinc.org/* - https://loinc.org/*
# partitioning:
# allow_references_across_partitions: false ### Uncomment the following section, and any sub-properties you need in order to enable
# partitioning_include_in_search_hashes: false ### partitioning support on this server.
# conditional_create_duplicate_identifiers_enabled: false #partitioning:
# request_tenant_partitioning_mode: true # allow_references_across_partitions: false
# partitioning_include_in_search_hashes: false
# default_partition_id: 0
### Enable the following setting to enable Database Partitioning Mode
### See: https://hapifhir.io/hapi-fhir/docs/server_jpa_partitioning/db_partition_mode.html
# database_partition_mode_enabled: true
### Partition Style: Partitioning requires a partition interceptor which helps the server
### select which partition(s) should be accessed for a given request. You can supply your
### own interceptor (see https://hapifhir.io/hapi-fhir/docs/server_jpa_partitioning/partitioning.html#partition-interceptors )
### but the following setting can also be used to use a built-in form.
### Patient ID Partitioning Mode uses the patient/subject ID to determine the partition
# patient_id_partitioning_mode: true
### Request tenant mode can be used for a multi-tenancy setup where the request path is
### expected to have an additional path element, e.g. GET http://example.com/fhir/TENANT-ID/Patient/A
# request_tenant_partitioning_mode: false
cors: cors:
allow_Credentials: true 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- # 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-

View File

@@ -1,6 +1,8 @@
package ca.uhn.fhir.jpa.starter; package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect;
import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig; import ca.uhn.fhir.jpa.searchparam.config.NicknameServiceConfig;
import ca.uhn.fhir.jpa.starter.cr.CrProperties; import ca.uhn.fhir.jpa.starter.cr.CrProperties;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@@ -80,6 +82,9 @@ class ExampleServerR4IT implements IServerSupport {
@Autowired @Autowired
private CrProperties crProperties; private CrProperties crProperties;
@Autowired
private HibernatePropertiesProvider myHibernatePropertiesProvider;
@LocalServerPort @LocalServerPort
private int port; private int port;
@@ -367,6 +372,11 @@ class ExampleServerR4IT implements IServerSupport {
Parameters localResult = ourClient.operation().onType(CodeSystem.class).named("$validate-code").withParameter(Parameters.class, "url", new UrlType(testCodeSystem)).andParameter("coding", new Coding(testCodeSystem, "yes", null)).execute(); Parameters localResult = ourClient.operation().onType(CodeSystem.class).named("$validate-code").withParameter(Parameters.class, "url", new UrlType(testCodeSystem)).andParameter("coding", new Coding(testCodeSystem, "yes", null)).execute();
} }
@Test
public void testHibernatePropertiesProvider_GetDialect() {
assertEquals(HapiFhirH2Dialect.class, myHibernatePropertiesProvider.getDialect().getClass());
}
@BeforeEach @BeforeEach
void beforeEach() { void beforeEach() {