diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7cd8c41 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,32 @@ +# Use docker-based build environment (instead of openvz) +#sudo: false +#dist: trusty + +# Use VM based build environment +sudo: required +dist: trusty + +language: java +jdk: + - openjdk11 + #- oraclejdk9 + +env: + global: + - MAVEN_OPTS="-Xmx10244M -Xss128M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC" + +cache: + directories: + - '$HOME/.m2/repository' + +install: /bin/true + +before_script: +# This seems to be required to get travis to set Xmx4g, per https://github.com/travis-ci/travis-ci/issues/3893 + - export MAVEN_SKIP_RC=true +# Sometimes things get restored from the cache with bad permissions. See https://github.com/travis-ci/travis-ci/issues/9630 + - sudo chmod -R 777 "$HOME/.m2/repository"; + - sudo chown -R travis:travis "$HOME/.m2/repository"; + +script: + - mvn install diff --git a/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java similarity index 59% rename from src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java rename to src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java index 7e3a887..c88aaaa 100644 --- a/src/main/java/ca/uhn/fhir/jpa/demo/FhirServerConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigCommon.java @@ -1,29 +1,16 @@ -package ca.uhn.fhir.jpa.demo; +package ca.uhn.fhir.jpa.starter; import java.lang.reflect.InvocationTargetException; import java.sql.Driver; -import java.util.Properties; -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - -import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.jpa.model.entity.ModelConfig; -import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; -import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory; -import ca.uhn.fhir.jpa.util.DerbyTenSevenHapiFhirDialect; import org.apache.commons.dbcp2.BasicDataSource; -import org.apache.commons.lang3.time.DateUtils; -import org.hibernate.jpa.HibernatePersistenceProvider; import org.hl7.fhir.instance.model.Subscription; import org.springframework.beans.factory.annotation.Autowire; 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 org.springframework.transaction.annotation.EnableTransactionManagement; -import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; @@ -35,7 +22,7 @@ import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; */ @Configuration @EnableTransactionManagement() -public class FhirServerConfig extends BaseJavaConfigDstu3 { +public class FhirServerConfigCommon { /** * Configure FHIR properties around the the JPA server via this bean @@ -64,19 +51,6 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 { return new ModelConfig(); } - /** - * 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; - } - /** * 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". @@ -94,31 +68,6 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 { return retVal; } - @Override - @Bean() - public LocalContainerEntityManagerFactoryBean entityManagerFactory() { - LocalContainerEntityManagerFactoryBean retVal = super.entityManagerFactory(); - retVal.setPersistenceUnitName(HapiProperties.getPersistenceUnitName()); - - try { - retVal.setDataSource(dataSource()); - } catch (Exception e) { - throw new ConfigurationException("Could not set the data source due to a configuration issue", e); - } - - retVal.setJpaProperties(jpaProperties()); - return retVal; - } - - private Properties jpaProperties() { - Properties extraProperties = HapiProperties.getProperties(); - - if (extraProperties == null) { - extraProperties = new Properties(); - } - - return extraProperties; - } /** * Do some fancy logging to create a nice access log that has details about each incoming request. @@ -141,16 +90,4 @@ public class FhirServerConfig extends BaseJavaConfigDstu3 { return retVal; } - @Bean(autowire = Autowire.BY_TYPE) - public IServerInterceptor subscriptionSecurityInterceptor() { - SubscriptionsRequireManualActivationInterceptorDstu3 retVal = new SubscriptionsRequireManualActivationInterceptorDstu3(); - 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/FhirServerConfigDstu2.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java new file mode 100644 index 0000000..2335e53 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu2.java @@ -0,0 +1,57 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; +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 FhirServerConfigDstu2 extends BaseJavaConfigDstu2 { + + @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(HapiProperties.getPersistenceUnitName()); + + 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/FhirServerConfigDstu3.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java new file mode 100644 index 0000000..3eb1d9e --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigDstu3.java @@ -0,0 +1,57 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3; +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 FhirServerConfigDstu3 extends BaseJavaConfigDstu3 { + + @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(HapiProperties.getPersistenceUnitName()); + + 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/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java new file mode 100644 index 0000000..a9957e9 --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirServerConfigR4.java @@ -0,0 +1,57 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.ConfigurationException; +import ca.uhn.fhir.jpa.config.BaseJavaConfigR4; +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 FhirServerConfigR4 extends BaseJavaConfigR4 { + + @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(HapiProperties.getPersistenceUnitName()); + + 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/demo/FhirTesterConfig.java b/src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java similarity index 98% rename from src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfig.java rename to src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java index f7ec7e1..8a99968 100644 --- a/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfig.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/FhirTesterConfig.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.demo; +package ca.uhn.fhir.jpa.starter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/src/main/java/ca/uhn/fhir/jpa/demo/HapiProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java similarity index 72% rename from src/main/java/ca/uhn/fhir/jpa/demo/HapiProperties.java rename to src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java index 183db20..f4fb68b 100644 --- a/src/main/java/ca/uhn/fhir/jpa/demo/HapiProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/HapiProperties.java @@ -1,55 +1,67 @@ - -package ca.uhn.fhir.jpa.demo; +package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.server.ETagSupportEnum; +import com.google.common.annotations.VisibleForTesting; -import java.io.FileNotFoundException; -import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class HapiProperties { - private static final String SERVER_ADDRESS = "server_address"; - private static final String DEFAULT_PRETTY_PRINT = "default_pretty_print"; - private static final String MAX_PAGE_SIZE = "max_page_size"; - private static final String DEFAULT_PAGE_SIZE = "default_page_size"; - private static final String LOGGER_NAME = "logger.name"; - private static final String LOGGER_FORMAT = "logger.format"; - private static final String ALLOW_EXTERNAL_REFERENCES = "allow_external_references"; - private static final String ALLOW_MULTIPLE_DELETE = "allow_multiple_delete"; - private static final String DATASOURCE_PASSWORD = "datasource.password"; - private static final String DATASOURCE_USERNAME = "datasource.username"; - private static final String DATASOURCE_URL = "datasource.url"; - private static final String DATASOURCE_DRIVER = "datasource.driver"; - private static final String LOGGER_LOG_EXCEPTIONS = "logger.log_exceptions"; - private static final String LOGGER_ERROR_FORMAT = "logger.error_format"; - private static final String PERSISTENCE_UNIT_NAME = "persistence_unit_name"; - private static final String SERVER_BASE = "server.base"; - private static final String TEST_PORT = "test.port"; - private static final String SERVER_NAME = "server.name"; - private static final String SERVER_ID = "server.id"; - private static final String ALLOW_PLACEHOLDER_REFERENCES = "allow_placeholder_references"; - private static final String HAPI_PROPERTIES = "hapi.properties"; - private static final String FHIR_VERSION = "fhir_version"; - private static final String DEFAULT_ENCODING = "default_encoding"; - private static final String ETAG_SUPPORT = "etag_support"; - private static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled"; - private static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.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"; + static final String DATASOURCE_DRIVER = "datasource.driver"; + static final String DATASOURCE_PASSWORD = "datasource.password"; + static final String DATASOURCE_URL = "datasource.url"; + static final String DATASOURCE_USERNAME = "datasource.username"; + static final String DEFAULT_ENCODING = "default_encoding"; + static final String DEFAULT_PAGE_SIZE = "default_page_size"; + 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 HAPI_PROPERTIES = "hapi.properties"; + static final String LOGGER_ERROR_FORMAT = "logger.error_format"; + static final String LOGGER_FORMAT = "logger.format"; + static final String LOGGER_LOG_EXCEPTIONS = "logger.log_exceptions"; + static final String LOGGER_NAME = "logger.name"; + static final String MAX_PAGE_SIZE = "max_page_size"; + static final String PERSISTENCE_UNIT_NAME = "persistence_unit_name"; + static final String SERVER_ADDRESS = "server_address"; + static final String SERVER_BASE = "server.base"; + static final String SERVER_ID = "server.id"; + static final String SERVER_NAME = "server.name"; + static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled"; + static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.enabled"; + static final String TEST_PORT = "test.port"; + private static Properties properties; + /* + * Force the configuration to be reloaded + */ + public static void forceReload() { + properties = null; + getProperties(); + } + + /** + * This is mostly here for unit tests. Use the actual properties file + * to set values + */ + @VisibleForTesting + public static void setProperty(String theKey, String theValue) { + getProperties().setProperty(theKey, theValue); + } + public static Properties getProperties() { if (properties == null) { // Load the configurable properties file - InputStream in = null; - - try { - in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES); + try (InputStream in = HapiProperties.class.getClassLoader().getResourceAsStream(HAPI_PROPERTIES)){ HapiProperties.properties = new Properties(); HapiProperties.properties.load(in); - in.close(); } catch (Exception e) { throw new ConfigurationException("Could not load HAPI properties", e); } @@ -201,7 +213,7 @@ public class HapiProperties { } public static String getServerBase() { - return HapiProperties.getProperty(SERVER_BASE, "/baseDstu3"); + return HapiProperties.getProperty(SERVER_BASE, "/fhir"); } public static String getServerName() { diff --git a/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java similarity index 62% rename from src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java rename to src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java index 012f867..f4fbece 100644 --- a/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.demo; +package ca.uhn.fhir.jpa.starter; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; @@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; 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.search.DatabaseBackedPagingProvider; @@ -21,21 +22,20 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Meta; -import org.hl7.fhir.instance.model.Subscription; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.context.ContextLoaderListener; -import org.springframework.web.context.WebApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import javax.servlet.ServletException; import java.util.List; -public class JpaServerDemo extends RestfulServer { +public class JpaRestfulServer extends RestfulServer { private static final long serialVersionUID = 1L; - private static final Logger ourLog = LoggerFactory.getLogger(JpaServerDemo.class); + private AnnotationConfigApplicationContext appCtx; - private WebApplicationContext myAppCtx; + @Override + public void destroy() { + appCtx.close(); + } @SuppressWarnings("unchecked") @Override @@ -43,69 +43,61 @@ public class JpaServerDemo extends RestfulServer { super.initialize(); /* - * We want to support FHIR DSTU2 format. This means that the server - * will use the DSTU2 bundle format and other DSTU2 encoding changes. - * - * If you want to use DSTU1 instead, change the following line, and change the 2 occurrences of dstu2 in web.xml to dstu1 + * Create a FhirContext object that uses the version of FHIR + * specified in the properties file. */ FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion(); setFhirContext(new FhirContext(fhirVersion)); - // Get the spring context from the web container (it's declared in web.xml) - myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext(); + appCtx = new AnnotationConfigApplicationContext(); /* - * The BaseJavaConfigDstu2.java class is a spring configuration - * file which is automatically generated as a part of hapi-fhir-jpaserver-base and - * contains bean definitions for a resource provider for each resource type - */ - String resourceProviderBeanName; - if (fhirVersion == FhirVersionEnum.DSTU2) { - resourceProviderBeanName = "myResourceProvidersDstu2"; - } else if (fhirVersion == FhirVersionEnum.DSTU3) { - resourceProviderBeanName = "myResourceProvidersDstu3"; - } else if (fhirVersion == FhirVersionEnum.R4) { - resourceProviderBeanName = "myResourceProviderR4"; - } else { - throw new IllegalStateException(); - } - List beans = myAppCtx.getBean(resourceProviderBeanName, List.class); - setResourceProviders(beans); - - /* - * The system provider implements non-resource-type methods, such as - * transaction, and global history. + * ResourceProviders are fetched from the Spring context */ + List resourceProviders; Object systemProvider; if (fhirVersion == FhirVersionEnum.DSTU2) { - systemProvider = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class); + appCtx.register(FhirServerConfigDstu2.class, FhirServerConfigCommon.class); + appCtx.refresh(); + resourceProviders = appCtx.getBean("myResourceProvidersDstu2", List.class); + systemProvider = appCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class); } else if (fhirVersion == FhirVersionEnum.DSTU3) { - systemProvider = myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class); + appCtx.register(FhirServerConfigDstu3.class, FhirServerConfigCommon.class); + appCtx.refresh(); + resourceProviders = appCtx.getBean("myResourceProvidersDstu3", List.class); + systemProvider = appCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class); } else if (fhirVersion == FhirVersionEnum.R4) { - systemProvider = myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class); + appCtx.register(FhirServerConfigR4.class, FhirServerConfigCommon.class); + appCtx.refresh(); + resourceProviders = appCtx.getBean("myResourceProvidersR4", List.class); + systemProvider = appCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class); } else { throw new IllegalStateException(); } - setPlainProviders(systemProvider); + registerProviders(resourceProviders); + registerProvider(systemProvider); /* * The conformance provider exports the supported resources, search parameters, etc for - * this server. The JPA version adds resource counts to the exported statement, so it + * 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 */ if (fhirVersion == FhirVersionEnum.DSTU2) { - IFhirSystemDao systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); - JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class)); + IFhirSystemDao systemDao = appCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); + JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, appCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription("HAPI FHIR DSTU2 Server"); setServerConformanceProvider(confProvider); } else if (fhirVersion == FhirVersionEnum.DSTU3) { - IFhirSystemDao systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class); - JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class)); + IFhirSystemDao systemDao = appCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class); + JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, appCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription("HAPI FHIR DSTU3 Server"); setServerConformanceProvider(confProvider); } else if (fhirVersion == FhirVersionEnum.R4) { - IFhirSystemDao systemDao = myAppCtx.getBean("mySystemDaoR4", IFhirSystemDao.class); - JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class)); + IFhirSystemDao systemDao = appCtx.getBean("mySystemDaoR4", IFhirSystemDao.class); + JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, appCtx.getBean(DaoConfig.class)); confProvider.setImplementationDescription("HAPI FHIR R4 Server"); setServerConformanceProvider(confProvider); } else { @@ -134,20 +126,19 @@ public class JpaServerDemo extends RestfulServer { setDefaultResponseEncoding(HapiProperties.getDefaultEncoding()); /* - * -- New in HAPI FHIR 1.5 -- * 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(myAppCtx.getBean(DatabaseBackedPagingProvider.class)); + setPagingProvider(appCtx.getBean(DatabaseBackedPagingProvider.class)); /* * This interceptor formats the output using nice colourful * HTML output when the request is detected to come from a * browser. */ - ResponseHighlighterInterceptor responseHighlighterInterceptor = myAppCtx.getBean(ResponseHighlighterInterceptor.class); + ResponseHighlighterInterceptor responseHighlighterInterceptor = appCtx.getBean(ResponseHighlighterInterceptor.class); this.registerInterceptor(responseHighlighterInterceptor); /* @@ -170,16 +161,16 @@ public class JpaServerDemo extends RestfulServer { */ if (false) { // <-- DISABLED RIGHT NOW if (fhirVersion == FhirVersionEnum.DSTU3) { - registerProvider(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class)); + registerProvider(appCtx.getBean(TerminologyUploaderProviderDstu3.class)); } else if (fhirVersion == FhirVersionEnum.R4) { - registerProvider(myAppCtx.getBean(TerminologyUploaderProviderR4.class)); + registerProvider(appCtx.getBean(TerminologyUploaderProviderR4.class)); } } // If you want to enable the $trigger-subscription operation to allow // manual triggering of a subscription delivery, enable this provider if (false) { // <-- DISABLED RIGHT NOW - SubscriptionTriggeringProvider retriggeringProvider = myAppCtx.getBean(SubscriptionTriggeringProvider.class); + SubscriptionTriggeringProvider retriggeringProvider = appCtx.getBean(SubscriptionTriggeringProvider.class); registerProvider(retriggeringProvider); } } diff --git a/src/main/resources/hapi.properties b/src/main/resources/hapi.properties index a3c43aa..332825e 100644 --- a/src/main/resources/hapi.properties +++ b/src/main/resources/hapi.properties @@ -1,7 +1,19 @@ + +# Adjust this to set the version of FHIR supported by this server. See +# FhirVersionEnum for a list of available constants. fhir_version=DSTU3 + +# This is the address that the FHIR server will report as its own address. +# If this server will be deployed (for example) to an internet accessible +# server, put the DNS name of that server here. +server_address=http://localhost/fhir/ + +# This is the context path for the FHIR endpoint. If this is changed, the +# setting above should also be changed. +server.base=/fhir + default_encoding=JSON etag_support=ENABLED -server_address=http://mydomain.com/fhir/baseDstu3 default_page_size=20 max_page_size=200 allow_multiple_delete=true @@ -17,7 +29,6 @@ datasource.driver=org.apache.derby.jdbc.EmbeddedDriver datasource.url=jdbc:derby:directory:target/jpaserver_derby_files;create=true datasource.username= datasource.password= -server.base=/baseDstu3 server.name=Local Tester server.id=home test.port= diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 4ee9cc7..54164b1 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -5,6 +5,7 @@ metadata-complete="false" version="3.1"> + - spring org.springframework.web.servlet.DispatcherServlet @@ -32,28 +33,20 @@ contextConfigLocation - ca.uhn.fhir.jpa.demo.FhirTesterConfig + ca.uhn.fhir.jpa.starter.FhirTesterConfig 2 fhirServlet - ca.uhn.fhir.jpa.demo.JpaServerDemo - - ImplementationDescription - FHIR JPA Server - - - FhirVersion - DSTU3 - + ca.uhn.fhir.jpa.starter.JpaRestfulServer 1 fhirServlet - /baseDstu3/* + /fhir/* diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java new file mode 100644 index 0000000..e1aeb5f --- /dev/null +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu2IT.java @@ -0,0 +1,94 @@ +package ca.uhn.fhir.jpa.starter; + +import ca.uhn.fhir.context.FhirContext; +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 org.eclipse.jetty.server.Server; +import org.eclipse.jetty.webapp.WebAppContext; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class ExampleServerDstu2IT { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu2IT.class); + 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.TEST_PORT, Integer.toString(PortUtil.findFreePort())); + ourCtx = FhirContext.forDstu2(); + ourPort = HapiProperties.getTestPort(); + } + + @Test + public void testCreateAndRead() throws IOException { + ourLog.info("Base URL is: http://localhost:" + ourPort + HapiProperties.getServerBase()); + String methodName = "testCreateResourceConditional"; + + Patient pt = new Patient(); + pt.addName().addFamily(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().get(0).getValue()); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + /* + * This runs under maven, and I'm not sure how else to figure out the target directory from code.. + */ + String path = ExampleServerDstu2IT.class.getClassLoader().getResource(".keep_hapi-fhir-jpaserver-starter").getPath(); + path = new File(path).getParent(); + path = new File(path).getParent(); + path = new File(path).getParent(); + + ourLog.info("Project base path is: {}", path); + + if (ourPort == 0) { + ourPort = RandomServerPortProvider.findFreePort(); + } + ourServer = new Server(ourPort); + + WebAppContext webAppContext = new WebAppContext(); + webAppContext.setContextPath("/"); + 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(); + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + ourServerBase = "http://localhost:" + ourPort + HapiProperties.getServerBase(); + ourClient = ourCtx.newRestfulGenericClient(ourServerBase); + ourClient.registerInterceptor(new LoggingInterceptor(true)); + } + + public static void main(String[] theArgs) throws Exception { + ourPort = 8080; + beforeClass(); + } +} diff --git a/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java similarity index 84% rename from src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java rename to src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java index 9904a3e..43a5b89 100644 --- a/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerDstu3IT.java @@ -1,10 +1,11 @@ -package ca.uhn.fhir.jpa.demo; +package ca.uhn.fhir.jpa.starter; import static org.junit.Assert.assertEquals; import java.io.File; import java.io.IOException; +import ca.uhn.fhir.util.PortUtil; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.hl7.fhir.dstu3.model.Patient; @@ -16,9 +17,9 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; -public class ExampleServerIT { +public class ExampleServerDstu3IT { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerIT.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerDstu3IT.class); private static IGenericClient ourClient; private static FhirContext ourCtx; private static int ourPort; @@ -27,16 +28,10 @@ public class ExampleServerIT { private static String ourServerBase; static { - switch (HapiProperties.getFhirVersion()) { - case DSTU2: - ourCtx = FhirContext.forDstu2(); - case R4: - ourCtx = FhirContext.forR4(); - case DSTU3: - default: - ourCtx = FhirContext.forDstu3(); - } - + HapiProperties.forceReload(); + HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU3"); + HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort())); + ourCtx = FhirContext.forDstu3(); ourPort = HapiProperties.getTestPort(); } @@ -63,7 +58,7 @@ public class ExampleServerIT { /* * This runs under maven, and I'm not sure how else to figure out the target directory from code.. */ - String path = ExampleServerIT.class.getClassLoader().getResource(".keep_hapi-fhir-jpaserver-starter").getPath(); + String path = ExampleServerDstu3IT.class.getClassLoader().getResource(".keep_hapi-fhir-jpaserver-starter").getPath(); path = new File(path).getParent(); path = new File(path).getParent(); path = new File(path).getParent(); diff --git a/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java new file mode 100644 index 0000000..bccdd20 --- /dev/null +++ b/src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java @@ -0,0 +1,94 @@ +package ca.uhn.fhir.jpa.starter; + +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 ca.uhn.fhir.util.PortUtil; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.webapp.WebAppContext; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.assertEquals; + +public class ExampleServerR4IT { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR4IT.class); + 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, "R4"); + HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort())); + ourCtx = FhirContext.forR4(); + ourPort = HapiProperties.getTestPort(); + } + + @Test + public void testCreateAndRead() throws IOException { + ourLog.info("Base URL is: http://localhost:" + ourPort + HapiProperties.getServerBase()); + 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()); + } + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + /* + * This runs under maven, and I'm not sure how else to figure out the target directory from code.. + */ + String path = ExampleServerR4IT.class.getClassLoader().getResource(".keep_hapi-fhir-jpaserver-starter").getPath(); + path = new File(path).getParent(); + path = new File(path).getParent(); + path = new File(path).getParent(); + + ourLog.info("Project base path is: {}", path); + + if (ourPort == 0) { + ourPort = RandomServerPortProvider.findFreePort(); + } + ourServer = new Server(ourPort); + + WebAppContext webAppContext = new WebAppContext(); + webAppContext.setContextPath("/"); + 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(); + + ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); + ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); + ourServerBase = "http://localhost:" + ourPort + HapiProperties.getServerBase(); + ourClient = ourCtx.newRestfulGenericClient(ourServerBase); + ourClient.registerInterceptor(new LoggingInterceptor(true)); + } + + public static void main(String[] theArgs) throws Exception { + ourPort = 8080; + beforeClass(); + } +} diff --git a/src/test/java/ca/uhn/fhir/jpa/demo/RandomServerPortProvider.java b/src/test/java/ca/uhn/fhir/jpa/starter/RandomServerPortProvider.java similarity index 90% rename from src/test/java/ca/uhn/fhir/jpa/demo/RandomServerPortProvider.java rename to src/test/java/ca/uhn/fhir/jpa/starter/RandomServerPortProvider.java index c8f4e49..65446f0 100644 --- a/src/test/java/ca/uhn/fhir/jpa/demo/RandomServerPortProvider.java +++ b/src/test/java/ca/uhn/fhir/jpa/starter/RandomServerPortProvider.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.demo; +package ca.uhn.fhir.jpa.starter; import java.io.IOException; import java.net.ServerSocket;