diff --git a/.travis.yml b/.travis.yml
index 7cd8c41..54d387b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -29,4 +29,7 @@ before_script:
- sudo chown -R travis:travis "$HOME/.m2/repository";
script:
- - mvn install
+ - mvn -U install
+
+
+
diff --git a/pom.xml b/pom.xml
index 0e272c5..9df45f1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
ca.uhn.hapi.fhir
hapi-fhir
- 3.8.0
+ 4.0.0-SNAPSHOT
ca.uhn.hapi.fhir.demo
@@ -138,11 +138,11 @@
-
- org.apache.derby
- derby
+ com.h2database
+ h2
@@ -198,6 +198,13 @@
provided
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-test-utilities
+ ${project.version}
+ test
+
+
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java
index ddddbe0..24b4f79 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/ApplicationContext.java
@@ -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();
}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java
index e44faac..a396ee4 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java
@@ -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) {
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java
new file mode 100644
index 0000000..e176080
--- /dev/null
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR5.java
@@ -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;
+ }
+
+}
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java
index 40faec3..3dac8f4 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java
@@ -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=, 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 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);
+ }
+
}
+
diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java
index ebfbad9..a640740 100644
--- a/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java
+++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java
@@ -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 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 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));
+ }
+ }
+
}
}
diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties
index 6a34a06..0f9bd81 100644
--- a/src/main/resources/hapi.properties
+++ b/src/main/resources/hapi.properties
@@ -22,6 +22,7 @@ allow_override_default_search_params=true
allow_contains_searches=true
allow_multiple_delete=true
allow_external_references=true
+allow_cascading_deletes=true
allow_placeholder_references=true
expunge_enabled=true
persistence_unit_name=HAPI_PU
@@ -29,14 +30,40 @@ logger.name=fhirtest.access
logger.format=Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]
logger.error_format=ERROR - ${requestVerb} ${requestUrl}
logger.log_exceptions=true
-datasource.driver=org.apache.derby.jdbc.EmbeddedDriver
-datasource.url=jdbc:derby:directory:target/jpaserver_derby_files;create=true
+datasource.driver=org.h2.Driver
+datasource.url=jdbc:h2:file:./target/database/h2
datasource.username=
datasource.password=
server.name=Local Tester
server.id=home
test.port=
-hibernate.dialect=ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect
+
+###################################################
+# Validation
+###################################################
+# Should all incoming requests be validated
+validation.requests.enabled=false
+# Should outgoing responses be validated
+validation.responses.enabled=false
+
+###################################################
+# Search Features
+###################################################
+filter_search.enabled=true
+graphql.enabled=true
+
+###################################################
+# Supported Resources
+###################################################
+# Enable the following property if you want to customize the
+# list of resources that is supported by the server (i.e. to
+# disable specific resources)
+#supported_resource_types=Patient,Observation,Encounter
+
+###################################################
+# Database Settings
+###################################################
+hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory
hibernate.format_sql=false
hibernate.show_sql=false
@@ -50,6 +77,15 @@ hibernate.search.default.directory_provider=filesystem
hibernate.search.default.indexBase=target/lucenefiles
hibernate.search.lucene_version=LUCENE_CURRENT
tester.config.refuse_to_fetch_third_party_urls=false
+
+##################################################
+# Binary Storage Operations
+##################################################
+binary_storage.enabled=true
+
+##################################################
+# CORS Settings
+##################################################
cors.enabled=true
cors.allowCredentials=true
# Supports multiple, comma separated allowed origin entries
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java
index 70b4e5f..ee6ebf8 100644
--- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java
@@ -5,7 +5,7 @@ import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
-import ca.uhn.fhir.util.PortUtil;
+import ca.uhn.fhir.test.utilities.JettyUtil;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.hl7.fhir.instance.model.api.IIdType;
@@ -23,16 +23,13 @@ public class ExampleServerDstu2IT {
private static IGenericClient ourClient;
private static FhirContext ourCtx;
private static int ourPort;
-
private static Server ourServer;
- private static String ourServerBase;
static {
HapiProperties.forceReload();
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU2");
- HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr2;create=true");
+ HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr2");
ourCtx = FhirContext.forDstu2();
- ourPort = PortUtil.findFreePort();
}
@Test
@@ -59,7 +56,7 @@ public class ExampleServerDstu2IT {
ourLog.info("Project base path is: {}", path);
- ourServer = new Server(ourPort);
+ ourServer = new Server(0);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/hapi-fhir-jpaserver");
@@ -70,9 +67,11 @@ public class ExampleServerDstu2IT {
ourServer.setHandler(webAppContext);
ourServer.start();
+ ourPort = JettyUtil.getPortForStartedServer(ourServer);
+
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
- ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/";
+ String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/";
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
}
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java
index 4580f9c..7507bfb 100644
--- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java
@@ -1,18 +1,13 @@
package ca.uhn.fhir.jpa.starter;
-import static ca.uhn.fhir.util.TestUtil.waitForSize;
-import static org.junit.Assert.assertEquals;
-
-import java.io.IOException;
-import java.net.URI;
-import java.nio.file.Paths;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
+import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
-import ca.uhn.fhir.util.PortUtil;
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
+import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
+import ca.uhn.fhir.test.utilities.JettyUtil;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
@@ -23,12 +18,17 @@ import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Subscription;
import org.hl7.fhir.instance.model.api.IIdType;
-import org.junit.*;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.rest.client.api.IGenericClient;
-import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
-import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
+import java.net.URI;
+import java.nio.file.Paths;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import static ca.uhn.fhir.util.TestUtil.waitForSize;
+import static org.junit.Assert.assertEquals;
public class ExampleServerDstu3IT {
@@ -36,17 +36,14 @@ public class ExampleServerDstu3IT {
private static IGenericClient ourClient;
private static FhirContext ourCtx;
private static int ourPort;
-
private static Server ourServer;
- private static String ourServerBase;
static {
HapiProperties.forceReload();
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU3");
- HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr3;create=true");
+ HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr3");
HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
ourCtx = FhirContext.forDstu3();
- ourPort = PortUtil.findFreePort();
}
@Test
@@ -131,10 +128,7 @@ public class ExampleServerDstu3IT {
ourLog.info("Project base path is: {}", path);
- if (ourPort == 0) {
- ourPort = RandomServerPortProvider.findFreePort();
- }
- ourServer = new Server(ourPort);
+ ourServer = new Server(0);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/hapi-fhir-jpaserver");
@@ -145,9 +139,11 @@ public class ExampleServerDstu3IT {
ourServer.setHandler(webAppContext);
ourServer.start();
+ ourPort = JettyUtil.getPortForStartedServer(ourServer);
+
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
- ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/";
+ String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/";
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
}
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java
index bff12bb..1eedafd 100644
--- a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java
@@ -7,7 +7,7 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
-import ca.uhn.fhir.util.PortUtil;
+import ca.uhn.fhir.test.utilities.JettyUtil;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.api.Session;
@@ -36,22 +36,19 @@ public class ExampleServerR4IT {
private static IGenericClient ourClient;
private static FhirContext ourCtx;
private static int ourPort;
-
private static Server ourServer;
- private static String ourServerBase;
static {
HapiProperties.forceReload();
- HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr4;create=true");
+ HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4");
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4");
HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
ourCtx = FhirContext.forR4();
- ourPort = PortUtil.findFreePort();
}
@Test
public void testCreateAndRead() {
- ourLog.info("Base URL is: " + HapiProperties.getServerAddress());
+ ourLog.info("Base URL is: " + HapiProperties.getServerAddress());
String methodName = "testCreateResourceConditional";
Patient pt = new Patient();
@@ -131,10 +128,7 @@ public class ExampleServerR4IT {
ourLog.info("Project base path is: {}", path);
- if (ourPort == 0) {
- ourPort = RandomServerPortProvider.findFreePort();
- }
- ourServer = new Server(ourPort);
+ ourServer = new Server(0);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/hapi-fhir-jpaserver");
@@ -146,9 +140,11 @@ public class ExampleServerR4IT {
ourServer.setHandler(webAppContext);
ourServer.start();
+ ourPort = JettyUtil.getPortForStartedServer(ourServer);
+
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
- ourServerBase = HapiProperties.getServerAddress();
+ String ourServerBase = HapiProperties.getServerAddress();
ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/";
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java
new file mode 100644
index 0000000..04aee05
--- /dev/null
+++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java
@@ -0,0 +1,157 @@
+package ca.uhn.fhir.jpa.starter;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.rest.api.CacheControlDirective;
+import ca.uhn.fhir.rest.api.EncodingEnum;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.client.api.IGenericClient;
+import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
+import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
+import ca.uhn.fhir.test.utilities.JettyUtil;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.webapp.WebAppContext;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Observation;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Subscription;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.net.URI;
+import java.nio.file.Paths;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import static ca.uhn.fhir.util.TestUtil.waitForSize;
+import static org.junit.Assert.assertEquals;
+
+public class ExampleServerR5IT {
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR5IT.class);
+ private static IGenericClient ourClient;
+ private static FhirContext ourCtx;
+ private static int ourPort;
+ private static Server ourServer;
+
+ static {
+ HapiProperties.forceReload();
+ HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr5");
+ HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R5");
+ HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
+ ourCtx = FhirContext.forR4();
+ }
+
+ @Test
+ public void testCreateAndRead() {
+ ourLog.info("Base URL is: " + HapiProperties.getServerAddress());
+ String methodName = "testCreateResourceConditional";
+
+ Patient pt = new Patient();
+ pt.addName().setFamily(methodName);
+ IIdType id = ourClient.create().resource(pt).execute().getId();
+
+ Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute();
+ assertEquals(methodName, pt2.getName().get(0).getFamily());
+ }
+
+ @Test
+ public void testWebsocketSubscription() throws Exception {
+ /*
+ * Create subscription
+ */
+ Subscription subscription = new Subscription();
+ subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
+ subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
+ subscription.setCriteria("Observation?status=final");
+
+ Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
+ channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET);
+ channel.setPayload("application/json");
+ subscription.setChannel(channel);
+
+ MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
+ IIdType mySubscriptionId = methodOutcome.getId();
+
+ // Wait for the subscription to be activated
+ waitForSize(1, () -> ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active")).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry().size());
+
+ /*
+ * Attach websocket
+ */
+
+ WebSocketClient myWebSocketClient = new WebSocketClient();
+ SocketImplementation mySocketImplementation = new SocketImplementation(mySubscriptionId.getIdPart(), EncodingEnum.JSON);
+
+ myWebSocketClient.start();
+ URI echoUri = new URI("ws://localhost:" + ourPort + "/hapi-fhir-jpaserver/websocket");
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ ourLog.info("Connecting to : {}", echoUri);
+ Future connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request);
+ Session session = connection.get(2, TimeUnit.SECONDS);
+
+ ourLog.info("Connected to WS: {}", session.isOpen());
+
+ /*
+ * Create a matching resource
+ */
+ Observation obs = new Observation();
+ obs.setStatus(Observation.ObservationStatus.FINAL);
+ ourClient.create().resource(obs).execute();
+
+ // Give some time for the subscription to deliver
+ Thread.sleep(2000);
+
+ /*
+ * Ensure that we receive a ping on the websocket
+ */
+ waitForSize(1, () -> mySocketImplementation.myPingCount);
+
+ /*
+ * Clean up
+ */
+ ourClient.delete().resourceById(mySubscriptionId).execute();
+ }
+
+ @AfterClass
+ public static void afterClass() throws Exception {
+ ourServer.stop();
+ }
+
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ String path = Paths.get("").toAbsolutePath().toString();
+
+ ourLog.info("Project base path is: {}", path);
+
+ ourServer = new Server(0);
+
+ WebAppContext webAppContext = new WebAppContext();
+ webAppContext.setContextPath("/hapi-fhir-jpaserver");
+ webAppContext.setDisplayName("HAPI FHIR");
+ webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml");
+ webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-starter");
+ webAppContext.setParentLoaderPriority(true);
+
+ ourServer.setHandler(webAppContext);
+ ourServer.start();
+
+ ourPort = JettyUtil.getPortForStartedServer(ourServer);
+
+ ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
+ ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
+ String ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/";
+
+ ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
+ ourClient.registerInterceptor(new LoggingInterceptor(true));
+ }
+
+ public static void main(String[] theArgs) throws Exception {
+ ourPort = 8080;
+ beforeClass();
+ }
+}