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 4314317..e7599a8 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -81,7 +81,7 @@ public class AppProperties { private Integer bundle_batch_pool_size = 20; private Integer bundle_batch_pool_max_size = 100; - private List local_base_urls = new ArrayList<>(); + private final List local_base_urls = new ArrayList<>(); public Boolean getOpenapi_enabled() { return openapi_enabled; @@ -203,10 +203,6 @@ public class AppProperties { this.supported_resource_types = supported_resource_types; } - public List getSupported_resource_types(List supported_resource_types) { - return this.supported_resource_types; - } - public Logger getLogger() { return logger; } 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 2a26b5c..8031c59 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/Application.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/Application.java @@ -3,11 +3,13 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig; import ca.uhn.fhir.jpa.batch2.JpaBatch2Config; import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion; +import ca.uhn.fhir.jpa.starter.common.FhirTesterConfig; import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig; import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig; import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig; +import ca.uhn.fhir.rest.server.RestfulServer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.boot.SpringApplication; @@ -23,8 +25,7 @@ import org.springframework.context.annotation.Import; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.servlet.DispatcherServlet; -@ServletComponentScan(basePackageClasses = { - JpaRestfulServer.class}) +@ServletComponentScan(basePackageClasses = {RestfulServer.class}) @SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class}) @Import({ SubscriptionSubmitterConfig.class, @@ -56,11 +57,10 @@ public class Application extends SpringBootServletInitializer { @Bean @Conditional(OnEitherVersion.class) - public ServletRegistrationBean hapiServletRegistration() { + public ServletRegistrationBean hapiServletRegistration(RestfulServer restfulServer) { ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(); - JpaRestfulServer jpaRestfulServer = new JpaRestfulServer(); - beanFactory.autowireBean(jpaRestfulServer); - servletRegistrationBean.setServlet(jpaRestfulServer); + beanFactory.autowireBean(restfulServer); + servletRegistrationBean.setServlet(restfulServer); servletRegistrationBean.addUrlMappings("/fhir/*"); servletRegistrationBean.setLoadOnStartup(1); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java deleted file mode 100644 index 81c0e0f..0000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/BaseJpaRestfulServer.java +++ /dev/null @@ -1,429 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider; -import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.context.FhirVersionEnum; -import ca.uhn.fhir.context.support.IValidationSupport; -import ca.uhn.fhir.cql.common.provider.CqlProviderLoader; -import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; -import ca.uhn.fhir.interceptor.api.IInterceptorService; -import ca.uhn.fhir.jpa.api.config.DaoConfig; -import ca.uhn.fhir.jpa.api.dao.DaoRegistry; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; -import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; -import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; -import ca.uhn.fhir.jpa.graphql.GraphQLProvider; -import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; -import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; -import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; -import ca.uhn.fhir.jpa.partition.PartitionManagementProvider; -import ca.uhn.fhir.jpa.provider.*; -import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; -import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; -import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; -import ca.uhn.fhir.mdm.provider.MdmProviderLoader; -import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; -import ca.uhn.fhir.narrative.INarrativeGenerator; -import ca.uhn.fhir.narrative2.NullNarrativeGenerator; -import ca.uhn.fhir.rest.openapi.OpenApiInterceptor; -import ca.uhn.fhir.rest.server.*; -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.rest.server.util.ISearchParamRegistry; -import ca.uhn.fhir.validation.IValidatorModule; -import ca.uhn.fhir.validation.ResultSeverityEnum; -import com.google.common.base.Strings; -import com.google.common.collect.ImmutableList; -import org.hl7.fhir.r4.model.Bundle.BundleType; -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 { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaRestfulServer.class); - - private static final long serialVersionUID = 1L; - @Autowired - DaoRegistry daoRegistry; - @Autowired - DaoConfig daoConfig; - @Autowired - ISearchParamRegistry searchParamRegistry; - @Autowired - IFhirSystemDao fhirSystemDao; - @Autowired - ResourceProviderFactory resourceProviderFactory; - @Autowired - IJpaSystemProvider jpaSystemProvider; - @Autowired - ValueSetOperationProvider myValueSetOperationProvider; - @Autowired - IInterceptorBroadcaster interceptorBroadcaster; - @Autowired - DatabaseBackedPagingProvider databaseBackedPagingProvider; - @Autowired - IInterceptorService interceptorService; - @Autowired - IValidatorModule validatorModule; - @Autowired - Optional graphQLProvider; - @Autowired - BulkDataExportProvider bulkDataExportProvider; - @Autowired - BulkDataImportProvider bulkDataImportProvider; - @Autowired - PartitionManagementProvider partitionManagementProvider; - - @Autowired - ValueSetOperationProvider valueSetOperationProvider; - @Autowired - ReindexProvider reindexProvider; - @Autowired - BinaryStorageInterceptor binaryStorageInterceptor; - @Autowired - Optional binaryAccessProvider; - @Autowired - IPackageInstallerSvc packageInstallerSvc; - @Autowired - AppProperties appProperties; - @Autowired - ApplicationContext myApplicationContext; - @Autowired(required = false) - IRepositoryValidationInterceptorFactory factory; - // These are set only if the features are enabled - @Autowired - Optional cqlProviderLoader; - @Autowired - Optional mdmProviderProvider; - - @Autowired - private IValidationSupport myValidationSupport; - - public BaseJpaRestfulServer() { - } - - @SuppressWarnings("unchecked") - @Override - protected void initialize() throws ServletException { - super.initialize(); - - /* - * Create a FhirContext object that uses the version of FHIR - * specified in the properties file. - */ - // Customize supported resource types - List supportedResourceTypes = appProperties.getSupported_resource_types(); - - if (!supportedResourceTypes.isEmpty()) { - if (!supportedResourceTypes.contains("SearchParameter")) { - supportedResourceTypes.add("SearchParameter"); - } - daoRegistry.setSupportedResourceTypes(supportedResourceTypes); - } - - setFhirContext(fhirSystemDao.getContext()); - - /* - * Order matters - the MDM provider registers itself on the resourceProviderFactory - hence the loading must be done - * ahead of provider registration - */ - if(appProperties.getMdm_enabled()) - mdmProviderProvider.get().loadProvider(); - - registerProviders(resourceProviderFactory.createProviders()); - registerProvider(jpaSystemProvider); - /* - * The conformance provider exports the supported resources, search parameters, etc for - * this server. The JPA version adds resourceProviders counts to the exported statement, so it - * is a nice addition. - * - * You can also create your own subclass of the conformance provider if you need to - * provide further customization of your server's CapabilityStatement - */ - - FhirVersionEnum fhirVersion = fhirSystemDao.getContext().getVersion().getVersion(); - if (fhirVersion == FhirVersionEnum.DSTU2) { - - JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, fhirSystemDao, - daoConfig); - confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server"); - setServerConformanceProvider(confProvider); - } else { - if (fhirVersion == FhirVersionEnum.DSTU3) { - - JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, fhirSystemDao, - daoConfig, searchParamRegistry); - confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server"); - setServerConformanceProvider(confProvider); - } else if (fhirVersion == FhirVersionEnum.R4) { - - JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, fhirSystemDao, - daoConfig, searchParamRegistry, myValidationSupport); - confProvider.setImplementationDescription("HAPI FHIR R4 Server"); - setServerConformanceProvider(confProvider); - } else if (fhirVersion == FhirVersionEnum.R5) { - - JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(this, fhirSystemDao, - daoConfig, searchParamRegistry, myValidationSupport); - confProvider.setImplementationDescription("HAPI FHIR R5 Server"); - setServerConformanceProvider(confProvider); - } else { - throw new IllegalStateException(); - } - } - - /* - * ETag Support - */ - - if (appProperties.getEtag_support_enabled() == false) - setETagSupport(ETagSupportEnum.DISABLED); - - - /* - * This server tries to dynamically generate narratives - */ - FhirContext ctx = getFhirContext(); - INarrativeGenerator theNarrativeGenerator = - appProperties.getNarrative_enabled() ? - new DefaultThymeleafNarrativeGenerator() : - new NullNarrativeGenerator(); - ctx.setNarrativeGenerator(theNarrativeGenerator); - - /* - * Default to JSON and pretty printing - */ - setDefaultPrettyPrint(appProperties.getDefault_pretty_print()); - - /* - * Default encoding - */ - setDefaultResponseEncoding(appProperties.getDefault_encoding()); - - /* - * This configures the server to page search results to and from - * the database, instead of only paging them to memory. This may mean - * a performance hit when performing searches that return lots of results, - * but makes the server much more scalable. - */ - - setPagingProvider(databaseBackedPagingProvider); - - /* - * This interceptor formats the output using nice colourful - * HTML output when the request is detected to come from a - * browser. - */ - ResponseHighlighterInterceptor responseHighlighterInterceptor = new ResponseHighlighterInterceptor(); - this.registerInterceptor(responseHighlighterInterceptor); - - if (appProperties.getFhirpath_interceptor_enabled()) { - registerInterceptor(new FhirPathFilterInterceptor()); - } - - /* - * Add some logging for each request - */ - LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - 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); - - /* - * If you are hosting this server at a specific DNS name, the server will try to - * figure out the FHIR base URL based on what the web container tells it, but - * 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 = 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()); - } - - /* - * If you are using DSTU3+, you may want to add a terminology uploader, which allows - * uploading of external terminologies such as Snomed CT. Note that this uploader - * does not have any security attached (any anonymous user may use it by default) - * so it is a potential security vulnerability. Consider using an AuthorizationInterceptor - * with this feature. - */ - if (ctx.getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { // <-- ENABLED RIGHT NOW - registerProvider(myApplicationContext.getBean(TerminologyUploaderProvider.class)); - } - - // If you want to enable the $trigger-subscription operation to allow - // manual triggering of a subscription delivery, enable this provider - if (true) { // <-- ENABLED RIGHT NOW - registerProvider(myApplicationContext.getBean(SubscriptionTriggeringProvider.class)); - } - - // Define your CORS configuration. This is an example - // showing a typical setup. You should customize this - // to your specific needs - if (appProperties.getCors() != null) { - ourLog.info("CORS is enabled on this server"); - CorsConfiguration config = new CorsConfiguration(); - config.addAllowedHeader(HttpHeaders.ORIGIN); - config.addAllowedHeader(HttpHeaders.ACCEPT); - config.addAllowedHeader(HttpHeaders.CONTENT_TYPE); - config.addAllowedHeader(HttpHeaders.AUTHORIZATION); - config.addAllowedHeader(HttpHeaders.CACHE_CONTROL); - config.addAllowedHeader("x-fhir-starter"); - config.addAllowedHeader("X-Requested-With"); - config.addAllowedHeader("Prefer"); - - List allAllowedCORSOrigins = appProperties.getCors().getAllowed_origin(); - allAllowedCORSOrigins.forEach(config::addAllowedOriginPattern); - ourLog.info("CORS allows the following origins: " + String.join(", ", allAllowedCORSOrigins)); - - config.addExposedHeader("Location"); - config.addExposedHeader("Content-Location"); - config.setAllowedMethods( - Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD")); - config.setAllowCredentials(appProperties.getCors().getAllow_Credentials()); - - // Create the interceptor and register it - CorsInterceptor interceptor = new CorsInterceptor(config); - registerInterceptor(interceptor); - } else { - ourLog.info("CORS is disabled on this server"); - } - - // If subscriptions are enabled, we want to register the interceptor that - // will activate them and match results against them - if (appProperties.getSubscription() != null) { - // Subscription debug logging - interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor()); - } - - // Cascading deletes - - - if (appProperties.getAllow_cascading_deletes()) { - CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(ctx, - daoRegistry, interceptorBroadcaster); - getInterceptorService().registerInterceptor(cascadingDeleteInterceptor); - } - - // Binary Storage - if (appProperties.getBinary_storage_enabled() && binaryAccessProvider.isPresent()) { - registerProvider(binaryAccessProvider.get()); - getInterceptorService().registerInterceptor(binaryStorageInterceptor); - } - - // Validation - - if (validatorModule != null) { - if (appProperties.getValidation().getRequests_enabled()) { - RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor(); - interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); - interceptor.setValidatorModules(Collections.singletonList(validatorModule)); - registerInterceptor(interceptor); - } - if (appProperties.getValidation().getResponses_enabled()) { - ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor(); - interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); - interceptor.setValidatorModules(Collections.singletonList(validatorModule)); - registerInterceptor(interceptor); - } - } - - // GraphQL - if (appProperties.getGraphql_enabled()) { - if (fhirVersion.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { - registerProvider(graphQLProvider.get()); - } - } - - if (appProperties.getAllowed_bundle_types() != null) { - daoConfig.setBundleTypesAllowedForStorage(appProperties.getAllowed_bundle_types().stream().map(BundleType::toCode).collect(Collectors.toSet())); - } - - daoConfig.setDeferIndexingForCodesystemsOfSize(appProperties.getDefer_indexing_for_codesystems_of_size()); - - if (appProperties.getOpenapi_enabled()) { - registerInterceptor(new OpenApiInterceptor()); - } - - // Bulk Export - if (appProperties.getBulk_export_enabled()) { - registerProvider(bulkDataExportProvider); - } - - //Bulk Import - if (appProperties.getBulk_import_enabled()) { - registerProvider(bulkDataImportProvider); - } - - // valueSet Operations i.e $expand - registerProvider(myValueSetOperationProvider); - - //reindex Provider $reindex - registerProvider(reindexProvider); - - // Partitioning - if (appProperties.getPartitioning() != null) { - registerInterceptor(new RequestTenantPartitionInterceptor()); - setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); - registerProviders(partitionManagementProvider); - } - - if (appProperties.getClient_id_strategy() == DaoConfig.ClientIdStrategyEnum.ANY) { - daoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID); - daoConfig.setResourceClientIdStrategy(appProperties.getClient_id_strategy()); - } - //Parallel Batch GET execution settings - daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_size()); - daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_max_size()); - - if (appProperties.getImplementationGuides() != null) { - Map guides = appProperties.getImplementationGuides(); - for (Map.Entry guide : guides.entrySet()) { - PackageInstallationSpec packageInstallationSpec = new PackageInstallationSpec() - .setPackageUrl(guide.getValue().getUrl()) - .setName(guide.getValue().getName()) - .setVersion(guide.getValue().getVersion()) - .setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); - if(appProperties.getInstall_transitive_ig_dependencies()) { - packageInstallationSpec.setFetchDependencies(true); - packageInstallationSpec.setDependencyExcludes(ImmutableList.of("hl7.fhir.r2.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core", "hl7.fhir.r5.core")); - } - packageInstallerSvc.install(packageInstallationSpec); - } - } - - if(factory != null) { - interceptorService.registerInterceptor(factory.buildUsingStoredStructureDefinitions()); - } - - - if (appProperties.getLastn_enabled()) { - daoConfig.setLastNEnabled(true); - } - - if(appProperties.getInline_resource_storage_below_size() != 0){ - daoConfig.setInlineResourceTextBelowSize(appProperties.getInline_resource_storage_below_size()); - } - - daoConfig.setStoreResourceInHSearchIndex(appProperties.getStore_resource_in_lucene_index_enabled()); - daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level()); - daoConfig.getModelConfig().setIndexOnContainedResources(appProperties.getEnable_index_contained_resource()); - } -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java deleted file mode 100644 index c895c22..0000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java +++ /dev/null @@ -1,28 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Import; - -import javax.servlet.ServletException; - -@Import(AppProperties.class) -public class JpaRestfulServer extends BaseJpaRestfulServer { - - @Autowired - AppProperties appProperties; - - private static final long serialVersionUID = 1L; - - public JpaRestfulServer() { - super(); - } - - @Override - protected void initialize() throws ServletException { - super.initialize(); - - // Add your own customization here - - } - -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java deleted file mode 100644 index 2acfabd..0000000 --- a/src/main/java/ca/uhn/fhir/jpa/starter/StarterJpaConfig.java +++ /dev/null @@ -1,128 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import ca.uhn.fhir.context.ConfigurationException; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.IDaoRegistry; -import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer; -import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; -import ca.uhn.fhir.jpa.config.util.ResourceCountCacheUtil; -import ca.uhn.fhir.jpa.config.util.ValidationSupportConfigUtil; -import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl; -import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl; -import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl; -import ca.uhn.fhir.jpa.dao.search.IHSearchSortHelper; -import ca.uhn.fhir.jpa.provider.DaoRegistryResourceSupportedSvc; -import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; -import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; -import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl; -import ca.uhn.fhir.jpa.util.ResourceCountCache; -import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; -import ca.uhn.fhir.mdm.dao.IMdmLinkDao; -import ca.uhn.fhir.rest.api.IResourceSupportedSvc; -import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; - -import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; -import org.springframework.batch.core.configuration.annotation.BatchConfigurer; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - -@Configuration -public class StarterJpaConfig { - @Bean - public IFulltextSearchSvc fullTextSearchSvc() { - return new FulltextSearchSvcImpl(); - } - - @Bean - public IStaleSearchDeletingSvc staleSearchDeletingSvc() { - return new StaleSearchDeletingSvcImpl(); - } - - @Primary - @Bean - public CachingValidationSupport validationSupportChain(JpaValidationSupportChain theJpaValidationSupportChain) { - return ValidationSupportConfigUtil.newCachingValidationSupport(theJpaValidationSupportChain); - } - - @Bean - public BatchConfigurer batchConfigurer() { - return new NonPersistedBatchConfigurer(); - } - - @Autowired - AppProperties appProperties; - @Autowired - private DataSource myDataSource; - @Autowired - private ConfigurableEnvironment configurableEnvironment; - - /** - * Customize the default/max page sizes for search results. You can set these however - * you want, although very large page sizes will require a lot of RAM. - */ - @Bean - public DatabaseBackedPagingProvider databaseBackedPagingProvider() { - DatabaseBackedPagingProvider pagingProvider = new DatabaseBackedPagingProvider(); - pagingProvider.setDefaultPageSize(appProperties.getDefault_page_size()); - pagingProvider.setMaximumPageSize(appProperties.getMax_page_size()); - return pagingProvider; - } - - @Bean - public IResourceSupportedSvc resourceSupportedSvc(IDaoRegistry theDaoRegistry) { - return new DaoRegistryResourceSupportedSvc(theDaoRegistry); - } - - @Bean(name = "myResourceCountsCache") - public ResourceCountCache resourceCountsCache(IFhirSystemDao theSystemDao) { - return ResourceCountCacheUtil.newResourceCountCache(theSystemDao); - } - - @Primary - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory( - ConfigurableListableBeanFactory myConfigurableListableBeanFactory, FhirContext theFhirContext) { - LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(myConfigurableListableBeanFactory, theFhirContext); - retVal.setPersistenceUnitName("HAPI_PU"); - - try { - retVal.setDataSource(myDataSource); - } catch (Exception e) { - throw new ConfigurationException("Could not set the data source due to a configuration issue", e); - } - retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, myConfigurableListableBeanFactory)); - return retVal; - } - - @Bean - @Primary - public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) { - JpaTransactionManager retVal = new JpaTransactionManager(); - retVal.setEntityManagerFactory(entityManagerFactory); - return retVal; - } - - @Autowired - private ISearchParamRegistry mySearchParamRegistry; - - @Bean - public IHSearchSortHelper hSearchSortHelper() { - return new HSearchSortHelperImpl(mySearchParamRegistry); - } - - @Bean - public IMdmLinkDao mdmLinkDao(){ - return new MdmLinkDaoJpaImpl(); - } -} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/ElasticsearchConfig.java similarity index 85% rename from src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/ElasticsearchConfig.java index 21216e6..78de890 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/ElasticsearchConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/ElasticsearchConfig.java @@ -1,7 +1,7 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.search.lastn.ElasticsearchSvcImpl; -import org.springframework.beans.factory.annotation.Autowired; +import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.ConfigurableEnvironment; @@ -10,12 +10,8 @@ import org.springframework.core.env.ConfigurableEnvironment; @Configuration public class ElasticsearchConfig { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElasticsearchConfig.class); - - @Autowired - private ConfigurableEnvironment configurableEnvironment; - @Bean - public ElasticsearchSvcImpl elasticsearchSvc() { + public ElasticsearchSvcImpl elasticsearchSvc(ConfigurableEnvironment configurableEnvironment) { if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) { String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment); if (elasticsearchUrl.startsWith("http")) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java similarity index 71% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java index ac80f09..4dd4412 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigCommon.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc; @@ -7,6 +7,8 @@ import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings.CrossPartitionReferenceMode; import ca.uhn.fhir.jpa.model.entity.ModelConfig; +import ca.uhn.fhir.jpa.starter.AppProperties; +import ca.uhn.fhir.jpa.starter.util.JpaHibernatePropertiesProvider; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory; import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl; import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender; @@ -14,6 +16,7 @@ import ca.uhn.fhir.rest.server.mail.IMailSvc; import ca.uhn.fhir.rest.server.mail.MailConfig; import ca.uhn.fhir.rest.server.mail.MailSvc; import com.google.common.base.Strings; +import org.hl7.fhir.r4.model.Bundle.BundleType; import org.hl7.fhir.dstu2.model.Subscription; import org.springframework.boot.env.YamlPropertySourceLoader; import org.springframework.context.annotation.Bean; @@ -25,6 +28,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.HashSet; import java.util.Optional; +import java.util.stream.Collectors; /** * This is the primary configuration file for the example server @@ -78,54 +82,85 @@ public class FhirServerConfigCommon { */ @Bean public DaoConfig daoConfig(AppProperties appProperties) { - DaoConfig retVal = new DaoConfig(); + DaoConfig daoConfig = new DaoConfig(); - retVal.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? DaoConfig.IndexEnabledEnum.ENABLED : DaoConfig.IndexEnabledEnum.DISABLED); - retVal.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets()); - retVal.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write()); - retVal.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete()); - retVal.setAllowContainsSearches(appProperties.getAllow_contains_searches()); - retVal.setAllowMultipleDelete(appProperties.getAllow_multiple_delete()); - retVal.setAllowExternalReferences(appProperties.getAllow_external_references()); - retVal.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled()); - retVal.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled()); - retVal.setExpungeEnabled(appProperties.getExpunge_enabled()); + daoConfig.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? DaoConfig.IndexEnabledEnum.ENABLED : DaoConfig.IndexEnabledEnum.DISABLED); + daoConfig.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets()); + daoConfig.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write()); + daoConfig.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete()); + daoConfig.setAllowContainsSearches(appProperties.getAllow_contains_searches()); + daoConfig.setAllowMultipleDelete(appProperties.getAllow_multiple_delete()); + daoConfig.setAllowExternalReferences(appProperties.getAllow_external_references()); + daoConfig.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled()); + daoConfig.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled()); + daoConfig.setExpungeEnabled(appProperties.getExpunge_enabled()); if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null) - retVal.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom()); + daoConfig.setEmailFromAddress(appProperties.getSubscription().getEmail().getFrom()); Integer maxFetchSize = appProperties.getMax_page_size(); - retVal.setFetchSizeDefaultMaximum(maxFetchSize); + daoConfig.setFetchSizeDefaultMaximum(maxFetchSize); ourLog.info("Server configured to have a maximum fetch size of " + (maxFetchSize == Integer.MAX_VALUE ? "'unlimited'" : maxFetchSize)); Long reuseCachedSearchResultsMillis = appProperties.getReuse_cached_search_results_millis(); - retVal.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis); + daoConfig.setReuseCachedSearchResultsForMillis(reuseCachedSearchResultsMillis); ourLog.info("Server configured to cache search results for {} milliseconds", reuseCachedSearchResultsMillis); Long retainCachedSearchesMinutes = appProperties.getRetain_cached_searches_mins(); - retVal.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000); + daoConfig.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000); 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); + daoConfig.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); + daoConfig.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); + daoConfig.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.WEBSOCKET); } } - retVal.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); - retVal.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing()); - retVal.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls())); + daoConfig.setFilterParameterEnabled(appProperties.getFilter_search_enabled()); + daoConfig.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing()); + daoConfig.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls())); - return retVal; + if (appProperties.getLastn_enabled()) { + daoConfig.setLastNEnabled(true); + } + + if(appProperties.getInline_resource_storage_below_size() != 0){ + daoConfig.setInlineResourceTextBelowSize(appProperties.getInline_resource_storage_below_size()); + } + + + daoConfig.setStoreResourceInHSearchIndex(appProperties.getStore_resource_in_lucene_index_enabled()); + daoConfig.getModelConfig().setNormalizedQuantitySearchLevel(appProperties.getNormalized_quantity_search_level()); + daoConfig.getModelConfig().setIndexOnContainedResources(appProperties.getEnable_index_contained_resource()); + + + + if (appProperties.getAllowed_bundle_types() != null) { + daoConfig.setBundleTypesAllowedForStorage(appProperties.getAllowed_bundle_types().stream().map(BundleType::toCode).collect(Collectors.toSet())); + } + + daoConfig.setDeferIndexingForCodesystemsOfSize(appProperties.getDefer_indexing_for_codesystems_of_size()); + + + if (appProperties.getClient_id_strategy() == DaoConfig.ClientIdStrategyEnum.ANY) { + daoConfig.setResourceServerIdStrategy(DaoConfig.IdStrategyEnum.UUID); + daoConfig.setResourceClientIdStrategy(appProperties.getClient_id_strategy()); + } + //Parallel Batch GET execution settings + daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_size()); + daoConfig.setBundleBatchPoolSize(appProperties.getBundle_batch_pool_max_size()); + + + return daoConfig; } @Bean @@ -183,24 +218,6 @@ public class FhirServerConfigCommon { return modelConfig; } - /** - * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a - * directory called "jpaserver_derby_files". - *

- * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource. - */ - /*@Bean(destroyMethod = "close") - public BasicDataSource dataSource() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { - BasicDataSource retVal = new BasicDataSource(); - Driver driver = (Driver) Class.forName(HapiProperties.getDataSourceDriver()).getConstructor().newInstance(); - retVal.setDriver(driver); - retVal.setUrl(HapiProperties.getDataSourceUrl()); - retVal.setUsername(HapiProperties.getDataSourceUsername()); - retVal.setPassword(HapiProperties.getDataSourcePassword()); - retVal.setMaxTotal(HapiProperties.getDataSourceMaxPoolSize()); - return retVal; - }*/ - @Lazy @Bean public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java similarity index 91% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java index b801138..151df82 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu2.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.config.JpaDstu2Config; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java similarity index 87% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java index 35d1dab..e0056bc 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigDstu3.java @@ -1,9 +1,8 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.config.dstu3.JpaDstu3Config; import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlDstu3Config; -import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java similarity index 87% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java index c31cf52..4c212ef 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java @@ -1,9 +1,8 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.config.r4.JpaR4Config; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config; -import ca.uhn.fhir.jpa.starter.mdm.MdmConfig; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR5.java similarity index 91% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR5.java index 8ee03df..1552aef 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR5.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; import ca.uhn.fhir.jpa.config.r5.JpaR5Config; import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition; diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java similarity index 78% rename from src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java index 7adf8bf..cb28659 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirTesterConfig.java @@ -1,5 +1,6 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common; +import ca.uhn.fhir.jpa.starter.AppProperties; import ca.uhn.fhir.to.FhirTesterMvcConfig; import ca.uhn.fhir.to.TesterConfig; import org.springframework.context.annotation.Bean; @@ -36,17 +37,17 @@ public class FhirTesterConfig { @Bean public TesterConfig testerConfig(AppProperties appProperties) { TesterConfig retVal = new TesterConfig(); - appProperties.getTester().entrySet().stream().forEach(t -> { - retVal - .addServer() - .withId(t.getKey()) - .withFhirVersion(t.getValue().getFhir_version()) - .withBaseUrl(t.getValue().getServer_address()) - .withName(t.getValue().getName()); - retVal.setRefuseToFetchThirdPartyUrls( - t.getValue().getRefuse_to_fetch_third_party_urls()); + appProperties.getTester().forEach((key, value) -> { + retVal + .addServer() + .withId(key) + .withFhirVersion(value.getFhir_version()) + .withBaseUrl(value.getServer_address()) + .withName(value.getName()); + retVal.setRefuseToFetchThirdPartyUrls( + value.getRefuse_to_fetch_third_party_urls()); - }); + }); return retVal; } 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 new file mode 100644 index 0000000..b2f108a --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/StarterJpaConfig.java @@ -0,0 +1,433 @@ +package ca.uhn.fhir.jpa.starter.common; + +import ca.uhn.fhir.batch2.jobs.imprt.BulkDataImportProvider; +import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; +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; +import ca.uhn.fhir.jpa.batch.config.NonPersistedBatchConfigurer; +import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor; +import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider; +import ca.uhn.fhir.jpa.bulk.export.provider.BulkDataExportProvider; +import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; +import ca.uhn.fhir.jpa.config.util.ResourceCountCacheUtil; +import ca.uhn.fhir.jpa.config.util.ValidationSupportConfigUtil; +import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl; +import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; +import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl; +import ca.uhn.fhir.jpa.dao.search.HSearchSortHelperImpl; +import ca.uhn.fhir.jpa.dao.search.IHSearchSortHelper; +import ca.uhn.fhir.jpa.graphql.GraphQLProvider; +import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; +import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor; +import ca.uhn.fhir.jpa.packages.IPackageInstallerSvc; +import ca.uhn.fhir.jpa.packages.PackageInstallationSpec; +import ca.uhn.fhir.jpa.partition.PartitionManagementProvider; +import ca.uhn.fhir.jpa.provider.*; +import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; +import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; +import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc; +import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl; +import ca.uhn.fhir.jpa.starter.AppProperties; +import ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory; +import ca.uhn.fhir.jpa.starter.util.EnvironmentHelper; +import ca.uhn.fhir.jpa.subscription.util.SubscriptionDebugLogInterceptor; +import ca.uhn.fhir.jpa.util.ResourceCountCache; +import ca.uhn.fhir.jpa.validation.JpaValidationSupportChain; +import ca.uhn.fhir.mdm.dao.IMdmLinkDao; +import ca.uhn.fhir.mdm.provider.MdmProviderLoader; +import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; +import ca.uhn.fhir.narrative2.NullNarrativeGenerator; +import ca.uhn.fhir.rest.api.IResourceSupportedSvc; +import ca.uhn.fhir.rest.openapi.OpenApiInterceptor; +import ca.uhn.fhir.rest.server.*; +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.rest.server.util.ISearchParamRegistry; +import ca.uhn.fhir.validation.IValidatorModule; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import org.hl7.fhir.common.hapi.validation.support.CachingValidationSupport; +import org.springframework.batch.core.configuration.annotation.BatchConfigurer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.http.HttpHeaders; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; +import org.springframework.web.cors.CorsConfiguration; + +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.*; + +import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR; + +@Configuration +public class StarterJpaConfig { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(StarterJpaConfig.class); + + @Bean + public IFulltextSearchSvc fullTextSearchSvc() { + return new FulltextSearchSvcImpl(); + } + + @Bean + public IStaleSearchDeletingSvc staleSearchDeletingSvc() { + return new StaleSearchDeletingSvcImpl(); + } + + @Primary + @Bean + public CachingValidationSupport validationSupportChain(JpaValidationSupportChain theJpaValidationSupportChain) { + return ValidationSupportConfigUtil.newCachingValidationSupport(theJpaValidationSupportChain); + } + + @Bean + public BatchConfigurer batchConfigurer() { + return new NonPersistedBatchConfigurer(); + } + + @Autowired + private ConfigurableEnvironment configurableEnvironment; + + /** + * Customize the default/max page sizes for search results. You can set these however + * you want, although very large page sizes will require a lot of RAM. + */ + @Bean + public DatabaseBackedPagingProvider databaseBackedPagingProvider(AppProperties appProperties) { + DatabaseBackedPagingProvider pagingProvider = new DatabaseBackedPagingProvider(); + pagingProvider.setDefaultPageSize(appProperties.getDefault_page_size()); + pagingProvider.setMaximumPageSize(appProperties.getMax_page_size()); + return pagingProvider; + } + + @Bean + public IResourceSupportedSvc resourceSupportedSvc(IDaoRegistry theDaoRegistry) { + return new DaoRegistryResourceSupportedSvc(theDaoRegistry); + } + + @Bean(name = "myResourceCountsCache") + public ResourceCountCache resourceCountsCache(IFhirSystemDao theSystemDao) { + return ResourceCountCacheUtil.newResourceCountCache(theSystemDao); + } + + @Primary + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource myDataSource, ConfigurableListableBeanFactory myConfigurableListableBeanFactory, FhirContext theFhirContext) { + LocalContainerEntityManagerFactoryBean retVal = HapiEntityManagerFactoryUtil.newEntityManagerFactory(myConfigurableListableBeanFactory, theFhirContext); + retVal.setPersistenceUnitName("HAPI_PU"); + + try { + retVal.setDataSource(myDataSource); + } catch (Exception e) { + throw new ConfigurationException("Could not set the data source due to a configuration issue", e); + } + retVal.setJpaProperties(EnvironmentHelper.getHibernateProperties(configurableEnvironment, myConfigurableListableBeanFactory)); + return retVal; + } + + @Bean + @Primary + public JpaTransactionManager hapiTransactionManager(EntityManagerFactory entityManagerFactory) { + JpaTransactionManager retVal = new JpaTransactionManager(); + retVal.setEntityManagerFactory(entityManagerFactory); + return retVal; + } + + @Bean + public IHSearchSortHelper hSearchSortHelper(ISearchParamRegistry mySearchParamRegistry) { + return new HSearchSortHelperImpl(mySearchParamRegistry); + } + + + @Bean + @ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") + public RepositoryValidatingInterceptor repositoryValidatingInterceptor(IRepositoryValidationInterceptorFactory factory) { + return factory.buildUsingStoredStructureDefinitions(); + } + + @Bean + public LoggingInterceptor loggingInterceptor(AppProperties appProperties) { + + /* + * Add some logging for each request + */ + + LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); + loggingInterceptor.setLoggerName(appProperties.getLogger().getName()); + loggingInterceptor.setMessageFormat(appProperties.getLogger().getFormat()); + loggingInterceptor.setErrorMessageFormat(appProperties.getLogger().getError_format()); + loggingInterceptor.setLogExceptions(appProperties.getLogger().getLog_exceptions()); + return loggingInterceptor; + } + + @Bean + @Primary + /* + This bean is currently necessary to override from MDM settings + */ + IMdmLinkDao mdmLinkDao() { + return new MdmLinkDaoJpaImpl(); + } + + + @Bean + @ConditionalOnProperty(prefix = "hapi.fhir", name = "cors") + public CorsInterceptor corsInterceptor(AppProperties appProperties) { + // Define your CORS configuration. This is an example + // showing a typical setup. You should customize this + // to your specific needs + ourLog.info("CORS is enabled on this server"); + CorsConfiguration config = new CorsConfiguration(); + config.addAllowedHeader(HttpHeaders.ORIGIN); + config.addAllowedHeader(HttpHeaders.ACCEPT); + config.addAllowedHeader(HttpHeaders.CONTENT_TYPE); + config.addAllowedHeader(HttpHeaders.AUTHORIZATION); + config.addAllowedHeader(HttpHeaders.CACHE_CONTROL); + config.addAllowedHeader("x-fhir-starter"); + config.addAllowedHeader("X-Requested-With"); + config.addAllowedHeader("Prefer"); + + List allAllowedCORSOrigins = appProperties.getCors().getAllowed_origin(); + allAllowedCORSOrigins.forEach(config::addAllowedOriginPattern); + ourLog.info("CORS allows the following origins: " + String.join(", ", allAllowedCORSOrigins)); + + config.addExposedHeader("Location"); + config.addExposedHeader("Content-Location"); + config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH", "HEAD")); + config.setAllowCredentials(appProperties.getCors().getAllow_Credentials()); + + // Create the interceptor and register it + return new CorsInterceptor(config); + + } + + @Bean + public RestfulServer restfulServer(IFhirSystemDao fhirSystemDao, AppProperties appProperties, DaoRegistry daoRegistry, Optional mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional terminologyUploaderProvider, Optional subscriptionTriggeringProvider, Optional corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc) { + RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext()); + + List supportedResourceTypes = appProperties.getSupported_resource_types(); + + if (!supportedResourceTypes.isEmpty()) { + if (!supportedResourceTypes.contains("SearchParameter")) { + supportedResourceTypes.add("SearchParameter"); + } + daoRegistry.setSupportedResourceTypes(supportedResourceTypes); + } + + if (appProperties.getNarrative_enabled()) { + fhirSystemDao.getContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + } else { + fhirSystemDao.getContext().setNarrativeGenerator(new NullNarrativeGenerator()); + } + + if (appProperties.getMdm_enabled()) mdmProviderProvider.get().loadProvider(); + + fhirServer.registerProviders(resourceProviderFactory.createProviders()); + fhirServer.registerProvider(jpaSystemProvider); + fhirServer.setServerConformanceProvider(calculateConformanceProvider(fhirSystemDao, fhirServer, daoConfig, searchParamRegistry, theValidationSupport)); + + /* + * ETag Support + */ + + if (!appProperties.getEtag_support_enabled()) fhirServer.setETagSupport(ETagSupportEnum.DISABLED); + + + /* + * Default to JSON and pretty printing + */ + fhirServer.setDefaultPrettyPrint(appProperties.getDefault_pretty_print()); + + /* + * Default encoding + */ + fhirServer.setDefaultResponseEncoding(appProperties.getDefault_encoding()); + + /* + * This configures the server to page search results to and from + * the database, instead of only paging them to memory. This may mean + * a performance hit when performing searches that return lots of results, + * but makes the server much more scalable. + */ + + fhirServer.setPagingProvider(databaseBackedPagingProvider); + + /* + * This interceptor formats the output using nice colourful + * HTML output when the request is detected to come from a + * browser. + */ + fhirServer.registerInterceptor(new ResponseHighlighterInterceptor()); + + if (appProperties.getFhirpath_interceptor_enabled()) { + fhirServer.registerInterceptor(new FhirPathFilterInterceptor()); + } + + fhirServer.registerInterceptor(loggingInterceptor); + + /* + * If you are hosting this server at a specific DNS name, the server will try to + * figure out the FHIR base URL based on what the web container tells it, but + * 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 = appProperties.getServer_address(); + if (!Strings.isNullOrEmpty(serverAddress)) { + fhirServer.setServerAddressStrategy(new HardcodedServerAddressStrategy(serverAddress)); + } else if (appProperties.getUse_apache_address_strategy()) { + boolean useHttps = appProperties.getUse_apache_address_strategy_https(); + fhirServer.setServerAddressStrategy(useHttps ? ApacheProxyAddressStrategy.forHttps() : ApacheProxyAddressStrategy.forHttp()); + } else { + fhirServer.setServerAddressStrategy(new IncomingRequestAddressStrategy()); + } + + /* + * If you are using DSTU3+, you may want to add a terminology uploader, which allows + * uploading of external terminologies such as Snomed CT. Note that this uploader + * does not have any security attached (any anonymous user may use it by default) + * so it is a potential security vulnerability. Consider using an AuthorizationInterceptor + * with this feature. + */ + if (fhirSystemDao.getContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { // <-- ENABLED RIGHT NOW + fhirServer.registerProvider(terminologyUploaderProvider.get()); + } + + // If you want to enable the $trigger-subscription operation to allow + // manual triggering of a subscription delivery, enable this provider + if (true) { // <-- ENABLED RIGHT NOW + fhirServer.registerProvider(subscriptionTriggeringProvider.get()); + } + + corsInterceptor.ifPresent(fhirServer::registerInterceptor); + + if (appProperties.getSubscription() != null) { + // Subscription debug logging + fhirServer.registerInterceptor(new SubscriptionDebugLogInterceptor()); + } + + if (appProperties.getAllow_cascading_deletes()) { + CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(fhirSystemDao.getContext(), daoRegistry, interceptorBroadcaster); + fhirServer.registerInterceptor(cascadingDeleteInterceptor); + } + + // Binary Storage + if (appProperties.getBinary_storage_enabled() && binaryAccessProvider.isPresent()) { + fhirServer.registerProvider(binaryAccessProvider.get()); + fhirServer.registerInterceptor(binaryStorageInterceptor); + } + + // Validation + + if (validatorModule != null) { + if (appProperties.getValidation().getRequests_enabled()) { + RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor(); + interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); + interceptor.setValidatorModules(Collections.singletonList(validatorModule)); + fhirServer.registerInterceptor(interceptor); + } + if (appProperties.getValidation().getResponses_enabled()) { + ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor(); + interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR); + interceptor.setValidatorModules(Collections.singletonList(validatorModule)); + fhirServer.registerInterceptor(interceptor); + } + } + + // GraphQL + if (appProperties.getGraphql_enabled()) { + if (fhirSystemDao.getContext().getVersion().getVersion().isEqualOrNewerThan(FhirVersionEnum.DSTU3)) { + fhirServer.registerProvider(graphQLProvider.get()); + } + } + + if (appProperties.getOpenapi_enabled()) { + fhirServer.registerInterceptor(new OpenApiInterceptor()); + } + + // Bulk Export + if (appProperties.getBulk_export_enabled()) { + fhirServer.registerProvider(bulkDataExportProvider); + } + + //Bulk Import + if (appProperties.getBulk_import_enabled()) { + fhirServer.registerProvider(bulkDataImportProvider); + } + + + // valueSet Operations i.e $expand + fhirServer.registerProvider(theValueSetOperationProvider); + + //reindex Provider $reindex + fhirServer.registerProvider(reindexProvider); + + // Partitioning + if (appProperties.getPartitioning() != null) { + fhirServer.registerInterceptor(new RequestTenantPartitionInterceptor()); + fhirServer.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy()); + fhirServer.registerProviders(partitionManagementProvider); + } + + + repositoryValidatingInterceptor.ifPresent(fhirServer::registerInterceptor); + + if (appProperties.getImplementationGuides() != null) { + Map guides = appProperties.getImplementationGuides(); + for (Map.Entry guide : guides.entrySet()) { + PackageInstallationSpec packageInstallationSpec = new PackageInstallationSpec().setPackageUrl(guide.getValue().getUrl()).setName(guide.getValue().getName()).setVersion(guide.getValue().getVersion()).setInstallMode(PackageInstallationSpec.InstallModeEnum.STORE_AND_INSTALL); + if (appProperties.getInstall_transitive_ig_dependencies()) { + packageInstallationSpec.setFetchDependencies(true); + packageInstallationSpec.setDependencyExcludes(ImmutableList.of("hl7.fhir.r2.core", "hl7.fhir.r3.core", "hl7.fhir.r4.core", "hl7.fhir.r5.core")); + } + packageInstallerSvc.install(packageInstallationSpec); + } + } + + return fhirServer; + } + + public static IServerConformanceProvider calculateConformanceProvider(IFhirSystemDao fhirSystemDao, RestfulServer fhirServer, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport) { + FhirVersionEnum fhirVersion = fhirSystemDao.getContext().getVersion().getVersion(); + if (fhirVersion == FhirVersionEnum.DSTU2) { + + JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(fhirServer, fhirSystemDao, daoConfig); + confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server"); + return confProvider; + } else if (fhirVersion == FhirVersionEnum.DSTU3) { + + JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry); + confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server"); + return confProvider; + } else if (fhirVersion == FhirVersionEnum.R4) { + + JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport); + confProvider.setImplementationDescription("HAPI FHIR R4 Server"); + return confProvider; + } else if (fhirVersion == FhirVersionEnum.R5) { + + JpaCapabilityStatementProvider confProvider = new JpaCapabilityStatementProvider(fhirServer, fhirSystemDao, daoConfig, searchParamRegistry, theValidationSupport); + confProvider.setImplementationDescription("HAPI FHIR R5 Server"); + return confProvider; + } else { + throw new IllegalStateException(); + } + } +} + diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/IRepositoryValidationInterceptorFactory.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/IRepositoryValidationInterceptorFactory.java similarity index 63% rename from src/main/java/ca/uhn/fhir/jpa/starter/IRepositoryValidationInterceptorFactory.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/IRepositoryValidationInterceptorFactory.java index 13262a1..67a40c7 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/IRepositoryValidationInterceptorFactory.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/IRepositoryValidationInterceptorFactory.java @@ -1,8 +1,10 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common.validation; import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor; public interface IRepositoryValidationInterceptorFactory { + + String ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR = "enable_repository_validating_interceptor"; RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions(); RepositoryValidatingInterceptor build(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java similarity index 84% rename from src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryDstu3.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java index a68ae3e..f8874b3 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryDstu3.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryDstu3.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR; + /** * This class can be customized to enable the {@link RepositoryValidatingInterceptor} * on this server. @@ -26,7 +28,7 @@ import java.util.stream.Collectors; * The enable_repository_validating_interceptor property must be enabled in application.yaml * in order to use this class. */ -@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true") +@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") @Configuration @Conditional(OnDSTU3Condition.class) public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryValidationInterceptorFactory { @@ -50,10 +52,9 @@ public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryV .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); - structureDefintions.entrySet().forEach(structureDefinitionListEntry -> - { - String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new); - repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); + structureDefintions.forEach((key, value) -> { + String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new); + repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); }); List rules = repositoryValidatingRuleBuilder.build(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java similarity index 84% rename from src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR4.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java index a563d3c..c4fca01 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR4.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR; + /** * This class can be customized to enable the {@link ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor} * on this server. @@ -26,7 +28,7 @@ import java.util.stream.Collectors; * The enable_repository_validating_interceptor property must be enabled in application.yaml * in order to use this class. */ -@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true") +@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") @Configuration @Conditional(OnR4Condition.class) public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryValidationInterceptorFactory { @@ -51,10 +53,9 @@ public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryVali .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); - structureDefintions.entrySet().forEach(structureDefinitionListEntry -> - { - String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new); - repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); + structureDefintions.forEach((key, value) -> { + String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new); + repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); }); List rules = repositoryValidatingRuleBuilder.build(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java similarity index 84% rename from src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR5.java rename to src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java index 7702ef0..594800d 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/RepositoryValidationInterceptorFactoryR5.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/RepositoryValidationInterceptorFactoryR5.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.common.validation; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; @@ -19,6 +19,8 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import static ca.uhn.fhir.jpa.starter.common.validation.IRepositoryValidationInterceptorFactory.ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR; + /** * This class can be customized to enable the {@link RepositoryValidatingInterceptor} * on this server. @@ -26,7 +28,7 @@ import java.util.stream.Collectors; * The enable_repository_validating_interceptor property must be enabled in application.yaml * in order to use this class. */ -@ConditionalOnProperty(prefix = "hapi.fhir", name = "enable_repository_validating_interceptor", havingValue = "true") +@ConditionalOnProperty(prefix = "hapi.fhir", name = ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR, havingValue = "true") @Configuration @Conditional(OnR5Condition.class) public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryValidationInterceptorFactory { @@ -50,10 +52,9 @@ public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryVali .map(StructureDefinition.class::cast) .collect(Collectors.groupingBy(StructureDefinition::getType)); - structureDefintions.entrySet().forEach(structureDefinitionListEntry -> - { - String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new); - repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); + structureDefintions.forEach((key, value) -> { + String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new); + repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles(); }); List rules = repositoryValidatingRuleBuilder.build(); diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java b/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java index 0a5f7f4..38622ba 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/cql/CqlConfigCondition.java @@ -9,7 +9,6 @@ public class CqlConfigCondition implements Condition { @Override public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) { String property = theConditionContext.getEnvironment().getProperty("hapi.fhir.cql_enabled"); - boolean enabled = Boolean.parseBoolean(property); - return enabled; + return Boolean.parseBoolean(property); } } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java b/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java similarity index 95% rename from src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java rename to src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java index 732705a..41bb556 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/EnvironmentHelper.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/util/EnvironmentHelper.java @@ -1,9 +1,10 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.util; import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers; import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder; import org.apache.lucene.util.Version; +import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy; import org.hibernate.cfg.AvailableSettings; import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchBackendSettings; import org.hibernate.search.backend.elasticsearch.index.IndexStatus; @@ -27,6 +28,8 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; +import static java.util.Objects.requireNonNullElse; + public class EnvironmentHelper { public static Properties getHibernateProperties(ConfigurableEnvironment environment, @@ -41,7 +44,7 @@ public class EnvironmentHelper { //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()); + properties.putIfAbsent(AvailableSettings.PHYSICAL_NAMING_STRATEGY, CamelCaseToUnderscoresNamingStrategy.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)); @@ -102,18 +105,6 @@ public class EnvironmentHelper { 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); } diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaHibernatePropertiesProvider.java b/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java similarity index 96% rename from src/main/java/ca/uhn/fhir/jpa/starter/JpaHibernatePropertiesProvider.java rename to src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java index d090b9c..83125a9 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaHibernatePropertiesProvider.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/util/JpaHibernatePropertiesProvider.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.starter; +package ca.uhn.fhir.jpa.starter.util; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider; diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b8c322c..b1baff5 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -89,7 +89,7 @@ hapi: # default_pretty_print: true # default_page_size: 20 # delete_expunge_enabled: true - # enable_repository_validating_interceptor: false + # enable_repository_validating_interceptor: true # enable_index_missing_fields: false # enable_index_of_type: true # enable_index_contained_resource: false diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java b/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java deleted file mode 100644 index d460943..0000000 --- a/src/test/java/ca/uhn/fhir/jpa/starter/Demo.java +++ /dev/null @@ -1,20 +0,0 @@ -package ca.uhn.fhir.jpa.starter; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration; -import org.springframework.boot.web.servlet.ServletComponentScan; - -@ServletComponentScan(basePackageClasses = {JpaRestfulServer.class}) -@SpringBootApplication(exclude = ElasticsearchRestClientAutoConfiguration.class) -public class Demo { - - public static void main(String[] args) { - - System.setProperty("spring.profiles.active", "r4"); - System.setProperty("spring.batch.job.enabled", "false"); - SpringApplication.run(Demo.class, args); - - //Server is now accessible at eg. http://localhost:8080/fhir/metadata - } -} diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java index b4ca6c3..0dfcf83 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -113,7 +113,7 @@ public class ExampleServerDstu3IT implements IServerSupport { .execute(); List response = outParams.getParameter(); - Assert.assertTrue(!response.isEmpty()); + Assert.assertFalse(response.isEmpty()); Parameters.ParametersParameterComponent component = response.get(0); Assert.assertTrue(component.getResource() instanceof MeasureReport); MeasureReport report = (MeasureReport) component.getResource(); @@ -149,7 +149,7 @@ public class ExampleServerDstu3IT implements IServerSupport { private Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException { String json = stringFromResource(theLocation); Bundle bundle = (Bundle) theCtx.newJsonParser().parseResource(json); - Bundle result = (Bundle) theClient.transaction().withBundle(bundle).execute(); + Bundle result = theClient.transaction().withBundle(bundle).execute(); return result; } diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java index 7b05487..47834f5 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -1,15 +1,11 @@ package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.partition.SystemRequestDetails; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.rest.api.CacheControlDirective; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.api.MethodOutcome; -import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; - import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java b/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java index 9318a0a..7734a23 100644 --- a/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/SocketImplementation.java @@ -15,10 +15,10 @@ import java.util.List; public class SocketImplementation { private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(SocketImplementation.class); - private String myCriteria; + private final String myCriteria; protected String myError; protected boolean myGotBound; - private List myMessages = new ArrayList(); + private final List myMessages = new ArrayList(); protected int myPingCount; protected String mySubsId; private Session session;