Merge branch 'rel_4_0_0'
This commit is contained in:
@@ -13,6 +13,8 @@ public class ApplicationContext extends AnnotationConfigWebApplicationContext {
|
||||
register(FhirServerConfigDstu3.class, FhirServerConfigCommon.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
register(FhirServerConfigR4.class, FhirServerConfigCommon.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
register(FhirServerConfigR5.class, FhirServerConfigCommon.class);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
package ca.uhn.fhir.jpa.starter;
|
||||
|
||||
import ca.uhn.fhir.jpa.binstore.DatabaseBlobBinaryStorageSvcImpl;
|
||||
import ca.uhn.fhir.jpa.binstore.IBinaryStorageSvc;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
|
||||
import ca.uhn.fhir.jpa.subscription.module.cache.SubscriptionDeliveryHandlerFactory;
|
||||
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.IEmailSender;
|
||||
import ca.uhn.fhir.jpa.subscription.module.subscriber.email.JavaMailEmailSender;
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.hl7.fhir.instance.model.Subscription;
|
||||
import org.hl7.fhir.dstu2.model.Subscription;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.thymeleaf.util.Validate;
|
||||
|
||||
@@ -108,6 +111,8 @@ public class FhirServerConfigCommon {
|
||||
retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.WEBSOCKET);
|
||||
}
|
||||
|
||||
retVal.setFilterParameterEnabled(HapiProperties.getFilterSearchEnabled());
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@@ -149,6 +154,12 @@ public class FhirServerConfigCommon {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public IBinaryStorageSvc binaryStorageSvc() {
|
||||
return new DatabaseBlobBinaryStorageSvcImpl();
|
||||
}
|
||||
|
||||
@Bean()
|
||||
public IEmailSender emailSender() {
|
||||
if (this.emailEnabled) {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package ca.uhn.fhir.jpa.starter;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.jpa.config.BaseJavaConfigR4;
|
||||
import ca.uhn.fhir.jpa.config.BaseJavaConfigR5;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@Configuration
|
||||
public class FhirServerConfigR5 extends BaseJavaConfigR5 {
|
||||
|
||||
@Autowired
|
||||
private DataSource myDataSource;
|
||||
|
||||
/**
|
||||
* We override the paging provider definition so that we can 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.
|
||||
*/
|
||||
@Override
|
||||
public DatabaseBackedPagingProvider databaseBackedPagingProvider() {
|
||||
DatabaseBackedPagingProvider pagingProvider = super.databaseBackedPagingProvider();
|
||||
pagingProvider.setDefaultPageSize(HapiProperties.getDefaultPageSize());
|
||||
pagingProvider.setMaximumPageSize(HapiProperties.getMaximumPageSize());
|
||||
return pagingProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean()
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory();
|
||||
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(HapiProperties.getProperties());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean()
|
||||
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
|
||||
JpaTransactionManager retVal = new JpaTransactionManager();
|
||||
retVal.setEntityManagerFactory(entityManagerFactory);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,11 +6,17 @@ import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
public class HapiProperties {
|
||||
public static final String BINARY_STORAGE_ENABLED = "binary_storage.enabled";
|
||||
static final String ALLOW_EXTERNAL_REFERENCES = "allow_external_references";
|
||||
static final String ALLOW_MULTIPLE_DELETE = "allow_multiple_delete";
|
||||
static final String ALLOW_PLACEHOLDER_REFERENCES = "allow_placeholder_references";
|
||||
@@ -25,6 +31,7 @@ public class HapiProperties {
|
||||
static final String DEFAULT_PRETTY_PRINT = "default_pretty_print";
|
||||
static final String ETAG_SUPPORT = "etag_support";
|
||||
static final String FHIR_VERSION = "fhir_version";
|
||||
static final String ALLOW_CASCADING_DELETES = "allow_cascading_deletes";
|
||||
static final String HAPI_PROPERTIES = "hapi.properties";
|
||||
static final String LOGGER_ERROR_FORMAT = "logger.error_format";
|
||||
static final String LOGGER_FORMAT = "logger.format";
|
||||
@@ -46,7 +53,10 @@ public class HapiProperties {
|
||||
static final String ALLOW_CONTAINS_SEARCHES = "allow_contains_searches";
|
||||
static final String ALLOW_OVERRIDE_DEFAULT_SEARCH_PARAMS = "allow_override_default_search_params";
|
||||
static final String EMAIL_FROM = "email.from";
|
||||
|
||||
private static final String VALIDATE_REQUESTS_ENABLED = "validation.requests.enabled";
|
||||
private static final String VALIDATE_RESPONSES_ENABLED = "validation.responses.enabled";
|
||||
private static final String FILTER_SEARCH_ENABLED = "filter_search.enabled";
|
||||
private static final String GRAPHQL_ENABLED = "graphql.enabled";
|
||||
private static Properties properties;
|
||||
|
||||
/*
|
||||
@@ -69,7 +79,7 @@ public class HapiProperties {
|
||||
public static Properties getProperties() {
|
||||
if (properties == null) {
|
||||
// Load the configurable properties file
|
||||
try (InputStream in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES)){
|
||||
try (InputStream in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES)) {
|
||||
HapiProperties.properties = new Properties();
|
||||
HapiProperties.properties.load(in);
|
||||
} catch (Exception e) {
|
||||
@@ -77,8 +87,8 @@ public class HapiProperties {
|
||||
}
|
||||
|
||||
Properties overrideProps = loadOverrideProperties();
|
||||
if(overrideProps != null) {
|
||||
properties.putAll(overrideProps);
|
||||
if (overrideProps != null) {
|
||||
properties.putAll(overrideProps);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,17 +98,17 @@ public class HapiProperties {
|
||||
/**
|
||||
* If a configuration file path is explicitly specified via -Dhapi.properties=<path>, the properties there will
|
||||
* be used to override the entries in the default hapi.properties file (currently under WEB-INF/classes)
|
||||
*
|
||||
* @return properties loaded from the explicitly specified configuraiton file if there is one, or null otherwise.
|
||||
*/
|
||||
private static Properties loadOverrideProperties() {
|
||||
String confFile = System.getProperty(HAPI_PROPERTIES);
|
||||
if(confFile != null) {
|
||||
if (confFile != null) {
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
props.load(new FileInputStream(confFile));
|
||||
return props;
|
||||
}
|
||||
catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
throw new ConfigurationException("Could not load HAPI properties file: " + confFile, e);
|
||||
}
|
||||
}
|
||||
@@ -140,6 +150,10 @@ public class HapiProperties {
|
||||
return Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
private static boolean getBooleanProperty(String propertyName, boolean defaultValue) {
|
||||
return getBooleanProperty(propertyName, Boolean.valueOf(defaultValue));
|
||||
}
|
||||
|
||||
private static Integer getIntegerProperty(String propertyName, Integer defaultValue) {
|
||||
String value = HapiProperties.getProperty(propertyName);
|
||||
|
||||
@@ -160,6 +174,10 @@ public class HapiProperties {
|
||||
return FhirVersionEnum.DSTU3;
|
||||
}
|
||||
|
||||
public static boolean isBinaryStorageEnabled() {
|
||||
return HapiProperties.getBooleanProperty(BINARY_STORAGE_ENABLED, true);
|
||||
}
|
||||
|
||||
public static ETagSupportEnum getEtagSupport() {
|
||||
String etagSupportString = HapiProperties.getProperty(ETAG_SUPPORT);
|
||||
|
||||
@@ -240,6 +258,10 @@ public class HapiProperties {
|
||||
return HapiProperties.getBooleanProperty(ALLOW_MULTIPLE_DELETE, false);
|
||||
}
|
||||
|
||||
public static Boolean getAllowCascadingDeletes() {
|
||||
return HapiProperties.getBooleanProperty(ALLOW_CASCADING_DELETES, false);
|
||||
}
|
||||
|
||||
public static Boolean getAllowExternalReferences() {
|
||||
return HapiProperties.getBooleanProperty(ALLOW_EXTERNAL_REFERENCES, false);
|
||||
}
|
||||
@@ -264,6 +286,14 @@ public class HapiProperties {
|
||||
return HapiProperties.getProperty(CORS_ALLOWED_ORIGIN, "*");
|
||||
}
|
||||
|
||||
public static Set<String> getSupportedResourceTypes() {
|
||||
String[] types = defaultString(getProperty("supported_resource_types")).split(",");
|
||||
return Arrays.stream(types)
|
||||
.map(t -> trim(t))
|
||||
.filter(t -> isNotBlank(t))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static String getServerName() {
|
||||
return HapiProperties.getProperty(SERVER_NAME, "Local Tester");
|
||||
}
|
||||
@@ -328,4 +358,22 @@ public class HapiProperties {
|
||||
public static Boolean getCorsAllowedCredentials() {
|
||||
return HapiProperties.getBooleanProperty(CORS_ALLOW_CREDENTIALS, false);
|
||||
}
|
||||
|
||||
public static boolean getValidateRequestsEnabled() {
|
||||
return HapiProperties.getBooleanProperty(VALIDATE_REQUESTS_ENABLED, false);
|
||||
}
|
||||
|
||||
public static boolean getValidateResponsesEnabled() {
|
||||
return HapiProperties.getBooleanProperty(VALIDATE_RESPONSES_ENABLED, false);
|
||||
}
|
||||
|
||||
public static boolean getFilterSearchEnabled() {
|
||||
return HapiProperties.getBooleanProperty(FILTER_SEARCH_ENABLED, true);
|
||||
}
|
||||
|
||||
public static boolean getGraphqlEnabled() {
|
||||
return HapiProperties.getBooleanProperty(GRAPHQL_ENABLED, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2,18 +2,20 @@ package ca.uhn.fhir.jpa.starter;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.jpa.binstore.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
|
||||
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
|
||||
import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor;
|
||||
import ca.uhn.fhir.jpa.provider.*;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaConformanceProviderR5;
|
||||
import ca.uhn.fhir.jpa.provider.r5.JpaSystemProviderR5;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
|
||||
import ca.uhn.fhir.jpa.subscription.module.interceptor.SubscriptionDebugLogInterceptor;
|
||||
@@ -22,9 +24,9 @@ import ca.uhn.fhir.model.dstu2.composite.MetaDt;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.*;
|
||||
import ca.uhn.fhir.validation.IValidatorModule;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Meta;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
@@ -33,6 +35,8 @@ import org.springframework.web.cors.CorsConfiguration;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
public class JpaRestfulServer extends RestfulServer {
|
||||
|
||||
@@ -48,6 +52,12 @@ public class JpaRestfulServer extends RestfulServer {
|
||||
* specified in the properties file.
|
||||
*/
|
||||
ApplicationContext appCtx = (ApplicationContext) getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT");
|
||||
// Customize supported resource types
|
||||
Set<String> supportedResourceTypes = HapiProperties.getSupportedResourceTypes();
|
||||
if (!supportedResourceTypes.isEmpty()) {
|
||||
DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class);
|
||||
daoRegistry.setSupportedResourceTypes(supportedResourceTypes);
|
||||
}
|
||||
|
||||
/*
|
||||
* ResourceProviders are fetched from the Spring context
|
||||
@@ -64,6 +74,9 @@ public class JpaRestfulServer extends RestfulServer {
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersR4", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class);
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
resourceProviders = appCtx.getBean("myResourceProvidersR5", ResourceProviderFactory.class);
|
||||
systemProvider = appCtx.getBean("mySystemProviderR5", JpaSystemProviderR5.class);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
@@ -96,6 +109,11 @@ public class JpaRestfulServer extends RestfulServer {
|
||||
JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, appCtx.getBean(DaoConfig.class));
|
||||
confProvider.setImplementationDescription("HAPI FHIR R4 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else if (fhirVersion == FhirVersionEnum.R5) {
|
||||
IFhirSystemDao<org.hl7.fhir.r5.model.Bundle, org.hl7.fhir.r5.model.Meta> systemDao = appCtx.getBean("mySystemDaoR5", IFhirSystemDao.class);
|
||||
JpaConformanceProviderR5 confProvider = new JpaConformanceProviderR5(this, systemDao, appCtx.getBean(DaoConfig.class));
|
||||
confProvider.setImplementationDescription("HAPI FHIR R5 Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
@@ -167,11 +185,7 @@ public class JpaRestfulServer extends RestfulServer {
|
||||
* with this feature.
|
||||
*/
|
||||
if (false) { // <-- DISABLED RIGHT NOW
|
||||
if (fhirVersion == FhirVersionEnum.DSTU3) {
|
||||
registerProvider(appCtx.getBean(TerminologyUploaderProviderDstu3.class));
|
||||
} else if (fhirVersion == FhirVersionEnum.R4) {
|
||||
registerProvider(appCtx.getBean(TerminologyUploaderProviderR4.class));
|
||||
}
|
||||
registerProvider(appCtx.getBean(TerminologyUploaderProvider.class));
|
||||
}
|
||||
|
||||
// If you want to enable the $trigger-subscription operation to allow
|
||||
@@ -226,6 +240,58 @@ public class JpaRestfulServer extends RestfulServer {
|
||||
interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor());
|
||||
}
|
||||
|
||||
// Cascading deletes
|
||||
DaoRegistry daoRegistry = appCtx.getBean(DaoRegistry.class);
|
||||
IInterceptorBroadcaster interceptorBroadcaster = appCtx.getBean(IInterceptorBroadcaster.class);
|
||||
if (HapiProperties.getAllowCascadingDeletes()) {
|
||||
CascadingDeleteInterceptor cascadingDeleteInterceptor = new CascadingDeleteInterceptor(daoRegistry, interceptorBroadcaster);
|
||||
getInterceptorService().registerInterceptor(cascadingDeleteInterceptor);
|
||||
}
|
||||
|
||||
// Binary Storage
|
||||
if (HapiProperties.isBinaryStorageEnabled()) {
|
||||
BinaryStorageInterceptor binaryStorageInterceptor = appCtx.getBean(BinaryStorageInterceptor.class);
|
||||
getInterceptorService().registerInterceptor(binaryStorageInterceptor);
|
||||
}
|
||||
|
||||
// Validation
|
||||
IValidatorModule validatorModule;
|
||||
switch (fhirVersion) {
|
||||
case DSTU3:
|
||||
validatorModule = appCtx.getBean("myInstanceValidatorDstu3", IValidatorModule.class);
|
||||
break;
|
||||
case R4:
|
||||
validatorModule = appCtx.getBean("myInstanceValidatorR4", IValidatorModule.class);
|
||||
break;
|
||||
case R5:
|
||||
validatorModule = appCtx.getBean("myInstanceValidatorR5", IValidatorModule.class);
|
||||
break;
|
||||
default:
|
||||
validatorModule = null;
|
||||
break;
|
||||
}
|
||||
if (validatorModule != null) {
|
||||
if (HapiProperties.getValidateRequestsEnabled()) {
|
||||
RequestValidatingInterceptor interceptor = new RequestValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
if (HapiProperties.getValidateResponsesEnabled()) {
|
||||
ResponseValidatingInterceptor interceptor = new ResponseValidatingInterceptor();
|
||||
interceptor.setFailOnSeverity(ResultSeverityEnum.ERROR);
|
||||
interceptor.setValidatorModules(Collections.singletonList(validatorModule));
|
||||
registerInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
// GraphQL
|
||||
if (HapiProperties.getGraphqlEnabled()) {
|
||||
if (fhirVersion.isEqualOrNewerThan(FhirVersionEnum.DSTU3)) {
|
||||
registerProvider(appCtx.getBean(GraphQLProvider.class));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user