diff --git a/pom.xml b/pom.xml index 3d3fe06..218d077 100644 --- a/pom.xml +++ b/pom.xml @@ -126,7 +126,6 @@ hapi-fhir-testpage-overlay ${project.version} classes - + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + + ROOT 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 4d5edd1..888be76 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -70,6 +70,24 @@ public class AppProperties { private Integer search_coord_core_pool_size = 20; private Integer search_coord_max_pool_size = 100; private Integer search_coord_queue_capacity = 200; + private Boolean use_apache_address_strategy = false; + private Boolean use_apache_address_strategy_https = false; + + public Boolean getUse_apache_address_strategy() { + return use_apache_address_strategy; + } + + public void setUse_apache_address_strategy(Boolean use_apache_address_strategy) { + this.use_apache_address_strategy = use_apache_address_strategy; + } + + public Boolean getUse_apache_address_strategy_https() { + return use_apache_address_strategy_https; + } + + public void setUse_apache_address_strategy_https(Boolean use_apache_address_strategy_https) { + this.use_apache_address_strategy_https = use_apache_address_strategy_https; + } public Integer getDefer_indexing_for_codesystems_of_size() { return defer_indexing_for_codesystems_of_size; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java index c2c7841..8154714 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -27,6 +27,8 @@ import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.narrative2.NullNarrativeGenerator; import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; +import ca.uhn.fhir.rest.server.ApacheProxyAddressStrategy; +import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.*; import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor; @@ -239,6 +241,12 @@ public class BaseJpaRestfulServer extends RestfulServer { String serverAddress = appProperties.getServer_address(); if (!Strings.isNullOrEmpty(serverAddress)) { setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress)); + } else if (appProperties.getUse_apache_address_strategy()) { + boolean useHttps = appProperties.getUse_apache_address_strategy_https(); + setServerAddressStrategy(useHttps ? ApacheProxyAddressStrategy.forHttps() : + ApacheProxyAddressStrategy.forHttp()); + } else { + setServerAddressStrategy(new IncomingRequestAddressStrategy()); } /* diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java index 3df2c53..082e3b4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java @@ -1,170 +1,182 @@ package ca.uhn.fhir.jpa.starter; +import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer; import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder; -import org.apache.commons.lang3.StringUtils; -import org.elasticsearch.action.admin.indices.stats.IndexStats; +import org.apache.lucene.util.Version; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchBackendSettings; import org.hibernate.search.backend.elasticsearch.index.IndexStatus; import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings; +import org.hibernate.search.backend.lucene.lowlevel.directory.impl.LocalFileSystemDirectoryProvider; import org.hibernate.search.engine.cfg.BackendSettings; import org.hibernate.search.mapper.orm.automaticindexing.session.AutomaticIndexingSynchronizationStrategyNames; import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings; import org.hibernate.search.mapper.orm.schema.management.SchemaManagementStrategyName; +import org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy; +import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.PropertySource; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; +import java.util.*; public class EnvironmentHelper { - public static Properties getHibernateProperties(ConfigurableEnvironment environment) { - Properties properties = new Properties(); + public static Properties getHibernateProperties(ConfigurableEnvironment environment) { + Properties properties = new Properties(); + Map jpaProps = getPropertiesStartingWith(environment, "spring.jpa.properties"); + for (Map.Entry entry : jpaProps.entrySet()) { + String strippedKey = entry.getKey().replace("spring.jpa.properties.", ""); + properties.put(strippedKey, entry.getValue().toString()); + } - Map jpaProps = getPropertiesStartingWith(environment, "spring.jpa.properties"); - properties.putIfAbsent("hibernate.format_sql", "false"); - properties.putIfAbsent("hibernate.show_sql", "false"); - properties.putIfAbsent("hibernate.hbm2ddl.auto", "update"); - properties.putIfAbsent("hibernate.jdbc.batch_size", "20"); - properties.putIfAbsent("hibernate.cache.use_query_cache", "false"); - properties.putIfAbsent("hibernate.cache.use_second_level_cache", "false"); - properties.putIfAbsent("hibernate.cache.use_structured_entries", "false"); - properties.putIfAbsent("hibernate.cache.use_minimal_puts", "false"); + //Spring Boot Autoconfiguration defaults + properties.putIfAbsent(AvailableSettings.SCANNER, "org.hibernate.boot.archive.scan.internal.DisabledScanner"); + properties.putIfAbsent(AvailableSettings.IMPLICIT_NAMING_STRATEGY, SpringImplicitNamingStrategy.class.getName()); + properties.putIfAbsent(AvailableSettings.PHYSICAL_NAMING_STRATEGY, SpringPhysicalNamingStrategy.class.getName()); + //TODO The bean factory should be added as parameter but that requires that it can be injected from the entityManagerFactory bean from xBaseConfig + //properties.putIfAbsent(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory)); - if (jpaProps.getOrDefault("spring.jpa.properties.hibernate.search.enabled", "false").toString() == "true") { - properties.putIfAbsent(HibernateOrmMapperSettings.ENABLED, true); - properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), "local-filesystem"); - properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), "target/lucenefiles"); - properties.putIfAbsent(BackendSettings.backendKey(BackendSettings.TYPE), "lucene"); - properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), HapiLuceneAnalysisConfigurer.class.getName()); - properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), "LUCENE_CURRENT"); - } else { - properties.putIfAbsent(HibernateOrmMapperSettings.ENABLED, false); - } + //hapi-fhir-jpaserver-base "sensible defaults" + Map hapiJpaPropertyMap = new HapiFhirLocalContainerEntityManagerFactoryBean().getJpaPropertyMap(); + hapiJpaPropertyMap.forEach(properties::putIfAbsent); - for (Map.Entry entry : jpaProps.entrySet()) { - String strippedKey = entry.getKey().replace("spring.jpa.properties.", ""); - properties.put(strippedKey, entry.getValue().toString()); - } + //hapi-fhir-jpaserver-starter defaults + properties.putIfAbsent(AvailableSettings.FORMAT_SQL, false); + properties.putIfAbsent(AvailableSettings.SHOW_SQL, false); + properties.putIfAbsent(AvailableSettings.HBM2DDL_AUTO, "update"); + properties.putIfAbsent(AvailableSettings.STATEMENT_BATCH_SIZE, 20); + properties.putIfAbsent(AvailableSettings.USE_QUERY_CACHE, false); + properties.putIfAbsent(AvailableSettings.USE_SECOND_LEVEL_CACHE, false); + properties.putIfAbsent(AvailableSettings.USE_STRUCTURED_CACHE, false); + properties.putIfAbsent(AvailableSettings.USE_MINIMAL_PUTS, false); + //Hibernate Search defaults + properties.putIfAbsent(HibernateOrmMapperSettings.ENABLED, true); + if (Boolean.parseBoolean(String.valueOf(properties.get(HibernateOrmMapperSettings.ENABLED)))) { + properties.putIfAbsent(BackendSettings.backendKey(BackendSettings.TYPE), LuceneBackendSettings.TYPE_NAME); - if (environment.getProperty("elasticsearch.enabled", Boolean.class) != null - && environment.getProperty("elasticsearch.enabled", Boolean.class) == true) { - ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder(); - IndexStatus requiredIndexStatus = environment.getProperty("elasticsearch.required_index_status", IndexStatus.class); - if (requiredIndexStatus == null) { - builder.setRequiredIndexStatus(IndexStatus.YELLOW); - } else { - builder.setRequiredIndexStatus(requiredIndexStatus); - } + if (properties.get(BackendSettings.backendKey(BackendSettings.TYPE)).equals(LuceneBackendSettings.TYPE_NAME)) { + properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), LocalFileSystemDirectoryProvider.NAME); + properties.putIfAbsent(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_ROOT), "target/lucenefiles"); + properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), HapiLuceneAnalysisConfigurer.class.getName()); + properties.putIfAbsent(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), Version.LATEST); - builder.setRestUrl(getElasticsearchServerUrl(environment)); - builder.setUsername(getElasticsearchServerUsername(environment)); - builder.setPassword(getElasticsearchServerPassword(environment)); - builder.setProtocol(getElasticsearchServerProtocol(environment)); - SchemaManagementStrategyName indexSchemaManagementStrategy = environment.getProperty("elasticsearch.schema_management_strategy", SchemaManagementStrategyName.class); - if (indexSchemaManagementStrategy == null) { - builder.setIndexSchemaManagementStrategy(SchemaManagementStrategyName.CREATE); - } else { - builder.setIndexSchemaManagementStrategy(indexSchemaManagementStrategy); - } - // pretty_print_json_log: false - Boolean refreshAfterWrite = environment.getProperty("elasticsearch.debug.refresh_after_write", Boolean.class); - if (refreshAfterWrite == null || refreshAfterWrite == false) { - builder.setDebugIndexSyncStrategy(AutomaticIndexingSynchronizationStrategyNames.ASYNC); - } else { - builder.setDebugIndexSyncStrategy(AutomaticIndexingSynchronizationStrategyNames.READ_SYNC); - } - // pretty_print_json_log: false - Boolean prettyPrintJsonLog = environment.getProperty("elasticsearch.debug.pretty_print_json_log", Boolean.class); - if (prettyPrintJsonLog == null) { - builder.setDebugPrettyPrintJsonLog(false); - } else { - builder.setDebugPrettyPrintJsonLog(prettyPrintJsonLog); - } - builder.apply(properties); - } - return properties; - } + } else if (properties.get(BackendSettings.backendKey(BackendSettings.TYPE)).equals(ElasticsearchBackendSettings.TYPE_NAME)) { + ElasticsearchHibernatePropertiesBuilder builder = new ElasticsearchHibernatePropertiesBuilder(); + IndexStatus requiredIndexStatus = environment.getProperty("elasticsearch.required_index_status", IndexStatus.class); + builder.setRequiredIndexStatus(requireNonNullElse(requiredIndexStatus, IndexStatus.YELLOW)); + builder.setRestUrl(getElasticsearchServerUrl(environment)); + builder.setUsername(getElasticsearchServerUsername(environment)); + builder.setPassword(getElasticsearchServerPassword(environment)); + builder.setProtocol(getElasticsearchServerProtocol(environment)); + SchemaManagementStrategyName indexSchemaManagementStrategy = environment.getProperty("elasticsearch.schema_management_strategy", SchemaManagementStrategyName.class); + builder.setIndexSchemaManagementStrategy(requireNonNullElse(indexSchemaManagementStrategy, SchemaManagementStrategyName.CREATE)); + Boolean refreshAfterWrite = environment.getProperty("elasticsearch.debug.refresh_after_write", Boolean.class); + if (refreshAfterWrite == null || !refreshAfterWrite) { + builder.setDebugIndexSyncStrategy(AutomaticIndexingSynchronizationStrategyNames.ASYNC); + } else { + builder.setDebugIndexSyncStrategy(AutomaticIndexingSynchronizationStrategyNames.READ_SYNC); + } + builder.setDebugPrettyPrintJsonLog(requireNonNullElse(environment.getProperty("elasticsearch.debug.pretty_print_json_log", Boolean.class), false)); + builder.apply(properties); - public static String getElasticsearchServerUrl(ConfigurableEnvironment environment) { - return environment.getProperty("elasticsearch.rest_url", String.class); - } + } else { + throw new UnsupportedOperationException("Unsupported Hibernate Search backend: " + properties.get(BackendSettings.backendKey(BackendSettings.TYPE))); + } + } + + return properties; + } + + //TODO Removed when we're up on Java 11 + private static T requireNonNullElse(T obj, T defaultObj) { + return (obj != null) ? obj : requireNonNull(defaultObj, "defaultObj"); + } + + //TODO Removed when we're up on Java 11 + private static T requireNonNull(T obj, String message) { + if (obj == null) + throw new NullPointerException(message); + return obj; + } + + public static String getElasticsearchServerUrl(ConfigurableEnvironment environment) { + return environment.getProperty("elasticsearch.rest_url", String.class); + } public static String getElasticsearchServerProtocol(ConfigurableEnvironment environment) { return environment.getProperty("elasticsearch.protocol", String.class, "http"); } public static String getElasticsearchServerUsername(ConfigurableEnvironment environment) { - return environment.getProperty("elasticsearch.username"); - } + return environment.getProperty("elasticsearch.username"); + } - public static String getElasticsearchServerPassword(ConfigurableEnvironment environment) { - return environment.getProperty("elasticsearch.password"); - } + public static String getElasticsearchServerPassword(ConfigurableEnvironment environment) { + return environment.getProperty("elasticsearch.password"); + } - public static Boolean isElasticsearchEnabled(ConfigurableEnvironment environment) { - if (environment.getProperty("elasticsearch.enabled", Boolean.class) != null) { - return environment.getProperty("elasticsearch.enabled", Boolean.class); - } else { - return false; - } - } + public static Boolean isElasticsearchEnabled(ConfigurableEnvironment environment) { + if (environment.getProperty("elasticsearch.enabled", Boolean.class) != null) { + return environment.getProperty("elasticsearch.enabled", Boolean.class); + } else { + return false; + } + } - public static Map getPropertiesStartingWith(ConfigurableEnvironment aEnv, - String aKeyPrefix) { - Map result = new HashMap<>(); + public static Map getPropertiesStartingWith(ConfigurableEnvironment aEnv, + String aKeyPrefix) { + Map result = new HashMap<>(); - Map map = getAllProperties(aEnv); + Map map = getAllProperties(aEnv); - for (Map.Entry entry : map.entrySet()) { - String key = entry.getKey(); + for (Map.Entry entry : map.entrySet()) { + String key = entry.getKey(); - if (key.startsWith(aKeyPrefix)) { - result.put(key, entry.getValue()); - } - } + if (key.startsWith(aKeyPrefix)) { + result.put(key, entry.getValue()); + } + } - return result; - } + return result; + } - public static Map getAllProperties(ConfigurableEnvironment aEnv) { - Map result = new HashMap<>(); - aEnv.getPropertySources().forEach(ps -> addAll(result, getAllProperties(ps))); - return result; - } + public static Map getAllProperties(ConfigurableEnvironment aEnv) { + Map result = new HashMap<>(); + aEnv.getPropertySources().forEach(ps -> addAll(result, getAllProperties(ps))); + return result; + } - public static Map getAllProperties(PropertySource aPropSource) { - Map result = new HashMap<>(); + public static Map getAllProperties(PropertySource aPropSource) { + Map result = new HashMap<>(); - if (aPropSource instanceof CompositePropertySource) { - CompositePropertySource cps = (CompositePropertySource) aPropSource; - cps.getPropertySources().forEach(ps -> addAll(result, getAllProperties(ps))); - return result; - } + if (aPropSource instanceof CompositePropertySource) { + CompositePropertySource cps = (CompositePropertySource) aPropSource; + cps.getPropertySources().forEach(ps -> addAll(result, getAllProperties(ps))); + return result; + } - if (aPropSource instanceof EnumerablePropertySource) { - EnumerablePropertySource ps = (EnumerablePropertySource) aPropSource; - Arrays.asList(ps.getPropertyNames()).forEach(key -> result.put(key, ps.getProperty(key))); - return result; - } + if (aPropSource instanceof EnumerablePropertySource) { + EnumerablePropertySource ps = (EnumerablePropertySource) aPropSource; + Arrays.asList(ps.getPropertyNames()).forEach(key -> result.put(key, ps.getProperty(key))); + return result; + } - return result; + return result; - } + } - private static void addAll(Map aBase, Map aToBeAdded) { - for (Map.Entry entry : aToBeAdded.entrySet()) { - if (aBase.containsKey(entry.getKey())) { - continue; - } + private static void addAll(Map aBase, Map aToBeAdded) { + for (Map.Entry entry : aToBeAdded.entrySet()) { + if (aBase.containsKey(entry.getKey())) { + continue; + } - aBase.put(entry.getKey(), entry.getValue()); - } - } + aBase.put(entry.getKey(), entry.getValue()); + } + } } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 3416404..f46cacf 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -36,6 +36,12 @@ spring: hapi: fhir: + ### enable to use the ApacheProxyAddressStrategy which uses X-Forwarded-* headers + ### to determine the FHIR server address +# use_apache_address_strategy: false + ### forces the use of the https:// protocol for the returned server address. + ### alternatively, it may be set using the X-Forwarded-Proto header. +# use_apache_address_strategy_https: false ### enable to set the Server URL # server_address: http://hapi.fhir.org/baseR4 ### This is the FHIR version. Choose between, DSTU2, DSTU3, R4 or R5