From 437d22e460322fe9e9b5d7121378184886457459 Mon Sep 17 00:00:00 2001 From: jvi Date: Mon, 7 Sep 2020 13:47:09 +0200 Subject: [PATCH] All but tests works --- pom.xml | 11 +- .../java/ca/uhn/fhir/jpa/empi/EmpiConfig.java | 5 +- .../uhn/fhir/jpa/starter/AppProperties.java | 217 ++++++++++++++++-- .../ca/uhn/fhir/jpa/starter/Application.java | 2 +- .../jpa/starter/BaseJpaRestfulServer.java | 181 +++++++-------- .../jpa/starter/FhirServerConfigCommon.java | 110 ++++----- .../fhir/jpa/starter/JpaRestfulServer.java | 11 +- src/main/resources/application.yaml | 133 ++++++----- .../jpa/starter/ExampleServerDstu2IT.java | 9 +- 9 files changed, 436 insertions(+), 243 deletions(-) diff --git a/pom.xml b/pom.xml index 661ee01..94d87c0 100644 --- a/pom.xml +++ b/pom.xml @@ -18,6 +18,12 @@ hapi-fhir-jpaserver-starter + + 11 + ${java.version} + ${java.version} + + 3.6.0 @@ -293,8 +299,6 @@ - - org.springframework.boot @@ -374,6 +378,9 @@ src/main/webapp/WEB-INF/web.xml + + false + diff --git a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java b/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java index b1f74a2..d54dfea 100644 --- a/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/empi/EmpiConfig.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.empi; import ca.uhn.fhir.empi.api.IEmpiSettings; import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator; import ca.uhn.fhir.empi.rules.config.EmpiSettings; +import ca.uhn.fhir.jpa.starter.AppProperties; import ca.uhn.fhir.jpa.starter.HapiProperties; import com.google.common.base.Charsets; import org.apache.commons.io.IOUtils; @@ -21,11 +22,11 @@ import java.io.IOException; public class EmpiConfig { @Bean - IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException { + IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator, AppProperties appProperties) throws IOException { DefaultResourceLoader resourceLoader = new DefaultResourceLoader(); Resource resource = resourceLoader.getResource("empi-rules.json"); String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8); - return new EmpiSettings(theEmpiRuleValidator).setEnabled(HapiProperties.getEmpiEnabled()).setScriptText(json); + return new EmpiSettings(theEmpiRuleValidator).setEnabled(appProperties.getEmpi_enabled()).setScriptText(json); } } 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 a3cd5d2..b8613b4 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -4,8 +4,11 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.api.config.DaoConfig.ClientIdStrategyEnum; import ca.uhn.fhir.rest.api.EncodingEnum; + import java.util.ArrayList; import java.util.List; + +import org.hl7.fhir.r4.model.Bundle; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; @@ -16,7 +19,7 @@ import org.springframework.context.annotation.Configuration; @EnableConfigurationProperties public class AppProperties { - + private Boolean empi_enabled = false; private Boolean allow_cascading_deletes = false; private Boolean allow_contains_searches = true; private Boolean allow_external_references = false; @@ -24,7 +27,6 @@ public class AppProperties { private Boolean allow_override_default_search_params = true; private Boolean allow_placeholder_references = true; private Boolean auto_create_placeholder_reference_targets = true; - private Integer default_page_size = 20; private Boolean enable_index_missing_fields = false; private Boolean enforce_referential_integrity_on_delete = true; private Boolean enforce_referential_integrity_on_write = true; @@ -35,23 +37,79 @@ public class AppProperties { private Boolean graphql_enabled = true; private Boolean binary_storage_enabled = true; private Boolean bulk_export_enabled = true; - private Integer max_binary_size = 104857600; + private Boolean default_pretty_print = true; + private Integer default_page_size = 20; + private Integer max_binary_size = null; private Integer max_page_size = Integer.MAX_VALUE; - private Integer retain_cached_searches_mins = 60; + private Long retain_cached_searches_mins = 60L; private Long reuse_cached_search_results_millis = 60000L; + private String server_address = null; private EncodingEnum default_encoding = EncodingEnum.JSON; private FhirVersionEnum fhir_version = FhirVersionEnum.R4; private ClientIdStrategyEnum client_id_strategy = ClientIdStrategyEnum.ALPHANUMERIC; + private List supported_resource_types = new ArrayList<>(); + private List allowed_bundle_types = null; private Validation validation = new Validation(); private List tester = new ArrayList<>(); private Logger logger = new Logger(); private Subscription subscription = new Subscription(); + private Cors cors = null; + private Partitioning partitioning = null; + + public Partitioning getPartitioning() { + return partitioning; + } + + public void setPartitioning(Partitioning partitioning) { + this.partitioning = partitioning; + } + + public Boolean getEmpi_enabled() { + return empi_enabled; + } + + public void setEmpi_enabled(Boolean empi_enabled) { + this.empi_enabled = empi_enabled; + } + + + public Cors getCors() { + return cors; + } + + public void setCors(Cors cors) { + this.cors = cors; + } + + public List getAllowed_bundle_types() { + return allowed_bundle_types; + } + + public void setAllowed_bundle_types(List allowed_bundle_types) { + this.allowed_bundle_types = allowed_bundle_types; + } + + public String getServer_address() { + return server_address; + } + + public void setServer_address(String server_address) { + this.server_address = server_address; + } public Subscription getSubscription() { return subscription; } + public Boolean getDefault_pretty_print() { + return default_pretty_print; + } + + public void setDefault_pretty_print(Boolean default_pretty_print) { + this.default_pretty_print = default_pretty_print; + } + public void setSubscription(Subscription subscription) { this.subscription = subscription; } @@ -64,6 +122,13 @@ public class AppProperties { this.validation = validation; } + public List getSupported_resource_types() { + return supported_resource_types; + } + + public void setSupported_resource_types(List supported_resource_types) { + this.supported_resource_types = supported_resource_types; + } public Logger getLogger() { return logger; @@ -262,11 +327,11 @@ public class AppProperties { this.max_page_size = max_page_size; } - public Integer getRetain_cached_searches_mins() { + public Long getRetain_cached_searches_mins() { return retain_cached_searches_mins; } - public void setRetain_cached_searches_mins(Integer retain_cached_searches_mins) { + public void setRetain_cached_searches_mins(Long retain_cached_searches_mins) { this.retain_cached_searches_mins = retain_cached_searches_mins; } @@ -286,6 +351,29 @@ public class AppProperties { this.tester = tester; } + public static class Cors { + private Boolean allow_Credentials = true; + private List allowed_origin = new ArrayList<>();//List.of("*"); + + public List getAllowed_origin() { + return allowed_origin; + } + + public void setAllowed_origin(List allowed_origin) { + this.allowed_origin = allowed_origin; + } + + public Boolean getAllow_Credentials() { + return allow_Credentials; + } + + public void setAllow_Credentials(Boolean allow_Credentials) { + this.allow_Credentials = allow_Credentials; + } + + + } + public static class Logger { private String name = "fhirtest.access"; @@ -398,16 +486,22 @@ public class AppProperties { } } + public static class Partitioning { + + private Boolean partitioning_include_in_search_hashes = false; + + + public Boolean getPartitioning_include_in_search_hashes() { + return partitioning_include_in_search_hashes; + } + + public void setPartitioning_include_in_search_hashes(Boolean partitioning_include_in_search_hashes) { + this.partitioning_include_in_search_hashes = partitioning_include_in_search_hashes; + } + } + public static class Subscription { - public Boolean getEmail_enabled() { - return email_enabled; - } - - public void setEmail_enabled(Boolean email_enabled) { - this.email_enabled = email_enabled; - } - public Boolean getResthook_enabled() { return resthook_enabled; } @@ -424,8 +518,101 @@ public class AppProperties { this.websocket_enabled = websocket_enabled; } - private Boolean email_enabled = false; private Boolean resthook_enabled = false; private Boolean websocket_enabled = false; + private Email email = null; + + public Email getEmail() { + return email; + } + + public void setEmail(Email email) { + this.email = email; + } + + + public static class Email { + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public Boolean getAuth() { + return auth; + } + + public void setAuth(Boolean auth) { + this.auth = auth; + } + + public Boolean getStartTlsEnable() { + return startTlsEnable; + } + + public void setStartTlsEnable(Boolean startTlsEnable) { + this.startTlsEnable = startTlsEnable; + } + + public Boolean getStartTlsRequired() { + return startTlsRequired; + } + + public void setStartTlsRequired(Boolean startTlsRequired) { + this.startTlsRequired = startTlsRequired; + } + + public Boolean getQuitWait() { + return quitWait; + } + + public void setQuitWait(Boolean quitWait) { + this.quitWait = quitWait; + } + + private String from; + private String host; + private Integer port = 25; + private String username; + private String password; + private Boolean auth = false; + private Boolean startTlsEnable = false; + private Boolean startTlsRequired = false; + private Boolean quitWait = false; + } } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java index be03b5f..e09c995 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -34,7 +34,7 @@ public class Application { ServletRegistrationBean registrationBean = new ServletRegistrationBean(); registrationBean.setServlet(dispatcherServlet); - registrationBean.addUrlMappings("/*"); + registrationBean.addUrlMappings("/hapi-fhir-jpaserver/*", "/*"); registrationBean.setLoadOnStartup(1); return registrationBean; 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 e359e3d..168a06b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.IInterceptorService; -import ca.uhn.fhir.jpa.api.IDaoRegistry; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; @@ -12,56 +11,36 @@ import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor; import ca.uhn.fhir.jpa.bulk.provider.BulkDataExportProvider; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.partition.PartitionManagementProvider; -import ca.uhn.fhir.jpa.provider.GraphQLProvider; -import ca.uhn.fhir.jpa.provider.IJpaSystemProvider; -import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; -import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; -import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.*; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; -import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; -import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5; -import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry; import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; -import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; +import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; -import ca.uhn.fhir.rest.server.interceptor.FhirPathFilterInterceptor; -import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; -import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; -import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; -import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; +import ca.uhn.fhir.rest.server.interceptor.*; import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor; import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory; import ca.uhn.fhir.rest.server.tenant.UrlBaseTenantIdentificationStrategy; import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.ResultSeverityEnum; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.TreeSet; -import javax.servlet.ServletException; -import org.hl7.fhir.dstu3.model.Bundle; -import org.hl7.fhir.dstu3.model.Meta; +import com.google.common.base.Strings; import org.hl7.fhir.r4.model.Bundle.BundleType; -import org.springframework.aop.framework.Advised; -import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationContext; import org.springframework.http.HttpHeaders; import org.springframework.web.cors.CorsConfiguration; +import javax.servlet.ServletException; +import java.util.*; +import java.util.stream.Collectors; + public class BaseJpaRestfulServer extends RestfulServer { - AppProperties hapiProperties; @Autowired DaoRegistry daoRegistry; @@ -81,8 +60,35 @@ public class BaseJpaRestfulServer extends RestfulServer { @Autowired IJpaSystemProvider jpaSystemProvider; - public BaseJpaRestfulServer(AppProperties appProperties) { - this.hapiProperties = appProperties; + @Autowired + IInterceptorBroadcaster interceptorBroadcaster; + + @Autowired + DatabaseBackedPagingProvider databaseBackedPagingProvider; + + @Autowired + IInterceptorService interceptorService; + + @Autowired + IValidatorModule validatorModule; + + @Autowired + Optional graphQLProvider; + + @Autowired + BulkDataExportProvider bulkDataExportProvider; + + @Autowired + PartitionManagementProvider partitionManagementProvider; + + @Autowired + BinaryStorageInterceptor binaryStorageInterceptor; + + @Autowired + AppProperties appProperties; + + public BaseJpaRestfulServer() { + } private static final long serialVersionUID = 1L; @@ -92,26 +98,18 @@ public class BaseJpaRestfulServer extends RestfulServer { protected void initialize() throws ServletException { super.initialize(); - ApplicationContext appCtx = (ApplicationContext) getServletContext() - .getAttribute("org.springframework.web.context.WebApplicationContext.ROOT"); - /* * Create a FhirContext object that uses the version of FHIR * specified in the properties file. */ // Customize supported resource types - Set supportedResourceTypes = HapiProperties.getSupportedResourceTypes(); + List supportedResourceTypes = appProperties.getSupported_resource_types(); if (!supportedResourceTypes.isEmpty() && !supportedResourceTypes.contains("SearchParameter")) { supportedResourceTypes.add("SearchParameter"); - } - - if (!supportedResourceTypes.isEmpty()) { daoRegistry.setSupportedResourceTypes(supportedResourceTypes); } - - setFhirContext(fhirSystemDao.getContext()); registerProviders(resourceProviders.createProviders()); registerProvider(jpaSystemProvider); @@ -160,7 +158,10 @@ public class BaseJpaRestfulServer extends RestfulServer { /* * ETag Support */ - setETagSupport(HapiProperties.getEtagSupport()); + + if(appProperties.getEtag_support_enabled() == false) + setETagSupport(ETagSupportEnum.DISABLED); + /* * This server tries to dynamically generate narratives @@ -171,12 +172,12 @@ public class BaseJpaRestfulServer extends RestfulServer { /* * Default to JSON and pretty printing */ - setDefaultPrettyPrint(HapiProperties.getDefaultPrettyPrint()); + setDefaultPrettyPrint(appProperties.getDefault_pretty_print()); /* * Default encoding */ - setDefaultResponseEncoding(HapiProperties.getDefaultEncoding()); + setDefaultResponseEncoding(appProperties.getDefault_encoding()); /* * This configures the server to page search results to and from @@ -185,7 +186,7 @@ public class BaseJpaRestfulServer extends RestfulServer { * but makes the server much more scalable. */ - setPagingProvider(appCtx.getBean(DatabaseBackedPagingProvider.class)); + setPagingProvider(databaseBackedPagingProvider); /* * This interceptor formats the output using nice colourful @@ -195,7 +196,7 @@ public class BaseJpaRestfulServer extends RestfulServer { ResponseHighlighterInterceptor responseHighlighterInterceptor = new ResponseHighlighterInterceptor(); this.registerInterceptor(responseHighlighterInterceptor); - if (HapiProperties.isFhirPathFilterInterceptorEnabled()) { + if (appProperties.getFhirpath_interceptor_enabled()) { registerInterceptor(new FhirPathFilterInterceptor()); } @@ -203,10 +204,10 @@ public class BaseJpaRestfulServer extends RestfulServer { * Add some logging for each request */ LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - loggingInterceptor.setLoggerName(HapiProperties.getLoggerName()); - loggingInterceptor.setMessageFormat(HapiProperties.getLoggerFormat()); - loggingInterceptor.setErrorMessageFormat(HapiProperties.getLoggerErrorFormat()); - loggingInterceptor.setLogExceptions(HapiProperties.getLoggerLogExceptions()); + loggingInterceptor.setLoggerName(appProperties.getLogger().getName()); + loggingInterceptor.setMessageFormat(appProperties.getLogger().getFormat()); + loggingInterceptor.setErrorMessageFormat(appProperties.getLogger().getError_format()); + loggingInterceptor.setLogExceptions(appProperties.getLogger().getLog_exceptions()); this.registerInterceptor(loggingInterceptor); /* @@ -215,8 +216,8 @@ public class BaseJpaRestfulServer extends RestfulServer { * this doesn't always work. If you are setting links in your search bundles that * just refer to "localhost", you might want to use a server address strategy: */ - String serverAddress = HapiProperties.getServerAddress(); - if (serverAddress != null && serverAddress.length() > 0) { + String serverAddress = appProperties.getServer_address(); + if (!Strings.isNullOrEmpty(serverAddress)) { setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress)); } @@ -228,21 +229,21 @@ public class BaseJpaRestfulServer extends RestfulServer { * with this feature. */ if (false) { // <-- DISABLED RIGHT NOW - registerProvider(appCtx.getBean(TerminologyUploaderProvider.class)); + //registerProvider(appCtx.getBean(TerminologyUploaderProvider.class)); } // If you want to enable the $trigger-subscription operation to allow // manual triggering of a subscription delivery, enable this provider if (false) { // <-- DISABLED RIGHT NOW - SubscriptionTriggeringProvider retriggeringProvider = appCtx + /* SubscriptionTriggeringProvider retriggeringProvider = appCtx .getBean(SubscriptionTriggeringProvider.class); - registerProvider(retriggeringProvider); + registerProvider(retriggeringProvider);*/ } // Define your CORS configuration. This is an example // showing a typical setup. You should customize this // to your specific needs - if (HapiProperties.getCorsEnabled()) { + if (appProperties.getCors() != null) { CorsConfiguration config = new CorsConfiguration(); config.addAllowedHeader(HttpHeaders.ORIGIN); config.addAllowedHeader(HttpHeaders.ACCEPT); @@ -252,17 +253,15 @@ public class BaseJpaRestfulServer extends RestfulServer { config.addAllowedHeader("x-fhir-starter"); config.addAllowedHeader("X-Requested-With"); config.addAllowedHeader("Prefer"); - String allAllowedCORSOrigins = HapiProperties.getCorsAllowedOrigin(); - Arrays.stream(allAllowedCORSOrigins.split(",")).forEach(o -> { - config.addAllowedOrigin(o); - }); - config.addAllowedOrigin(HapiProperties.getCorsAllowedOrigin()); + List allAllowedCORSOrigins = appProperties.getCors().getAllowed_origin(); + allAllowedCORSOrigins.forEach(config::addAllowedOrigin); + config.addExposedHeader("Location"); config.addExposedHeader("Content-Location"); config.setAllowedMethods( Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD")); - config.setAllowCredentials(HapiProperties.getCorsAllowedCredentials()); + config.setAllowCredentials(appProperties.getCors().getAllow_Credentials()); // Create the interceptor and register it CorsInterceptor interceptor = new CorsInterceptor(config); @@ -271,40 +270,35 @@ public class BaseJpaRestfulServer extends RestfulServer { // If subscriptions are enabled, we want to register the interceptor that // will activate them and match results against them - if (HapiProperties.getSubscriptionWebsocketEnabled() || - HapiProperties.getSubscriptionEmailEnabled() || - HapiProperties.getSubscriptionRestHookEnabled()) { + if (appProperties.getSubscription() != null) { // Subscription debug logging - IInterceptorService interceptorService = appCtx.getBean(IInterceptorService.class); interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor()); } // Cascading deletes - DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class); - IInterceptorBroadcaster interceptorBroadcaster = appCtx.getBean(IInterceptorBroadcaster.class); - if (HapiProperties.getAllowCascadingDeletes()) { + + + if (appProperties.getAllow_cascading_deletes()) { CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(ctx, daoRegistry, interceptorBroadcaster); getInterceptorService().registerInterceptor(cascadingDeleteInterceptor); } // Binary Storage - if (HapiProperties.isBinaryStorageEnabled()) { - BinaryStorageInterceptor binaryStorageInterceptor = appCtx - .getBean(BinaryStorageInterceptor.class); + if (appProperties.getBinary_storage_enabled()) { getInterceptorService().registerInterceptor(binaryStorageInterceptor); } // Validation - IValidatorModule validatorModule = appCtx.getBean(IValidatorModule.class); + if (validatorModule != null) { - if (HapiProperties.getValidateRequestsEnabled()) { + if (appProperties.getValidation().getRequests_enabled()) { RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor(); interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); interceptor.setValidatorModules(Collections.singletonList(validatorModule)); registerInterceptor(interceptor); } - if (HapiProperties.getValidateResponsesEnabled()) { + if (appProperties.getValidation().getResponses_enabled()) { ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor(); interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); interceptor.setValidatorModules(Collections.singletonList(validatorModule)); @@ -313,52 +307,33 @@ public class BaseJpaRestfulServer extends RestfulServer { } // GraphQL - if (HapiProperties.getGraphqlEnabled()) { + if (appProperties.getGraphql_enabled()) { if (fhirVersion.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { - registerProvider(appCtx.getBean(GraphQLProvider.class)); + registerProvider(graphQLProvider.get()); } } - if (!HapiProperties.getAllowedBundleTypes().isEmpty()) { - String allowedBundleTypesString = HapiProperties.getAllowedBundleTypes(); - Set allowedBundleTypes = new HashSet<>(); - Arrays.stream(allowedBundleTypesString.split(",")).forEach(o -> { - BundleType type = BundleType.valueOf(o); - allowedBundleTypes.add(type.toCode()); - }); - DaoConfig config = daoConfig; - config.setBundleTypesAllowedForStorage( - Collections.unmodifiableSet(new TreeSet<>(allowedBundleTypes))); + if (appProperties.getAllowed_bundle_types() != null) { + daoConfig.setBundleTypesAllowedForStorage(appProperties.getAllowed_bundle_types().stream().map(BundleType::toCode).collect(Collectors.toSet())); } // Bulk Export - if (HapiProperties.getBulkExportEnabled()) { - registerProvider(appCtx.getBean(BulkDataExportProvider.class)); + if (appProperties.getBulk_export_enabled()) { + registerProvider(bulkDataExportProvider); } // Partitioning - if (HapiProperties.getPartitioningMultitenancyEnabled()) { + if (appProperties.getPartitioning() != null) { registerInterceptor(new RequestTenantPartitionInterceptor()); setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); - registerProviders(appCtx.getBean(PartitionManagementProvider.class)); + registerProviders(partitionManagementProvider); } - if (HapiProperties.getClientIdStrategy() == DaoConfig.ClientIdStrategyEnum.ANY) { + if (appProperties.getClient_id_strategy() == DaoConfig.ClientIdStrategyEnum.ANY) { daoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID); - daoConfig.setResourceClientIdStrategy(HapiProperties.getClientIdStrategy()); + daoConfig.setResourceClientIdStrategy(appProperties.getClient_id_strategy()); } } - protected T getBeanWithoutProxy(Object bean) { - - if (AopUtils.isAopProxy(bean) && bean instanceof Advised) { - try { - return (T) ((Advised) bean).getTargetSource().getTarget(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return (T)bean; - } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java index d81361b..28aab92 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory; import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender; import ca.uhn.fhir.jpa.subscription.match.deliver.email.JavaMailEmailSender; +import com.google.common.base.Strings; import org.apache.commons.dbcp2.BasicDataSource; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.beans.factory.annotation.Autowired; @@ -20,6 +21,7 @@ import org.thymeleaf.util.Validate; import java.lang.reflect.InvocationTargetException; import java.sql.Driver; +import java.util.Optional; /** * This is the primary configuration file for the example server @@ -30,23 +32,6 @@ public class FhirServerConfigCommon { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirServerConfigCommon.class); - - - private Boolean subscriptionRestHookEnabled = HapiProperties.getSubscriptionRestHookEnabled(); - private Boolean subscriptionEmailEnabled = HapiProperties.getSubscriptionEmailEnabled(); - - private String emailFrom = HapiProperties.getEmailFrom(); - private Boolean emailEnabled = HapiProperties.getEmailEnabled(); - private String emailHost = HapiProperties.getEmailHost(); - private Integer emailPort = HapiProperties.getEmailPort(); - private String emailUsername = HapiProperties.getEmailUsername(); - private String emailPassword = HapiProperties.getEmailPassword(); - private Boolean emailAuth = HapiProperties.getEmailAuth(); - private Boolean emailStartTlsEnable = HapiProperties.getEmailStartTlsEnable(); - private Boolean emailStartTlsRequired = HapiProperties.getEmailStartTlsRequired(); - private Boolean emailQuitWait = HapiProperties.getEmailQuitWait(); - - @Autowired private ApplicationContext appContext; @@ -58,24 +43,25 @@ public class FhirServerConfigCommon { ourLog.info("Server configured to " + (appProperties.getAllow_placeholder_references() ? "allow" : "deny") + " placeholder references"); ourLog.info("Server configured to " + (appProperties.getAllow_override_default_search_params() ? "allow" : "deny") + " overriding default search params"); - if (this.emailEnabled) { - ourLog.info("Server is configured to enable email with host '" + this.emailHost + "' and port " + this.emailPort.toString()); - ourLog.info("Server will use '" + this.emailFrom + "' as the from email address"); + if (appProperties.getSubscription().getEmail() != null) { + AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail(); + ourLog.info("Server is configured to enable email with host '" + email.getHost() + "' and port " + email.getPort()); + ourLog.info("Server will use '" + email.getFrom() + "' as the from email address"); - if (this.emailUsername != null && this.emailUsername.length() > 0) { - ourLog.info("Server is configured to use username '" + this.emailUsername + "' for email"); + if (!Strings.isNullOrEmpty(email.getUsername())) { + ourLog.info("Server is configured to use username '" + email.getUsername() + "' for email"); } - if (this.emailPassword != null && this.emailPassword.length() > 0) { + if (!Strings.isNullOrEmpty(email.getPassword())) { ourLog.info("Server is configured to use a password for email"); } } - if (this.subscriptionRestHookEnabled) { + if (appProperties.getSubscription().getResthook_enabled()) { ourLog.info("REST-hook subscriptions enabled"); } - if (this.subscriptionEmailEnabled) { + if (appProperties.getSubscription().getEmail() != null) { ourLog.info("Email subscriptions enabled"); } } @@ -96,7 +82,8 @@ public class FhirServerConfigCommon { retVal.setAllowExternalReferences(appProperties.getAllow_external_references()); retVal.setExpungeEnabled(appProperties.getExpunge_enabled()); retVal.setAutoCreatePlaceholderReferenceTargets(appProperties.getAllow_placeholder_references()); - retVal.setEmailFromAddress(this.emailFrom); + if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) + retVal.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom()); Integer maxFetchSize = appProperties.getMax_page_size(); retVal.setFetchSizeDefaultMaximum(maxFetchSize); @@ -106,21 +93,24 @@ public class FhirServerConfigCommon { retVal.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis); ourLog.info("Server configured to cache search results for {} milliseconds", reuseCachedSearchResultsMillis); - Long retainCachedSearchesMinutes = HapiProperties.getExpireSearchResultsAfterMins(); + + Long retainCachedSearchesMinutes = appProperties.getRetain_cached_searches_mins(); retVal.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000); - // Subscriptions are enabled by channel type - if (appProperties.getSubscription().getResthook_enabled()) { - ourLog.info("Enabling REST-hook subscriptions"); - retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK); - } - if (appProperties.getSubscription().getEmail_enabled()) { - ourLog.info("Enabling email subscriptions"); - retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.EMAIL); - } - if (appProperties.getSubscription().getWebsocket_enabled()) { - ourLog.info("Enabling websocket subscriptions"); - retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET); + if(appProperties.getSubscription() != null) { + // Subscriptions are enabled by channel type + if (appProperties.getSubscription().getResthook_enabled()) { + ourLog.info("Enabling REST-hook subscriptions"); + retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK); + } + if (appProperties.getSubscription().getEmail() != null) { + ourLog.info("Enabling email subscriptions"); + retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.EMAIL); + } + if (appProperties.getSubscription().getWebsocket_enabled()) { + ourLog.info("Enabling websocket subscriptions"); + retVal.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET); + } } retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); @@ -133,7 +123,7 @@ public class FhirServerConfigCommon { PartitionSettings retVal = new PartitionSettings(); // Partitioning - if (HapiProperties.getPartitioningMultitenancyEnabled()) { + if (appProperties.getPartitioning() != null) { retVal.setPartitioningEnabled(true); } @@ -147,14 +137,15 @@ public class FhirServerConfigCommon { modelConfig.setAllowContainsSearches(appProperties.getAllow_contains_searches()); modelConfig.setAllowExternalReferences(appProperties.getAllow_external_references()); modelConfig.setDefaultSearchParamsCanBeOverridden(appProperties.getAllow_override_default_search_params()); - modelConfig.setEmailFromAddress(this.emailFrom); + if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) + modelConfig.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom()); // You can enable these if you want to support Subscriptions from your server - if (this.subscriptionRestHookEnabled) { + if (appProperties.getSubscription() != null && appProperties.getSubscription().getResthook_enabled() != null) { modelConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.RESTHOOK); } - if (this.subscriptionEmailEnabled) { + if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) { modelConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.EMAIL); } @@ -181,34 +172,33 @@ public class FhirServerConfigCommon { @Lazy @Bean - public IBinaryStorageSvc binaryStorageSvc() { + public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) { DatabaseBlobBinaryStorageSvcImpl binaryStorageSvc = new DatabaseBlobBinaryStorageSvcImpl(); - if (HapiProperties.getMaxBinarySize() != null) { - binaryStorageSvc.setMaximumBinarySize(HapiProperties.getMaxBinarySize()); + if (appProperties.getMax_binary_size() != null) { + binaryStorageSvc.setMaximumBinarySize(appProperties.getMax_binary_size()); } return binaryStorageSvc; } @Bean() - public IEmailSender emailSender() { - if (this.emailEnabled) { + public IEmailSender emailSender(AppProperties appProperties, Optional subscriptionDeliveryHandlerFactory) { + if (appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) { JavaMailEmailSender retVal = new JavaMailEmailSender(); - retVal.setSmtpServerHostname(this.emailHost); - retVal.setSmtpServerPort(this.emailPort); - retVal.setSmtpServerUsername(this.emailUsername); - retVal.setSmtpServerPassword(this.emailPassword); - retVal.setAuth(this.emailAuth); - retVal.setStartTlsEnable(this.emailStartTlsEnable); - retVal.setStartTlsRequired(this.emailStartTlsRequired); - retVal.setQuitWait(this.emailQuitWait); - - SubscriptionDeliveryHandlerFactory subscriptionDeliveryHandlerFactory = appContext.getBean(SubscriptionDeliveryHandlerFactory.class); - Validate.notNull(subscriptionDeliveryHandlerFactory, "No subscription delivery handler"); - subscriptionDeliveryHandlerFactory.setEmailSender(retVal); + AppProperties.Subscription.Email email = appProperties.getSubscription().getEmail(); + retVal.setSmtpServerHostname(email.getHost()); + retVal.setSmtpServerPort(email.getPort()); + retVal.setSmtpServerUsername(email.getUsername()); + retVal.setSmtpServerPassword(email.getPassword()); + retVal.setAuth(email.getAuth()); + retVal.setStartTlsEnable(email.getStartTlsEnable()); + retVal.setStartTlsRequired(email.getStartTlsRequired()); + retVal.setQuitWait(email.getQuitWait()); + if(subscriptionDeliveryHandlerFactory.isPresent()) + subscriptionDeliveryHandlerFactory.get().setEmailSender(retVal); return retVal; } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index 491aae3..8669fc8 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -2,16 +2,21 @@ package ca.uhn.fhir.jpa.starter; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; + +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Import; -@WebServlet(urlPatterns = {"/fhir/*"}) +@WebServlet(urlPatterns = {"/hapi-fhir-jpaserver/fhir/*"}) @Import(AppProperties.class) public class JpaRestfulServer extends BaseJpaRestfulServer { + @Autowired + AppProperties appProperties; + private static final long serialVersionUID = 1L; - public JpaRestfulServer(AppProperties appProperties) { - super(appProperties); + public JpaRestfulServer() { + super(); } @Override diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 13ec39d..f43409e 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,6 +1,6 @@ spring: datasource: - url: 'jdbc:h2:file:./target/database/h2dflkjglkj' + url: 'jdbc:h2:file:./target/database/h2' username: sa password: null driverClassName: org.h2.Driver @@ -10,42 +10,57 @@ spring: active: r4 hapi: fhir: - allow_cascading_deletes: true - allow_contains_searches: true - allow_external_references: true - allow_multiple_delete: true - allow_override_default_search_params: true - allow_placeholder_references: true - auto_create_placeholder_reference_targets: false - default_encoding: JSON - default_page_size: 20 - enable_index_missing_fields: false - enforce_referential_integrity_on_delete: false - enforce_referential_integrity_on_write: false - etag_support_enabled: true - expunge_enabled: true - daoconfig_client_id_strategy: null - fhirpath_interceptor_enabled: false - filter_search_enabled: true - graphql_enabled: true - logger: - error_format: 'ERROR - ${requestVerb} ${requestUrl}' - format: >- - Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] - Operation[${operationType} ${operationName} ${idOrResourceName}] - UA[${requestHeader.user-agent}] Params[${requestParameters}] - ResponseEncoding[${responseEncodingNoDefault}] - log_exceptions: true - name: fhirtest.access - max_binary_size: 104857600 - max_page_size: 200 - retain_cached_searches_mins: 60 - reuse_cached_search_results_millis: 60000 + #supported_resource_types: + # - Patient + # - Observation +# allow_cascading_deletes: true +# allow_contains_searches: true +# allow_external_references: true +# allow_multiple_delete: true +# allow_override_default_search_params: true +# allow_placeholder_references: true +# auto_create_placeholder_reference_targets: false +# default_encoding: JSON +# default_pretty_print: true +# default_page_size: 20 +# enable_index_missing_fields: false +# enforce_referential_integrity_on_delete: false +# enforce_referential_integrity_on_write: false +# etag_support_enabled: true +# expunge_enabled: true +# daoconfig_client_id_strategy: null +# fhirpath_interceptor_enabled: false +# filter_search_enabled: true +# graphql_enabled: true + #partitioning: + # cross_partition_reference_mode: true + # multitenancy_enabled: true + # partitioning_include_in_search_hashes: true + #cors: + # allow_Credentials: true + # Supports multiple, comma separated allowed origin entries + # cors.allowed_origin=http://localhost:8080,https://localhost:8080,https://fhirtest.uhn.ca + # allowed_origin: + # - '*' + +# logger: +# error_format: 'ERROR - ${requestVerb} ${requestUrl}' +# format: >- +# Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] +# Operation[${operationType} ${operationName} ${idOrResourceName}] +# UA[${requestHeader.user-agent}] Params[${requestParameters}] +# ResponseEncoding[${responseEncodingNoDefault}] +# log_exceptions: true +# name: fhirtest.access +# max_binary_size: 104857600 +# max_page_size: 200 +# retain_cached_searches_mins: 60 +# reuse_cached_search_results_millis: 60000 tester: - id: home name: Local Tester - server_address: 'http://localhost:8080/fhir' + server_address: 'http://localhost:8080/hapi-fhir-jpaserver/fhir' refuse_to_fetch_third_party_urls: false fhir_version: R4 - @@ -54,23 +69,35 @@ hapi: server_address: "http://hapi.fhir.org/baseR4" refuse_to_fetch_third_party_urls: false fhir_version: R4 - validation: - requests_enabled: true - responses_enabled: true - binary_storage_enabled: true - bulk_export_enabled: true - partitioning_multitenancy_enabled: - subscription: - email_enabled: false - resthook_enabled: false - websocket_enabled: false -elasticsearch: - debug: - pretty_print_json_log: false - refresh_after_write: false - enabled: false - password: SomePassword - required_index_status: YELLOW - rest_url: 'http://localhost:9200' - schema_management_strategy: CREATE - username: SomeUsername +# validation: +# requests_enabled: true +# responses_enabled: true +# binary_storage_enabled: true +# bulk_export_enabled: true +# partitioning_multitenancy_enabled: +# subscription: +# resthook_enabled: false +# websocket_enabled: false +# email: +# from: some@test.com +# host: google.com +# port: +# username: +# password: +# auth: +# startTlsEnable: +# startTlsRequired: +# quitWait: + + +# +#elasticsearch: +# debug: +# pretty_print_json_log: false +# refresh_after_write: false +# enabled: false +# password: SomePassword +# required_index_status: YELLOW +# rest_url: 'http://localhost:9200' +# schema_management_strategy: CREATE +# username: SomeUsername diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java index b34e67d..26a0385 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -26,10 +26,10 @@ public class ExampleServerDstu2IT { private static Server ourServer; static { - HapiProperties.forceReload(); - HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU2"); - HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr2"); ourCtx = FhirContext.forDstu2(); + System.setProperty("spring.profiles.active", "dstu2"); + System.setProperty("spring.batch.job.enabled", "false"); + System.setProperty("spring.datasource.url", "jdbc:h2:mem:dbr2"); } @Test @@ -60,7 +60,8 @@ public class ExampleServerDstu2IT { WebAppContext webAppContext = new WebAppContext(); webAppContext.setContextPath("/hapi-fhir-jpaserver"); - webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml"); + + webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-starter"); webAppContext.setParentLoaderPriority(true);