diff --git a/hapi-fhir-jpaserver-cds-example/pom.xml b/hapi-fhir-jpaserver-cds-example/pom.xml
new file mode 100644
index 00000000000..4059173b5f4
--- /dev/null
+++ b/hapi-fhir-jpaserver-cds-example/pom.xml
@@ -0,0 +1,286 @@
+
+ 4.0.0
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir
+ 3.1.0-SNAPSHOT
+ ../pom.xml
+
+
+ hapi-fhir-jpaserver-cds
+ war
+
+ HAPI FHIR JPA Clinical Decision Support Server - Example
+
+
+
+ oss-snapshots
+
+ true
+
+ https://oss.sonatype.org/content/repositories/snapshots/
+
+
+
+
+
+
+ org.opencds.cqf
+ cqf-ruler
+ 0.1.0-SNAPSHOT
+
+
+
+ org.eclipse.jetty.websocket
+ websocket-api
+ ${jetty_version}
+
+
+ org.eclipse.jetty.websocket
+ websocket-client
+ ${jetty_version}
+
+
+ mysql
+ mysql-connector-java
+ 6.0.5
+
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-base
+ ${project.version}
+
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-structures-dstu3
+ ${project.version}
+
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-jpaserver-base
+ ${project.version}
+
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-testpage-overlay
+ ${project.version}
+ war
+ provided
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-testpage-overlay
+ ${project.version}
+ classes
+ provided
+
+
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
+
+ org.thymeleaf
+ thymeleaf
+
+
+
+
+ org.ebaysf.web
+ cors-filter
+
+
+ servlet-api
+ javax.servlet
+
+
+
+
+
+
+ org.springframework
+ spring-web
+
+
+
+
+ org.apache.commons
+ commons-dbcp2
+
+
+
+
+ org.apache.derby
+ derby
+
+
+ org.apache.derby
+ derbynet
+
+
+ org.apache.derby
+ derbyclient
+
+
+
+
+ org.eclipse.jetty
+ jetty-servlets
+ test
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ test
+
+
+ org.eclipse.jetty.websocket
+ websocket-server
+ test
+
+
+ org.eclipse.jetty
+ jetty-server
+ test
+
+
+ org.eclipse.jetty
+ jetty-util
+ test
+
+
+ org.eclipse.jetty
+ jetty-webapp
+ test
+
+
+ com.phloc
+ phloc-schematron
+
+
+ Saxon-HE
+ net.sf.saxon
+
+
+
+
+
+
+ javax.interceptor
+ javax.interceptor-api
+ provided
+
+
+
+
+
+
+
+ hapi-fhir-jpaserver-cds
+
+
+
+
+
+ org.eclipse.jetty
+ jetty-maven-plugin
+
+
+ /hapi-fhir-jpaserver-cds
+ true
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ 1.7
+ 1.7
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+
+
+
+ ${maven.build.timestamp}
+
+
+
+
+ ca.uhn.hapi.fhir
+ hapi-fhir-testpage-overlay
+
+
+ src/main/webapp/WEB-INF/web.xml
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+
+ true
+
+
+
+
+ integration-test
+ verify
+
+
+
+
+
+
+
+
+
diff --git a/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsHooksServerExample.java b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsHooksServerExample.java
new file mode 100644
index 00000000000..e17bc747437
--- /dev/null
+++ b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsHooksServerExample.java
@@ -0,0 +1,16 @@
+package ca.uhn.fhir.jpa.cds.example;
+
+import org.opencds.cqf.servlet.CdsServicesServlet;
+
+public class CdsHooksServerExample extends CdsServicesServlet {
+
+// @Override
+// protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+// // Change how requests are handled
+// }
+//
+// @Override
+// protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+// // Change discovery response
+// }
+}
diff --git a/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java
new file mode 100644
index 00000000000..ed5e7185cd4
--- /dev/null
+++ b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java
@@ -0,0 +1,165 @@
+
+package ca.uhn.fhir.jpa.cds.example;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
+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.rp.dstu3.ActivityDefinitionResourceProvider;
+import ca.uhn.fhir.jpa.rp.dstu3.MeasureResourceProvider;
+import ca.uhn.fhir.jpa.rp.dstu3.PlanDefinitionResourceProvider;
+import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
+import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
+import ca.uhn.fhir.rest.api.EncodingEnum;
+import ca.uhn.fhir.rest.server.ETagSupportEnum;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import org.hl7.fhir.dstu3.model.Bundle;
+import org.hl7.fhir.dstu3.model.Meta;
+import org.opencds.cqf.providers.FHIRActivityDefinitionResourceProvider;
+import org.opencds.cqf.providers.FHIRMeasureResourceProvider;
+import org.opencds.cqf.providers.FHIRPlanDefinitionResourceProvider;
+import org.springframework.web.context.ContextLoaderListener;
+import org.springframework.web.context.WebApplicationContext;
+
+import javax.servlet.ServletException;
+import java.util.Collection;
+import java.util.List;
+
+public class CdsServerExample extends RestfulServer {
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected void initialize() throws ServletException {
+ super.initialize();
+
+ FhirVersionEnum fhirVersion = FhirVersionEnum.DSTU3;
+ setFhirContext(new FhirContext(fhirVersion));
+
+ // Get the spring context from the web container (it's declared in web.xml)
+ WebApplicationContext myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
+
+ if (myAppCtx == null) {
+ throw new ServletException("Error retrieving spring context from the web container");
+ }
+
+ String resourceProviderBeanName = "myResourceProvidersDstu3";
+ List beans = myAppCtx.getBean(resourceProviderBeanName, List.class);
+ setResourceProviders(beans);
+
+ Object systemProvider = myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class);
+ setPlainProviders(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
+ * is a nice addition.
+ */
+ IFhirSystemDao systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class);
+ JpaConformanceProviderDstu3 confProvider =
+ new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class));
+ confProvider.setImplementationDescription("Example Server");
+ setServerConformanceProvider(confProvider);
+
+ /*
+ * Enable ETag Support (this is already the default)
+ */
+ setETagSupport(ETagSupportEnum.ENABLED);
+
+ /*
+ * This server tries to dynamically generate narratives
+ */
+ FhirContext ctx = getFhirContext();
+ ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
+
+ /*
+ * Default to JSON and pretty printing
+ */
+ setDefaultPrettyPrint(true);
+ setDefaultResponseEncoding(EncodingEnum.JSON);
+
+ /*
+ * -- 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));
+
+ /*
+ * Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
+ */
+ Collection interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
+ for (IServerInterceptor interceptor : interceptorBeans) {
+ this.registerInterceptor(interceptor);
+ }
+
+ /*
+ * Adding resource providers from the cqf-ruler
+ */
+ // Measure processing
+ FHIRMeasureResourceProvider measureProvider = new FHIRMeasureResourceProvider(getResourceProviders());
+ MeasureResourceProvider jpaMeasureProvider = (MeasureResourceProvider) getProvider("Measure");
+ measureProvider.setDao(jpaMeasureProvider.getDao());
+ measureProvider.setContext(jpaMeasureProvider.getContext());
+
+ // PlanDefinition processing
+ FHIRPlanDefinitionResourceProvider planDefProvider = new FHIRPlanDefinitionResourceProvider(getResourceProviders());
+ PlanDefinitionResourceProvider jpaPlanDefProvider =
+ (PlanDefinitionResourceProvider) getProvider("PlanDefinition");
+ planDefProvider.setDao(jpaPlanDefProvider.getDao());
+ planDefProvider.setContext(jpaPlanDefProvider.getContext());
+
+ // ActivityDefinition processing
+ FHIRActivityDefinitionResourceProvider actDefProvider = new FHIRActivityDefinitionResourceProvider(getResourceProviders());
+ ActivityDefinitionResourceProvider jpaActDefProvider =
+ (ActivityDefinitionResourceProvider) getProvider("ActivityDefinition");
+ actDefProvider.setDao(jpaActDefProvider.getDao());
+ actDefProvider.setContext(jpaActDefProvider.getContext());
+
+ try {
+ unregisterProvider(jpaMeasureProvider);
+ unregisterProvider(jpaPlanDefProvider);
+ unregisterProvider(jpaActDefProvider);
+ } catch (Exception e) {
+ throw new ServletException("Unable to unregister provider: " + e.getMessage());
+ }
+
+ registerProvider(measureProvider);
+ registerProvider(planDefProvider);
+ registerProvider(actDefProvider);
+
+ /*
+ * If you are hosting this server at a specific DNS name, the server will try to
+ * figure out the FHIR base URL based on what the web container tells it, but
+ * this doesn't always work. If you are setting links in your search bundles that
+ * just refer to "localhost", you might want to use a server address strategy:
+ */
+ //setServerAddressStrategy(new HardcodedServerAddressStrategy("http://mydomain.com/fhir/baseDstu2"));
+
+ /*
+ * If you are using DSTU3+, you may want to add a terminology uploader, which allows
+ * uploading of external terminologies such as Snomed CT. Note that this uploader
+ * does not have any security attached (any anonymous user may use it by default)
+ * so it is a potential security vulnerability. Consider using an AuthorizationInterceptor
+ * with this feature.
+ */
+ registerProvider(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class));
+ }
+
+ public IResourceProvider getProvider(String name) {
+
+ for (IResourceProvider res : getResourceProviders()) {
+ if (res.getResourceType().getSimpleName().equals(name)) {
+ return res;
+ }
+ }
+
+ throw new IllegalArgumentException("This should never happen!");
+ }
+}
diff --git a/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java
new file mode 100644
index 00000000000..58a9536b281
--- /dev/null
+++ b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirServerConfig.java
@@ -0,0 +1,125 @@
+package ca.uhn.fhir.jpa.cds.example;
+
+import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
+import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3;
+import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
+import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
+import org.apache.commons.dbcp2.BasicDataSource;
+import org.apache.commons.lang3.time.DateUtils;
+import org.hibernate.jpa.HibernatePersistenceProvider;
+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 javax.persistence.EntityManagerFactory;
+import javax.sql.DataSource;
+import java.util.Properties;
+
+/**
+ * This is the primary configuration file for the example server
+ */
+@Configuration
+@EnableTransactionManagement()
+public class FhirServerConfig extends BaseJavaConfigDstu3 {
+
+ /**
+ * Configure FHIR properties around the the JPA server via this bean
+ */
+ @Bean()
+ public DaoConfig daoConfig() {
+ DaoConfig retVal = new DaoConfig();
+ retVal.setSubscriptionEnabled(true);
+ retVal.setSubscriptionPollDelay(5000);
+ retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
+ retVal.setAllowMultipleDelete(true);
+ return retVal;
+ }
+
+ /**
+ * The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a
+ * directory called "jpaserver_derby_files".
+ *
+ * A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource.
+ */
+ @Bean(destroyMethod = "close")
+ public DataSource dataSource() {
+ BasicDataSource retVal = new BasicDataSource();
+ retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
+ retVal.setUrl("jdbc:derby:directory:target/jpaserver_derby_files;create=true");
+ retVal.setUsername("");
+ retVal.setPassword("");
+ return retVal;
+ }
+
+ @Bean()
+ public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
+ LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
+ retVal.setPersistenceUnitName("HAPI_PU");
+ retVal.setDataSource(dataSource());
+ retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
+ retVal.setPersistenceProvider(new HibernatePersistenceProvider());
+ retVal.setJpaProperties(jpaProperties());
+ return retVal;
+ }
+
+ private Properties jpaProperties() {
+ Properties extraProperties = new Properties();
+ extraProperties.put("hibernate.dialect", org.hibernate.dialect.DerbyTenSevenDialect.class.getName());
+ extraProperties.put("hibernate.format_sql", "true");
+ extraProperties.put("hibernate.show_sql", "false");
+ extraProperties.put("hibernate.hbm2ddl.auto", "update");
+ extraProperties.put("hibernate.jdbc.batch_size", "20");
+ extraProperties.put("hibernate.cache.use_query_cache", "false");
+ extraProperties.put("hibernate.cache.use_second_level_cache", "false");
+ extraProperties.put("hibernate.cache.use_structured_entries", "false");
+ extraProperties.put("hibernate.cache.use_minimal_puts", "false");
+ extraProperties.put("hibernate.search.model_mapping", LuceneSearchMappingFactory.class.getName());
+ extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
+ extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
+ extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
+// extraProperties.put("hibernate.search.default.worker.execution", "async");
+ return extraProperties;
+ }
+
+ /**
+ * Do some fancy logging to create a nice access log that has details about each incoming request.
+ */
+ public IServerInterceptor loggingInterceptor() {
+ LoggingInterceptor retVal = new LoggingInterceptor();
+ retVal.setLoggerName("fhirtest.access");
+ retVal.setMessageFormat(
+ "Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]");
+ retVal.setLogExceptions(true);
+ retVal.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}");
+ return retVal;
+ }
+
+ /**
+ * This interceptor adds some pretty syntax highlighting in responses when a browser is detected
+ */
+ @Bean(autowire = Autowire.BY_TYPE)
+ public IServerInterceptor responseHighlighterInterceptor() {
+ ResponseHighlighterInterceptor retVal = new ResponseHighlighterInterceptor();
+ 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/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirTesterConfig.java b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirTesterConfig.java
new file mode 100644
index 00000000000..6fbc660b27d
--- /dev/null
+++ b/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/FhirTesterConfig.java
@@ -0,0 +1,56 @@
+package ca.uhn.fhir.jpa.cds.example;
+
+import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.to.FhirTesterMvcConfig;
+import ca.uhn.fhir.to.TesterConfig;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+
+//@formatter:off
+
+/**
+ * This spring config file configures the web testing module. It serves two
+ * purposes:
+ * 1. It imports FhirTesterMvcConfig, which is the spring config for the
+ * tester itself
+ * 2. It tells the tester which server(s) to talk to, via the testerConfig()
+ * method below
+ */
+@Configuration
+@Import(FhirTesterMvcConfig.class)
+public class FhirTesterConfig {
+
+ /**
+ * This bean tells the testing webpage which servers it should configure itself
+ * to communicate with. In this example we configure it to talk to the local
+ * server, as well as one public server. If you are creating a project to
+ * deploy somewhere else, you might choose to only put your own server's
+ * address here.
+ *
+ * Note the use of the ${serverBase} variable below. This will be replaced with
+ * the base URL as reported by the server itself. Often for a simple Tomcat
+ * (or other container) installation, this will end up being something
+ * like "http://localhost:8080/hapi-fhir-jpaserver-example". If you are
+ * deploying your server to a place with a fully qualified domain name,
+ * you might want to use that instead of using the variable.
+ */
+ @Bean
+ public TesterConfig testerConfig() {
+ TesterConfig retVal = new TesterConfig();
+ retVal
+ .addServer()
+ .withId("home")
+ .withFhirVersion(FhirVersionEnum.DSTU3)
+ .withBaseUrl("${serverBase}/baseDstu3")
+ .withName("Local Tester")
+ .addServer()
+ .withId("hapi")
+ .withFhirVersion(FhirVersionEnum.DSTU3)
+ .withBaseUrl("http://fhirtest.uhn.ca/baseDstu3")
+ .withName("Public HAPI Test Server");
+ return retVal;
+ }
+
+}
+//@formatter:on
diff --git a/hapi-fhir-jpaserver-cqf-ruler/src/main/webapp/WEB-INF/templates/about.html b/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/about.html
similarity index 97%
rename from hapi-fhir-jpaserver-cqf-ruler/src/main/webapp/WEB-INF/templates/about.html
rename to hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/about.html
index 33033992b9f..d552027e956 100644
--- a/hapi-fhir-jpaserver-cqf-ruler/src/main/webapp/WEB-INF/templates/about.html
+++ b/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/about.html
@@ -1,5 +1,5 @@
-
+
About This Server
diff --git a/hapi-fhir-jpaserver-cqf-ruler/src/main/webapp/WEB-INF/templates/tmpl-footer.html b/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-footer.html
similarity index 90%
rename from hapi-fhir-jpaserver-cqf-ruler/src/main/webapp/WEB-INF/templates/tmpl-footer.html
rename to hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-footer.html
index 5b87f43f1eb..bf18c498a78 100644
--- a/hapi-fhir-jpaserver-cqf-ruler/src/main/webapp/WEB-INF/templates/tmpl-footer.html
+++ b/hapi-fhir-jpaserver-cds-example/src/main/webapp/WEB-INF/templates/tmpl-footer.html
@@ -1,5 +1,5 @@
-
+