Feat/restructuring (#422)
* Did restructuring and made repo validation interceptor an optional bean instead as it makes it more clean * Moved construction of FHIR servlet into a bean for better reuse of others that would like to depend directly on this library * Disabled default validation enabled
This commit is contained in:
committed by
GitHub
parent
c5e460dab0
commit
d660d5f76d
@@ -81,7 +81,7 @@ public class AppProperties {
|
|||||||
|
|
||||||
private Integer bundle_batch_pool_size = 20;
|
private Integer bundle_batch_pool_size = 20;
|
||||||
private Integer bundle_batch_pool_max_size = 100;
|
private Integer bundle_batch_pool_max_size = 100;
|
||||||
private List<String> local_base_urls = new ArrayList<>();
|
private final List<String> local_base_urls = new ArrayList<>();
|
||||||
|
|
||||||
public Boolean getOpenapi_enabled() {
|
public Boolean getOpenapi_enabled() {
|
||||||
return openapi_enabled;
|
return openapi_enabled;
|
||||||
@@ -203,10 +203,6 @@ public class AppProperties {
|
|||||||
this.supported_resource_types = supported_resource_types;
|
this.supported_resource_types = supported_resource_types;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getSupported_resource_types(List<String> supported_resource_types) {
|
|
||||||
return this.supported_resource_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Logger getLogger() {
|
public Logger getLogger() {
|
||||||
return logger;
|
return logger;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ package ca.uhn.fhir.jpa.starter;
|
|||||||
import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig;
|
import ca.uhn.fhir.batch2.jobs.config.Batch2JobsConfig;
|
||||||
import ca.uhn.fhir.jpa.batch2.JpaBatch2Config;
|
import ca.uhn.fhir.jpa.batch2.JpaBatch2Config;
|
||||||
import ca.uhn.fhir.jpa.starter.annotations.OnEitherVersion;
|
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.starter.mdm.MdmConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
|
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.SubscriptionProcessorConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
import ca.uhn.fhir.jpa.subscription.match.config.WebsocketDispatcherConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig;
|
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.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
|
||||||
import org.springframework.boot.SpringApplication;
|
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.context.support.AnnotationConfigWebApplicationContext;
|
||||||
import org.springframework.web.servlet.DispatcherServlet;
|
import org.springframework.web.servlet.DispatcherServlet;
|
||||||
|
|
||||||
@ServletComponentScan(basePackageClasses = {
|
@ServletComponentScan(basePackageClasses = {RestfulServer.class})
|
||||||
JpaRestfulServer.class})
|
|
||||||
@SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class})
|
@SpringBootApplication(exclude = {ElasticsearchRestClientAutoConfiguration.class})
|
||||||
@Import({
|
@Import({
|
||||||
SubscriptionSubmitterConfig.class,
|
SubscriptionSubmitterConfig.class,
|
||||||
@@ -56,11 +57,10 @@ public class Application extends SpringBootServletInitializer {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Conditional(OnEitherVersion.class)
|
@Conditional(OnEitherVersion.class)
|
||||||
public ServletRegistrationBean hapiServletRegistration() {
|
public ServletRegistrationBean hapiServletRegistration(RestfulServer restfulServer) {
|
||||||
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
|
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
|
||||||
JpaRestfulServer jpaRestfulServer = new JpaRestfulServer();
|
beanFactory.autowireBean(restfulServer);
|
||||||
beanFactory.autowireBean(jpaRestfulServer);
|
servletRegistrationBean.setServlet(restfulServer);
|
||||||
servletRegistrationBean.setServlet(jpaRestfulServer);
|
|
||||||
servletRegistrationBean.addUrlMappings("/fhir/*");
|
servletRegistrationBean.addUrlMappings("/fhir/*");
|
||||||
servletRegistrationBean.setLoadOnStartup(1);
|
servletRegistrationBean.setLoadOnStartup(1);
|
||||||
|
|
||||||
|
|||||||
@@ -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> graphQLProvider;
|
|
||||||
@Autowired
|
|
||||||
BulkDataExportProvider bulkDataExportProvider;
|
|
||||||
@Autowired
|
|
||||||
BulkDataImportProvider bulkDataImportProvider;
|
|
||||||
@Autowired
|
|
||||||
PartitionManagementProvider partitionManagementProvider;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
ValueSetOperationProvider valueSetOperationProvider;
|
|
||||||
@Autowired
|
|
||||||
ReindexProvider reindexProvider;
|
|
||||||
@Autowired
|
|
||||||
BinaryStorageInterceptor binaryStorageInterceptor;
|
|
||||||
@Autowired
|
|
||||||
Optional<BinaryAccessProvider> 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> cqlProviderLoader;
|
|
||||||
@Autowired
|
|
||||||
Optional<MdmProviderLoader> 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<String> 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<String> 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<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides();
|
|
||||||
for (Map.Entry<String, AppProperties.ImplementationGuide> 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.env.ConfigurableEnvironment;
|
import org.springframework.core.env.ConfigurableEnvironment;
|
||||||
@@ -10,12 +10,8 @@ import org.springframework.core.env.ConfigurableEnvironment;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class ElasticsearchConfig {
|
public class ElasticsearchConfig {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElasticsearchConfig.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElasticsearchConfig.class);
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private ConfigurableEnvironment configurableEnvironment;
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ElasticsearchSvcImpl elasticsearchSvc() {
|
public ElasticsearchSvcImpl elasticsearchSvc(ConfigurableEnvironment configurableEnvironment) {
|
||||||
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
|
if (EnvironmentHelper.isElasticsearchEnabled(configurableEnvironment)) {
|
||||||
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
|
String elasticsearchUrl = EnvironmentHelper.getElasticsearchServerUrl(configurableEnvironment);
|
||||||
if (elasticsearchUrl.startsWith("http")) {
|
if (elasticsearchUrl.startsWith("http")) {
|
||||||
@@ -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.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.binary.api.IBinaryStorageSvc;
|
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;
|
||||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings.CrossPartitionReferenceMode;
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings.CrossPartitionReferenceMode;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
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.channel.subscription.SubscriptionDeliveryHandlerFactory;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl;
|
import ca.uhn.fhir.jpa.subscription.match.deliver.email.EmailSenderImpl;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender;
|
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.MailConfig;
|
||||||
import ca.uhn.fhir.rest.server.mail.MailSvc;
|
import ca.uhn.fhir.rest.server.mail.MailSvc;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||||
import org.hl7.fhir.dstu2.model.Subscription;
|
import org.hl7.fhir.dstu2.model.Subscription;
|
||||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -25,6 +28,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the primary configuration file for the example server
|
* This is the primary configuration file for the example server
|
||||||
@@ -78,54 +82,85 @@ public class FhirServerConfigCommon {
|
|||||||
*/
|
*/
|
||||||
@Bean
|
@Bean
|
||||||
public DaoConfig daoConfig(AppProperties appProperties) {
|
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);
|
daoConfig.setIndexMissingFields(appProperties.getEnable_index_missing_fields() ? DaoConfig.IndexEnabledEnum.ENABLED : DaoConfig.IndexEnabledEnum.DISABLED);
|
||||||
retVal.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets());
|
daoConfig.setAutoCreatePlaceholderReferenceTargets(appProperties.getAuto_create_placeholder_reference_targets());
|
||||||
retVal.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write());
|
daoConfig.setEnforceReferentialIntegrityOnWrite(appProperties.getEnforce_referential_integrity_on_write());
|
||||||
retVal.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete());
|
daoConfig.setEnforceReferentialIntegrityOnDelete(appProperties.getEnforce_referential_integrity_on_delete());
|
||||||
retVal.setAllowContainsSearches(appProperties.getAllow_contains_searches());
|
daoConfig.setAllowContainsSearches(appProperties.getAllow_contains_searches());
|
||||||
retVal.setAllowMultipleDelete(appProperties.getAllow_multiple_delete());
|
daoConfig.setAllowMultipleDelete(appProperties.getAllow_multiple_delete());
|
||||||
retVal.setAllowExternalReferences(appProperties.getAllow_external_references());
|
daoConfig.setAllowExternalReferences(appProperties.getAllow_external_references());
|
||||||
retVal.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled());
|
daoConfig.setSchedulingDisabled(!appProperties.getDao_scheduling_enabled());
|
||||||
retVal.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled());
|
daoConfig.setDeleteExpungeEnabled(appProperties.getDelete_expunge_enabled());
|
||||||
retVal.setExpungeEnabled(appProperties.getExpunge_enabled());
|
daoConfig.setExpungeEnabled(appProperties.getExpunge_enabled());
|
||||||
if(appProperties.getSubscription() != null && appProperties.getSubscription().getEmail() != null)
|
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();
|
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));
|
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();
|
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);
|
ourLog.info("Server configured to cache search results for {} milliseconds", reuseCachedSearchResultsMillis);
|
||||||
|
|
||||||
|
|
||||||
Long retainCachedSearchesMinutes = appProperties.getRetain_cached_searches_mins();
|
Long retainCachedSearchesMinutes = appProperties.getRetain_cached_searches_mins();
|
||||||
retVal.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000);
|
daoConfig.setExpireSearchResultsAfterMillis(retainCachedSearchesMinutes * 60 * 1000);
|
||||||
|
|
||||||
if(appProperties.getSubscription() != null) {
|
if(appProperties.getSubscription() != null) {
|
||||||
// Subscriptions are enabled by channel type
|
// Subscriptions are enabled by channel type
|
||||||
if (appProperties.getSubscription().getResthook_enabled()) {
|
if (appProperties.getSubscription().getResthook_enabled()) {
|
||||||
ourLog.info("Enabling REST-hook subscriptions");
|
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) {
|
if (appProperties.getSubscription().getEmail() != null) {
|
||||||
ourLog.info("Enabling email subscriptions");
|
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()) {
|
if (appProperties.getSubscription().getWebsocket_enabled()) {
|
||||||
ourLog.info("Enabling websocket subscriptions");
|
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());
|
daoConfig.setFilterParameterEnabled(appProperties.getFilter_search_enabled());
|
||||||
retVal.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing());
|
daoConfig.setAdvancedHSearchIndexing(appProperties.getAdvanced_lucene_indexing());
|
||||||
retVal.setTreatBaseUrlsAsLocal(new HashSet<>(appProperties.getLocal_base_urls()));
|
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
|
@Bean
|
||||||
@@ -183,24 +218,6 @@ public class FhirServerConfigCommon {
|
|||||||
return modelConfig;
|
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".
|
|
||||||
* <p>
|
|
||||||
* 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
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) {
|
public IBinaryStorageSvc binaryStorageSvc(AppProperties appProperties) {
|
||||||
@@ -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.config.JpaDstu2Config;
|
||||||
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition;
|
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU2Condition;
|
||||||
@@ -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.config.dstu3.JpaDstu3Config;
|
||||||
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
|
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
|
||||||
import ca.uhn.fhir.jpa.starter.cql.StarterCqlDstu3Config;
|
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.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
@@ -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.config.r4.JpaR4Config;
|
||||||
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
|
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
|
||||||
import ca.uhn.fhir.jpa.starter.cql.StarterCqlR4Config;
|
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.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
@@ -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.config.r5.JpaR5Config;
|
||||||
import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition;
|
import ca.uhn.fhir.jpa.starter.annotations.OnR5Condition;
|
||||||
@@ -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.FhirTesterMvcConfig;
|
||||||
import ca.uhn.fhir.to.TesterConfig;
|
import ca.uhn.fhir.to.TesterConfig;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -36,15 +37,15 @@ public class FhirTesterConfig {
|
|||||||
@Bean
|
@Bean
|
||||||
public TesterConfig testerConfig(AppProperties appProperties) {
|
public TesterConfig testerConfig(AppProperties appProperties) {
|
||||||
TesterConfig retVal = new TesterConfig();
|
TesterConfig retVal = new TesterConfig();
|
||||||
appProperties.getTester().entrySet().stream().forEach(t -> {
|
appProperties.getTester().forEach((key, value) -> {
|
||||||
retVal
|
retVal
|
||||||
.addServer()
|
.addServer()
|
||||||
.withId(t.getKey())
|
.withId(key)
|
||||||
.withFhirVersion(t.getValue().getFhir_version())
|
.withFhirVersion(value.getFhir_version())
|
||||||
.withBaseUrl(t.getValue().getServer_address())
|
.withBaseUrl(value.getServer_address())
|
||||||
.withName(t.getValue().getName());
|
.withName(value.getName());
|
||||||
retVal.setRefuseToFetchThirdPartyUrls(
|
retVal.setRefuseToFetchThirdPartyUrls(
|
||||||
t.getValue().getRefuse_to_fetch_third_party_urls());
|
value.getRefuse_to_fetch_third_party_urls());
|
||||||
|
|
||||||
});
|
});
|
||||||
return retVal;
|
return retVal;
|
||||||
@@ -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<String> 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<MdmProviderLoader> mdmProviderProvider, IJpaSystemProvider jpaSystemProvider, ResourceProviderFactory resourceProviderFactory, DaoConfig daoConfig, ISearchParamRegistry searchParamRegistry, IValidationSupport theValidationSupport, DatabaseBackedPagingProvider databaseBackedPagingProvider, LoggingInterceptor loggingInterceptor, Optional<TerminologyUploaderProvider> terminologyUploaderProvider, Optional<SubscriptionTriggeringProvider> subscriptionTriggeringProvider, Optional<CorsInterceptor> corsInterceptor, IInterceptorBroadcaster interceptorBroadcaster, Optional<BinaryAccessProvider> binaryAccessProvider, BinaryStorageInterceptor binaryStorageInterceptor, IValidatorModule validatorModule, Optional<GraphQLProvider> graphQLProvider, BulkDataExportProvider bulkDataExportProvider, BulkDataImportProvider bulkDataImportProvider, ValueSetOperationProvider theValueSetOperationProvider, ReindexProvider reindexProvider, PartitionManagementProvider partitionManagementProvider, Optional<RepositoryValidatingInterceptor> repositoryValidatingInterceptor, IPackageInstallerSvc packageInstallerSvc) {
|
||||||
|
RestfulServer fhirServer = new RestfulServer(fhirSystemDao.getContext());
|
||||||
|
|
||||||
|
List<String> 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<String, AppProperties.ImplementationGuide> guides = appProperties.getImplementationGuides();
|
||||||
|
for (Map.Entry<String, AppProperties.ImplementationGuide> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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;
|
import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor;
|
||||||
|
|
||||||
public interface IRepositoryValidationInterceptorFactory {
|
public interface IRepositoryValidationInterceptorFactory {
|
||||||
|
|
||||||
|
String ENABLE_REPOSITORY_VALIDATING_INTERCEPTOR = "enable_repository_validating_interceptor";
|
||||||
RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions();
|
RepositoryValidatingInterceptor buildUsingStoredStructureDefinitions();
|
||||||
|
|
||||||
RepositoryValidatingInterceptor build();
|
RepositoryValidatingInterceptor build();
|
||||||
@@ -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.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
@@ -19,6 +19,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
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}
|
* This class can be customized to enable the {@link RepositoryValidatingInterceptor}
|
||||||
* on this server.
|
* on this server.
|
||||||
@@ -26,7 +28,7 @@ import java.util.stream.Collectors;
|
|||||||
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||||
* in order to use this class.
|
* 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
|
@Configuration
|
||||||
@Conditional(OnDSTU3Condition.class)
|
@Conditional(OnDSTU3Condition.class)
|
||||||
public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryValidationInterceptorFactory {
|
public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryValidationInterceptorFactory {
|
||||||
@@ -50,10 +52,9 @@ public class RepositoryValidationInterceptorFactoryDstu3 implements IRepositoryV
|
|||||||
.map(StructureDefinition.class::cast)
|
.map(StructureDefinition.class::cast)
|
||||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||||
|
|
||||||
structureDefintions.entrySet().forEach(structureDefinitionListEntry ->
|
structureDefintions.forEach((key, value) -> {
|
||||||
{
|
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||||
String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||||
repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
@@ -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.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
@@ -19,6 +19,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
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}
|
* This class can be customized to enable the {@link ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingInterceptor}
|
||||||
* on this server.
|
* on this server.
|
||||||
@@ -26,7 +28,7 @@ import java.util.stream.Collectors;
|
|||||||
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||||
* in order to use this class.
|
* 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
|
@Configuration
|
||||||
@Conditional(OnR4Condition.class)
|
@Conditional(OnR4Condition.class)
|
||||||
public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryValidationInterceptorFactory {
|
public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryValidationInterceptorFactory {
|
||||||
@@ -51,10 +53,9 @@ public class RepositoryValidationInterceptorFactoryR4 implements IRepositoryVali
|
|||||||
.map(StructureDefinition.class::cast)
|
.map(StructureDefinition.class::cast)
|
||||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||||
|
|
||||||
structureDefintions.entrySet().forEach(structureDefinitionListEntry ->
|
structureDefintions.forEach((key, value) -> {
|
||||||
{
|
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||||
String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||||
repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
@@ -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.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
@@ -19,6 +19,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
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}
|
* This class can be customized to enable the {@link RepositoryValidatingInterceptor}
|
||||||
* on this server.
|
* on this server.
|
||||||
@@ -26,7 +28,7 @@ import java.util.stream.Collectors;
|
|||||||
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
* The <code>enable_repository_validating_interceptor</code> property must be enabled in <code>application.yaml</code>
|
||||||
* in order to use this class.
|
* 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
|
@Configuration
|
||||||
@Conditional(OnR5Condition.class)
|
@Conditional(OnR5Condition.class)
|
||||||
public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryValidationInterceptorFactory {
|
public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryValidationInterceptorFactory {
|
||||||
@@ -50,10 +52,9 @@ public class RepositoryValidationInterceptorFactoryR5 implements IRepositoryVali
|
|||||||
.map(StructureDefinition.class::cast)
|
.map(StructureDefinition.class::cast)
|
||||||
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
.collect(Collectors.groupingBy(StructureDefinition::getType));
|
||||||
|
|
||||||
structureDefintions.entrySet().forEach(structureDefinitionListEntry ->
|
structureDefintions.forEach((key, value) -> {
|
||||||
{
|
String[] urls = value.stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
||||||
String[] urls = structureDefinitionListEntry.getValue().stream().map(StructureDefinition::getUrl).toArray(String[]::new);
|
repositoryValidatingRuleBuilder.forResourcesOfType(key).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
||||||
repositoryValidatingRuleBuilder.forResourcesOfType(structureDefinitionListEntry.getKey()).requireAtLeastOneProfileOf(urls).and().requireValidationToDeclaredProfiles();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
List<IRepositoryValidatingRule> rules = repositoryValidatingRuleBuilder.build();
|
||||||
@@ -9,7 +9,6 @@ public class CqlConfigCondition implements Condition {
|
|||||||
@Override
|
@Override
|
||||||
public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) {
|
public boolean matches(ConditionContext theConditionContext, AnnotatedTypeMetadata theAnnotatedTypeMetadata) {
|
||||||
String property = theConditionContext.getEnvironment().getProperty("hapi.fhir.cql_enabled");
|
String property = theConditionContext.getEnvironment().getProperty("hapi.fhir.cql_enabled");
|
||||||
boolean enabled = Boolean.parseBoolean(property);
|
return Boolean.parseBoolean(property);
|
||||||
return enabled;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.config.HapiFhirLocalContainerEntityManagerFactoryBean;
|
||||||
import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers;
|
import ca.uhn.fhir.jpa.search.HapiHSearchAnalysisConfigurers;
|
||||||
import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder;
|
import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder;
|
||||||
import org.apache.lucene.util.Version;
|
import org.apache.lucene.util.Version;
|
||||||
|
import org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy;
|
||||||
import org.hibernate.cfg.AvailableSettings;
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchBackendSettings;
|
import org.hibernate.search.backend.elasticsearch.cfg.ElasticsearchBackendSettings;
|
||||||
import org.hibernate.search.backend.elasticsearch.index.IndexStatus;
|
import org.hibernate.search.backend.elasticsearch.index.IndexStatus;
|
||||||
@@ -27,6 +28,8 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNullElse;
|
||||||
|
|
||||||
public class EnvironmentHelper {
|
public class EnvironmentHelper {
|
||||||
|
|
||||||
public static Properties getHibernateProperties(ConfigurableEnvironment environment,
|
public static Properties getHibernateProperties(ConfigurableEnvironment environment,
|
||||||
@@ -41,7 +44,7 @@ public class EnvironmentHelper {
|
|||||||
//Spring Boot Autoconfiguration defaults
|
//Spring Boot Autoconfiguration defaults
|
||||||
properties.putIfAbsent(AvailableSettings.SCANNER, "org.hibernate.boot.archive.scan.internal.DisabledScanner");
|
properties.putIfAbsent(AvailableSettings.SCANNER, "org.hibernate.boot.archive.scan.internal.DisabledScanner");
|
||||||
properties.putIfAbsent(AvailableSettings.IMPLICIT_NAMING_STRATEGY, SpringImplicitNamingStrategy.class.getName());
|
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
|
//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));
|
//properties.putIfAbsent(AvailableSettings.BEAN_CONTAINER, new SpringBeanContainer(beanFactory));
|
||||||
|
|
||||||
@@ -102,18 +105,6 @@ public class EnvironmentHelper {
|
|||||||
return properties;
|
return properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Removed when we're up on Java 11
|
|
||||||
private static <T> 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> T requireNonNull(T obj, String message) {
|
|
||||||
if (obj == null)
|
|
||||||
throw new NullPointerException(message);
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getElasticsearchServerUrl(ConfigurableEnvironment environment) {
|
public static String getElasticsearchServerUrl(ConfigurableEnvironment environment) {
|
||||||
return environment.getProperty("elasticsearch.rest_url", String.class);
|
return environment.getProperty("elasticsearch.rest_url", String.class);
|
||||||
}
|
}
|
||||||
@@ -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.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
|
import ca.uhn.fhir.jpa.config.HibernatePropertiesProvider;
|
||||||
@@ -89,7 +89,7 @@ hapi:
|
|||||||
# default_pretty_print: true
|
# default_pretty_print: true
|
||||||
# default_page_size: 20
|
# default_page_size: 20
|
||||||
# delete_expunge_enabled: true
|
# delete_expunge_enabled: true
|
||||||
# enable_repository_validating_interceptor: false
|
# enable_repository_validating_interceptor: true
|
||||||
# enable_index_missing_fields: false
|
# enable_index_missing_fields: false
|
||||||
# enable_index_of_type: true
|
# enable_index_of_type: true
|
||||||
# enable_index_contained_resource: false
|
# enable_index_contained_resource: false
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -113,7 +113,7 @@ public class ExampleServerDstu3IT implements IServerSupport {
|
|||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
List<Parameters.ParametersParameterComponent> response = outParams.getParameter();
|
List<Parameters.ParametersParameterComponent> response = outParams.getParameter();
|
||||||
Assert.assertTrue(!response.isEmpty());
|
Assert.assertFalse(response.isEmpty());
|
||||||
Parameters.ParametersParameterComponent component = response.get(0);
|
Parameters.ParametersParameterComponent component = response.get(0);
|
||||||
Assert.assertTrue(component.getResource() instanceof MeasureReport);
|
Assert.assertTrue(component.getResource() instanceof MeasureReport);
|
||||||
MeasureReport report = (MeasureReport) component.getResource();
|
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 {
|
private Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
|
||||||
String json = stringFromResource(theLocation);
|
String json = stringFromResource(theLocation);
|
||||||
Bundle bundle = (Bundle) theCtx.newJsonParser().parseResource(json);
|
Bundle bundle = (Bundle) theCtx.newJsonParser().parseResource(json);
|
||||||
Bundle result = (Bundle) theClient.transaction().withBundle(bundle).execute();
|
Bundle result = theClient.transaction().withBundle(bundle).execute();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
package ca.uhn.fhir.jpa.starter;
|
package ca.uhn.fhir.jpa.starter;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
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.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
|
||||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||||
|
|||||||
@@ -15,10 +15,10 @@ import java.util.List;
|
|||||||
public class SocketImplementation {
|
public class SocketImplementation {
|
||||||
|
|
||||||
private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(SocketImplementation.class);
|
private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(SocketImplementation.class);
|
||||||
private String myCriteria;
|
private final String myCriteria;
|
||||||
protected String myError;
|
protected String myError;
|
||||||
protected boolean myGotBound;
|
protected boolean myGotBound;
|
||||||
private List<String> myMessages = new ArrayList<String>();
|
private final List<String> myMessages = new ArrayList<String>();
|
||||||
protected int myPingCount;
|
protected int myPingCount;
|
||||||
protected String mySubsId;
|
protected String mySubsId;
|
||||||
private Session session;
|
private Session session;
|
||||||
|
|||||||
Reference in New Issue
Block a user