();
+ for (BundleEntry next : resp.getEntries()) {
+ Patient nextPt = (Patient) next.getResource();
+ String nextStr = nextPt.getNameFirstRep().getGivenAsSingleString() + " " + nextPt.getNameFirstRep().getFamilyAsSingleString();
+ if (isNotBlank(nextStr)) {
+ names.add(nextStr);
+ }
+ }
+ return names;
+ }
+
+ @AfterClass
+ public static void afterClassClearContextBaseResourceProviderDstu3Test() throws Exception {
+ ourServer.stop();
+ ourHttpClient.close();
+ ourServer = null;
+ ourHttpClient = null;
+ ourWebApplicationContext.close();
+ ourWebApplicationContext = null;
+ TestUtil.clearAllStaticFieldsForUnitTest();
+ }
+
}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java
index 5d56b229e72..e7801fb2954 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java
@@ -31,6 +31,7 @@ import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3Config;
import ca.uhn.fhir.jpa.config.dstu3.WebsocketDstu3DispatcherConfig;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
+import ca.uhn.fhir.jpa.interceptor.RestHookSubscriptionDstu3Interceptor;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
@@ -55,6 +56,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
protected static String ourServerBase;
private static GenericWebApplicationContext ourWebApplicationContext;
private TerminologyUploaderProviderDstu3 myTerminologyUploaderProvider;
+ protected static RestHookSubscriptionDstu3Interceptor ourRestHookSubscriptionInterceptor;
protected static ISearchDao mySearchEntityDao;
protected static ISearchCoordinatorSvc mySearchCoordinatorSvc;
@@ -117,7 +119,10 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
dispatcherServlet.setContextClass(AnnotationConfigWebApplicationContext.class);
ServletHolder subsServletHolder = new ServletHolder();
subsServletHolder.setServlet(dispatcherServlet);
- subsServletHolder.setInitParameter(ContextLoader.CONFIG_LOCATION_PARAM, WebsocketDstu3Config.class.getName() + "\n" + WebsocketDstu3DispatcherConfig.class.getName());
+ subsServletHolder.setInitParameter(
+ ContextLoader.CONFIG_LOCATION_PARAM,
+ WebsocketDstu3Config.class.getName() + "\n" +
+ WebsocketDstu3DispatcherConfig.class.getName());
proxyHandler.addServlet(subsServletHolder, "/*");
// Register a CORS filter
@@ -143,6 +148,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
myValidationSupport = wac.getBean(JpaValidationSupportChainDstu3.class);
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
mySearchEntityDao = wac.getBean(ISearchDao.class);
+ ourRestHookSubscriptionInterceptor = wac.getBean(RestHookSubscriptionDstu3Interceptor.class);
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirDstu2Util.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirDstu2Util.java
similarity index 98%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirDstu2Util.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirDstu2Util.java
index dc294f517fc..de0a6d18013 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirDstu2Util.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirDstu2Util.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirDstu3Util.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirDstu3Util.java
similarity index 98%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirDstu3Util.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirDstu3Util.java
index 6b608645c5b..e5184f9939c 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirDstu3Util.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirDstu3Util.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.IGenericClient;
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirServiceUtil.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirServiceUtil.java
similarity index 98%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirServiceUtil.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirServiceUtil.java
index 9accc677f7b..a31ee1546f4 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/FhirServiceUtil.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirServiceUtil.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.MethodOutcome;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithCriteriaDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithCriteriaDstu2Test.java
new file mode 100644
index 00000000000..b18e04dad67
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithCriteriaDstu2Test.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2017 Cognitive Medical Systems, Inc (http://www.cognitivemedicine.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author Jeff Chung
+ */
+package ca.uhn.fhir.jpa.subscription;
+
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.net.URI;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
+import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
+import ca.uhn.fhir.model.dstu2.composite.CodingDt;
+import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
+import ca.uhn.fhir.model.dstu2.resource.Observation;
+import ca.uhn.fhir.model.dstu2.resource.Patient;
+import ca.uhn.fhir.model.dstu2.resource.Subscription;
+import ca.uhn.fhir.model.dstu2.resource.Subscription.Channel;
+import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum;
+import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
+import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.server.EncodingEnum;
+
+/**
+ * Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
+ * subscription
+ *
+ * Note: This test only returns a ping with the subscription id, Check FhirSubscriptionWithSubscriptionIdDstu3Test for
+ * a test that returns the xml of the observation
+ *
+ * To execute the following test, execute it the following way:
+ * 0. execute 'clean' test
+ * 1. Execute the 'createSubscription' test
+ * 2. Update the subscription id in the 'attachWebSocket' test
+ * 3. Execute the 'attachWebSocket' test
+ * 4. Execute the 'sendObservation' test
+ * 5. Look in the 'attachWebSocket' terminal execution and wait for your ping with the subscription id
+ */
+public class FhirSubscriptionWithCriteriaDstu2Test extends BaseResourceProviderDstu2Test {
+ private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithCriteriaDstu2Test.class);
+
+ private String myPatientId;
+ private String mySubscriptionId;
+ private WebSocketClient myWebSocketClient;
+ private SocketImplementation mySocketImplementation;
+
+ @After
+ public void after() throws Exception {
+ super.after();
+ myDaoConfig.setSubscriptionEnabled(new DaoConfig().isSubscriptionEnabled());
+ myDaoConfig.setSubscriptionPollDelay(new DaoConfig().getSubscriptionPollDelay());
+ }
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ myDaoConfig.setSubscriptionEnabled(true);
+ myDaoConfig.setSubscriptionPollDelay(0L);
+
+ /*
+ * Create patient
+ */
+
+ Patient patient = FhirDstu2Util.getPatient();
+ MethodOutcome methodOutcome = ourClient.create().resource(patient).execute();
+ myPatientId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Create subscription
+ */
+ Subscription subscription = new Subscription();
+ subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
+ subscription.setStatus(SubscriptionStatusEnum.ACTIVE);
+ // subscription.setCriteria("Observation?subject=Patient/" + PATIENT_ID);
+ subscription.setCriteria("Observation?code=SNOMED-CT|82313006&_format=xml");
+
+ Channel channel = new Channel();
+ channel.setType(SubscriptionChannelTypeEnum.WEBSOCKET);
+ channel.setPayload("application/json");
+ subscription.setChannel(channel);
+
+ methodOutcome = ourClient.create().resource(subscription).execute();
+ mySubscriptionId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Attach websocket
+ */
+
+ myWebSocketClient = new WebSocketClient();
+ mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON);
+
+ myWebSocketClient.start();
+ URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu2");
+ 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());
+ }
+
+ @After
+ public void afterCloseWebsocket() throws Exception {
+ ourLog.info("Shutting down websocket client");
+ myWebSocketClient.stop();
+ }
+
+ @Test
+ public void createObservation() throws Exception {
+ Observation observation = new Observation();
+ CodeableConceptDt cc = new CodeableConceptDt();
+ observation.setCode(cc);
+ CodingDt coding = cc.addCoding();
+ coding.setCode("82313006");
+ coding.setSystem("SNOMED-CT");
+ ResourceReferenceDt reference = new ResourceReferenceDt();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(ObservationStatusEnum.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(1, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId, "ping " + mySubscriptionId));
+ }
+
+ @Test
+ public void createObservationThatDoesNotMatch() throws Exception {
+ Observation observation = new Observation();
+ CodeableConceptDt cc = new CodeableConceptDt();
+ observation.setCode(cc);
+ CodingDt coding = cc.addCoding();
+ coding.setCode("8231");
+ coding.setSystem("SNOMED-CT");
+ ResourceReferenceDt reference = new ResourceReferenceDt();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(ObservationStatusEnum.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(0, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId));
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithCriteriaDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithCriteriaDstu3Test.java
new file mode 100644
index 00000000000..17c932adde6
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithCriteriaDstu3Test.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2017 Cognitive Medical Systems, Inc (http://www.cognitivemedicine.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author Jeff Chung
+ */
+package ca.uhn.fhir.jpa.subscription;
+
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.net.URI;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+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.dstu3.model.CodeableConcept;
+import org.hl7.fhir.dstu3.model.Coding;
+import org.hl7.fhir.dstu3.model.Observation;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.Reference;
+import org.hl7.fhir.dstu3.model.Subscription;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.server.EncodingEnum;
+
+/**
+ * Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
+ * subscription
+ *
+ * Note: This test only returns a ping with the subscription id, Check FhirSubscriptionWithSubscriptionIdDstu3Test for
+ * a test that returns the xml of the observation
+ *
+ * To execute the following test, execute it the following way:
+ * 0. execute 'clean' test
+ * 1. Execute the 'createPatient' test
+ * 2. Update the patient id static variable
+ * 3. Execute the 'createSubscription' test
+ * 4. Update the subscription id static variable
+ * 5. Execute the 'attachWebSocket' test
+ * 6. Execute the 'sendObservation' test
+ * 7. Look in the 'attachWebSocket' terminal execution and wait for your ping with the subscription id
+ */
+public class FhirSubscriptionWithCriteriaDstu3Test extends BaseResourceProviderDstu3Test {
+
+ private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithCriteriaDstu3Test.class);
+
+ private String myPatientId;
+ private String mySubscriptionId;
+ private WebSocketClient myWebSocketClient;
+ private SocketImplementation mySocketImplementation;
+
+ @After
+ public void after() throws Exception {
+ super.after();
+ myDaoConfig.setSubscriptionEnabled(new DaoConfig().isSubscriptionEnabled());
+ myDaoConfig.setSubscriptionPollDelay(new DaoConfig().getSubscriptionPollDelay());
+ }
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ myDaoConfig.setSubscriptionEnabled(true);
+ myDaoConfig.setSubscriptionPollDelay(0L);
+
+ /*
+ * Create patient
+ */
+
+ Patient patient = FhirDstu3Util.getPatient();
+ MethodOutcome methodOutcome = ourClient.create().resource(patient).execute();
+ myPatientId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Create subscription
+ */
+ Subscription subscription = new Subscription();
+ subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
+ subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
+ // subscription.setCriteria("Observation?subject=Patient/" + PATIENT_ID);
+ subscription.setCriteria("Observation?code=SNOMED-CT|82313006&_format=xml");
+
+ Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
+ channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET);
+ channel.setPayload("application/json");
+ subscription.setChannel(channel);
+
+ methodOutcome = ourClient.create().resource(subscription).execute();
+ mySubscriptionId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Attach websocket
+ */
+
+ myWebSocketClient = new WebSocketClient();
+ mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON);
+
+ myWebSocketClient.start();
+ URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu3");
+ 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());
+ }
+
+ @After
+ public void afterCloseWebsocket() throws Exception {
+ ourLog.info("Shutting down websocket client");
+ myWebSocketClient.stop();
+ }
+
+ @Test
+ public void createObservation() throws Exception {
+ Observation observation = new Observation();
+ CodeableConcept codeableConcept = new CodeableConcept();
+ observation.setCode(codeableConcept);
+ Coding coding = codeableConcept.addCoding();
+ coding.setCode("82313006");
+ coding.setSystem("SNOMED-CT");
+ Reference reference = new Reference();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(Observation.ObservationStatus.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(1, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId, "ping " + mySubscriptionId));
+ }
+
+ @Test
+ public void createObservationThatDoesNotMatch() throws Exception {
+ Observation observation = new Observation();
+ CodeableConcept codeableConcept = new CodeableConcept();
+ observation.setCode(codeableConcept);
+ Coding coding = codeableConcept.addCoding();
+ coding.setCode("8231");
+ coding.setSystem("SNOMED-CT");
+ Reference reference = new Reference();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(Observation.ObservationStatus.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(0, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId));
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithSubscriptionIdDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithSubscriptionIdDstu2Test.java
new file mode 100644
index 00000000000..9dc0aea095e
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithSubscriptionIdDstu2Test.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2017 Cognitive Medical Systems, Inc (http://www.cognitivemedicine.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author Jeff Chung
+ */
+package ca.uhn.fhir.jpa.subscription;
+
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.net.URI;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
+import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
+import ca.uhn.fhir.model.dstu2.composite.CodingDt;
+import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
+import ca.uhn.fhir.model.dstu2.resource.Observation;
+import ca.uhn.fhir.model.dstu2.resource.Patient;
+import ca.uhn.fhir.model.dstu2.resource.Subscription;
+import ca.uhn.fhir.model.dstu2.resource.Subscription.Channel;
+import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum;
+import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
+import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.server.EncodingEnum;
+
+/**
+ * Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
+ * subscription
+ *
+ * Note: This test only returns a ping with the subscription id, Check FhirSubscriptionWithSubscriptionIdDstu3Test for
+ * a test that returns the xml of the observation
+ *
+ * To execute the following test, execute it the following way:
+ * 0. execute 'clean' test
+ * 1. Execute the 'createSubscription' test
+ * 2. Update the subscription id in the 'attachWebSocket' test
+ * 3. Execute the 'attachWebSocket' test
+ * 4. Execute the 'sendObservation' test
+ * 5. Look in the 'attachWebSocket' terminal execution and wait for your ping with the subscription id
+ */
+public class FhirSubscriptionWithSubscriptionIdDstu2Test extends BaseResourceProviderDstu2Test {
+ private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithSubscriptionIdDstu2Test.class);
+
+ private String myPatientId;
+ private String mySubscriptionId;
+ private WebSocketClient myWebSocketClient;
+ private SocketImplementation mySocketImplementation;
+
+ @After
+ public void after() throws Exception {
+ super.after();
+ myDaoConfig.setSubscriptionEnabled(new DaoConfig().isSubscriptionEnabled());
+ myDaoConfig.setSubscriptionPollDelay(new DaoConfig().getSubscriptionPollDelay());
+ }
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ myDaoConfig.setSubscriptionEnabled(true);
+ myDaoConfig.setSubscriptionPollDelay(0L);
+
+ /*
+ * Create patient
+ */
+
+ Patient patient = FhirDstu2Util.getPatient();
+ MethodOutcome methodOutcome = ourClient.create().resource(patient).execute();
+ myPatientId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Create subscription
+ */
+ Subscription subscription = new Subscription();
+ subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
+ subscription.setStatus(SubscriptionStatusEnum.ACTIVE);
+ // subscription.setCriteria("Observation?subject=Patient/" + PATIENT_ID);
+ subscription.setCriteria("Observation?code=SNOMED-CT|82313006");
+
+ Channel channel = new Channel();
+ channel.setType(SubscriptionChannelTypeEnum.WEBSOCKET);
+ channel.setPayload("application/json");
+ subscription.setChannel(channel);
+
+ methodOutcome = ourClient.create().resource(subscription).execute();
+ mySubscriptionId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Attach websocket
+ */
+
+ myWebSocketClient = new WebSocketClient();
+ mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON);
+
+ myWebSocketClient.start();
+ URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu2");
+ 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());
+ }
+
+ @After
+ public void afterCloseWebsocket() throws Exception {
+ ourLog.info("Shutting down websocket client");
+ myWebSocketClient.stop();
+ }
+
+ @Test
+ public void createObservation() throws Exception {
+ Observation observation = new Observation();
+ CodeableConceptDt cc = new CodeableConceptDt();
+ observation.setCode(cc);
+ CodingDt coding = cc.addCoding();
+ coding.setCode("82313006");
+ coding.setSystem("SNOMED-CT");
+ ResourceReferenceDt reference = new ResourceReferenceDt();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(ObservationStatusEnum.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(1, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId, "ping " + mySubscriptionId));
+ }
+
+ @Test
+ public void createObservationThatDoesNotMatch() throws Exception {
+ Observation observation = new Observation();
+ CodeableConceptDt cc = new CodeableConceptDt();
+ observation.setCode(cc);
+ CodingDt coding = cc.addCoding();
+ coding.setCode("8231");
+ coding.setSystem("SNOMED-CT");
+ ResourceReferenceDt reference = new ResourceReferenceDt();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(ObservationStatusEnum.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(0, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId));
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithSubscriptionIdDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithSubscriptionIdDstu3Test.java
new file mode 100644
index 00000000000..26224bed1e1
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/FhirSubscriptionWithSubscriptionIdDstu3Test.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2017 Cognitive Medical Systems, Inc (http://www.cognitivemedicine.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author Jeff Chung
+ */
+package ca.uhn.fhir.jpa.subscription;
+
+import static org.hamcrest.Matchers.contains;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import java.net.URI;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+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.dstu3.model.CodeableConcept;
+import org.hl7.fhir.dstu3.model.Coding;
+import org.hl7.fhir.dstu3.model.Observation;
+import org.hl7.fhir.dstu3.model.Patient;
+import org.hl7.fhir.dstu3.model.Reference;
+import org.hl7.fhir.dstu3.model.Subscription;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.server.EncodingEnum;
+
+/**
+ * Adds a FHIR subscription with criteria through the rest interface. Then creates a websocket with the id of the
+ * subscription
+ *
+ * Note: This test only returns a ping with the subscription id, Check FhirSubscriptionWithSubscriptionIdDstu3Test for
+ * a test that returns the xml of the observation
+ *
+ * To execute the following test, execute it the following way:
+ * 0. execute 'clean' test
+ * 1. Execute the 'createPatient' test
+ * 2. Update the patient id static variable
+ * 3. Execute the 'createSubscription' test
+ * 4. Update the subscription id static variable
+ * 5. Execute the 'attachWebSocket' test
+ * 6. Execute the 'sendObservation' test
+ * 7. Look in the 'attachWebSocket' terminal execution and wait for your ping with the subscription id
+ */
+public class FhirSubscriptionWithSubscriptionIdDstu3Test extends BaseResourceProviderDstu3Test {
+
+ private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithSubscriptionIdDstu3Test.class);
+
+ private String myPatientId;
+ private String mySubscriptionId;
+ private WebSocketClient myWebSocketClient;
+ private SocketImplementation mySocketImplementation;
+
+ @After
+ public void after() throws Exception {
+ super.after();
+ myDaoConfig.setSubscriptionEnabled(new DaoConfig().isSubscriptionEnabled());
+ myDaoConfig.setSubscriptionPollDelay(new DaoConfig().getSubscriptionPollDelay());
+ }
+
+ @Before
+ public void before() throws Exception {
+ super.before();
+
+ myDaoConfig.setSubscriptionEnabled(true);
+ myDaoConfig.setSubscriptionPollDelay(0L);
+
+ /*
+ * Create patient
+ */
+
+ Patient patient = FhirDstu3Util.getPatient();
+ MethodOutcome methodOutcome = ourClient.create().resource(patient).execute();
+ myPatientId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Create subscription
+ */
+ Subscription subscription = new Subscription();
+ subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
+ subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
+ // subscription.setCriteria("Observation?subject=Patient/" + PATIENT_ID);
+ subscription.setCriteria("Observation?code=SNOMED-CT|82313006");
+
+ Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
+ channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET);
+ channel.setPayload("application/json");
+ subscription.setChannel(channel);
+
+ methodOutcome = ourClient.create().resource(subscription).execute();
+ mySubscriptionId = methodOutcome.getId().getIdPart();
+
+ /*
+ * Attach websocket
+ */
+
+ myWebSocketClient = new WebSocketClient();
+ mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON);
+
+ myWebSocketClient.start();
+ URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket/dstu3");
+ 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());
+ }
+
+ @After
+ public void afterCloseWebsocket() throws Exception {
+ ourLog.info("Shutting down websocket client");
+ myWebSocketClient.stop();
+ }
+
+ @Test
+ public void createObservation() throws Exception {
+ Observation observation = new Observation();
+ CodeableConcept codeableConcept = new CodeableConcept();
+ observation.setCode(codeableConcept);
+ Coding coding = codeableConcept.addCoding();
+ coding.setCode("82313006");
+ coding.setSystem("SNOMED-CT");
+ Reference reference = new Reference();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(Observation.ObservationStatus.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(1, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId, "ping " + mySubscriptionId));
+ }
+
+ @Test
+ public void createObservationThatDoesNotMatch() throws Exception {
+ Observation observation = new Observation();
+ CodeableConcept codeableConcept = new CodeableConcept();
+ observation.setCode(codeableConcept);
+ Coding coding = codeableConcept.addCoding();
+ coding.setCode("8231");
+ coding.setSystem("SNOMED-CT");
+ Reference reference = new Reference();
+ reference.setReference("Patient/" + myPatientId);
+ observation.setSubject(reference);
+ observation.setStatus(Observation.ObservationStatus.FINAL);
+
+ MethodOutcome methodOutcome2 = ourClient.create().resource(observation).execute();
+ String observationId = methodOutcome2.getId().getIdPart();
+ observation.setId(observationId);
+
+ ourLog.info("Observation id generated by server is: " + observationId);
+
+ int changes = mySubscriptionDao.pollForNewUndeliveredResources();
+ ourLog.info("Polling showed {}", changes);
+ assertEquals(0, changes);
+
+ Thread.sleep(2000);
+
+ ourLog.info("WS Messages: {}", mySocketImplementation.getMessages());
+ assertThat(mySocketImplementation.getMessages(), contains("bound " + mySubscriptionId));
+ }
+}
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RemoveDstu2TestIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RemoveDstu2TestIT.java
similarity index 98%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RemoveDstu2TestIT.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RemoveDstu2TestIT.java
index e082dd81fb2..ec17d2ac019 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RemoveDstu2TestIT.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RemoveDstu2TestIT.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Observation;
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RemoveDstu3TestIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RemoveDstu3TestIT.java
similarity index 98%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RemoveDstu3TestIT.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RemoveDstu3TestIT.java
index 34e556d3523..9ef524572cb 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RemoveDstu3TestIT.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RemoveDstu3TestIT.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.gclient.IQuery;
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RestHookTestDstu2IT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu2IT.java
similarity index 98%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RestHookTestDstu2IT.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu2IT.java
index 336326a35ac..bb6665a7b35 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RestHookTestDstu2IT.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu2IT.java
@@ -16,7 +16,7 @@
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu2.composite.CodingDt;
@@ -39,7 +39,7 @@ import org.slf4j.Logger;
@Ignore
public class RestHookTestDstu2IT {
- private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithSubscriptionIdDstu3IT.class);
+ private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithSubscriptionIdDstu3Test.class);
private static String code = "1000000012";
private IGenericClient client = FhirServiceUtil.getFhirDstu2Client();
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu2Test.java
new file mode 100644
index 00000000000..44b7bda1345
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu2Test.java
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2017 Cognitive Medical Systems, Inc (http://www.cognitivemedicine.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author Jeff Chung
+ */
+
+package ca.uhn.fhir.jpa.subscription;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.List;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.junit.*;
+
+import com.google.common.collect.Lists;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.provider.BaseResourceProviderDstu2Test;
+import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
+import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
+import ca.uhn.fhir.model.dstu2.composite.CodingDt;
+import ca.uhn.fhir.model.dstu2.resource.Observation;
+import ca.uhn.fhir.model.dstu2.resource.Subscription;
+import ca.uhn.fhir.model.dstu2.resource.Subscription.Channel;
+import ca.uhn.fhir.model.dstu2.valueset.*;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.annotation.*;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+
+/**
+ * Test the rest-hook subscriptions
+ */
+public class RestHookTestDstu2Test extends BaseResourceProviderDstu2Test {
+
+ private static List ourCreatedObservations = Lists.newArrayList();
+ private static int ourListenerPort;
+ private static RestfulServer ourListenerRestServer;
+ private static Server ourListenerServer;
+ private static String ourListenerServerBase;
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu2Test.class);
+ private static List ourUpdatedObservations = Lists.newArrayList();
+
+ @After
+ public void afterUnregisterRestHookListener() {
+ myDaoConfig.setAllowMultipleDelete(true);
+ ourLog.info("Deleting all subscriptions");
+ ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
+ ourLog.info("Done deleting all subscriptions");
+ myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
+
+ ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
+ }
+
+ @Before
+ public void beforeRegisterRestHookListener() {
+// ourRestHookSubscriptionInterceptor.set
+ ourRestServer.registerInterceptor(ourRestHookSubscriptionInterceptor);
+ }
+
+ @Before
+ public void beforeReset() {
+ ourCreatedObservations.clear();
+ ourUpdatedObservations.clear();
+ }
+
+ private Subscription createSubscription(String criteria, String payload, String endpoint) {
+ Subscription subscription = new Subscription();
+ subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
+ subscription.setStatus(SubscriptionStatusEnum.REQUESTED);
+ subscription.setCriteria(criteria);
+
+ Channel channel = new Channel();
+ channel.setType(SubscriptionChannelTypeEnum.REST_HOOK);
+ channel.setPayload(payload);
+ channel.setEndpoint(endpoint);
+ subscription.setChannel(channel);
+
+ MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
+ subscription.setId(methodOutcome.getId().getIdPart());
+
+ return subscription;
+ }
+
+ private Observation sendObservation(String code, String system) {
+ Observation observation = new Observation();
+ CodeableConceptDt codeableConcept = new CodeableConceptDt();
+ observation.setCode(codeableConcept);
+ CodingDt coding = codeableConcept.addCoding();
+ coding.setCode(code);
+ coding.setSystem(system);
+
+ observation.setStatus(ObservationStatusEnum.FINAL);
+
+ MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
+
+ String observationId = methodOutcome.getId().getIdPart();
+ observation.setId(observationId);
+
+ return observation;
+ }
+
+ @Test
+ public void testRestHookSubscriptionJson() throws Exception {
+ String payload = "application/json";
+
+ String code = "1000000050";
+ String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
+ String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
+
+ Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
+ Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
+
+ Observation observation1 = sendObservation(code, "SNOMED-CT");
+
+ // Should see 1 subscription notification
+ Thread.sleep(500);
+ assertEquals(1, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
+ Assert.assertNotNull(subscriptionTemp);
+
+ subscriptionTemp.setCriteria(criteria1);
+ ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
+
+
+ Observation observation2 = sendObservation(code, "SNOMED-CT");
+
+ // Should see two subscription notifications
+ Thread.sleep(500);
+ assertEquals(3, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ ourClient.delete().resourceById(new IdDt("Subscription/"+ subscription2.getId())).execute();
+
+ Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
+ CodeableConceptDt codeableConcept = new CodeableConceptDt();
+ observation3.setCode(codeableConcept);
+ CodingDt coding = codeableConcept.addCoding();
+ coding.setCode(code + "111");
+ coding.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
+
+ // Should see no subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
+
+ CodeableConceptDt codeableConcept1 = new CodeableConceptDt();
+ observation3a.setCode(codeableConcept1);
+ CodingDt coding1 = codeableConcept1.addCoding();
+ coding1.setCode(code);
+ coding1.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(1, ourUpdatedObservations.size());
+
+ Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
+ Assert.assertFalse(observation1.getId().isEmpty());
+ Assert.assertFalse(observation2.getId().isEmpty());
+ }
+
+ @Test
+ public void testRestHookSubscriptionXml() throws Exception {
+ String payload = "application/xml";
+
+ String code = "1000000050";
+ String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
+ String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
+
+ Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
+ Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
+
+ Observation observation1 = sendObservation(code, "SNOMED-CT");
+
+ // Should see 1 subscription notification
+ Thread.sleep(500);
+ assertEquals(1, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
+ Assert.assertNotNull(subscriptionTemp);
+
+ subscriptionTemp.setCriteria(criteria1);
+ ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
+
+
+ Observation observation2 = sendObservation(code, "SNOMED-CT");
+
+ // Should see two subscription notifications
+ Thread.sleep(500);
+ assertEquals(3, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ ourClient.delete().resourceById(new IdDt("Subscription/"+ subscription2.getId())).execute();
+
+ Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
+ CodeableConceptDt codeableConcept = new CodeableConceptDt();
+ observation3.setCode(codeableConcept);
+ CodingDt coding = codeableConcept.addCoding();
+ coding.setCode(code + "111");
+ coding.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
+
+ // Should see no subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
+
+ CodeableConceptDt codeableConcept1 = new CodeableConceptDt();
+ observation3a.setCode(codeableConcept1);
+ CodingDt coding1 = codeableConcept1.addCoding();
+ coding1.setCode(code);
+ coding1.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(1, ourUpdatedObservations.size());
+
+ Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
+ Assert.assertFalse(observation1.getId().isEmpty());
+ Assert.assertFalse(observation2.getId().isEmpty());
+ }
+
+
+ @BeforeClass
+ public static void startListenerServer() throws Exception {
+ ourListenerPort = RandomServerPortProvider.findFreePort();
+ ourListenerRestServer = new RestfulServer(FhirContext.forDstu2());
+ ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
+
+ ObservationListener obsListener = new ObservationListener();
+ ourListenerRestServer.setResourceProviders(obsListener);
+
+ ourListenerServer = new Server(ourListenerPort);
+
+ ServletContextHandler proxyHandler = new ServletContextHandler();
+ proxyHandler.setContextPath("/");
+
+ ServletHolder servletHolder = new ServletHolder();
+ servletHolder.setServlet(ourListenerRestServer);
+ proxyHandler.addServlet(servletHolder, "/fhir/context/*");
+
+ ourListenerServer.setHandler(proxyHandler);
+ ourListenerServer.start();
+ }
+
+ @AfterClass
+ public static void stopListenerServer() throws Exception {
+ ourListenerServer.stop();
+ }
+
+ public static class ObservationListener implements IResourceProvider {
+
+ @Create
+ public MethodOutcome create(@ResourceParam Observation theObservation) {
+ ourLog.info("Received Listener Create");
+ ourCreatedObservations.add(theObservation);
+ return new MethodOutcome(new IdDt("Observation/1"), true);
+ }
+
+ @Override
+ public Class extends IBaseResource> getResourceType() {
+ return Observation.class;
+ }
+
+ @Update
+ public MethodOutcome update(@ResourceParam Observation theObservation) {
+ ourLog.info("Received Listener Update");
+ ourUpdatedObservations.add(theObservation);
+ return new MethodOutcome(new IdDt("Observation/1"), false);
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3Test.java
new file mode 100644
index 00000000000..21e7d6234d9
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3Test.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright 2017 Cognitive Medical Systems, Inc (http://www.cognitivemedicine.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author Jeff Chung
+ */
+
+package ca.uhn.fhir.jpa.subscription;
+
+import static org.junit.Assert.*;
+
+import java.util.List;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.hl7.fhir.dstu3.model.*;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.junit.*;
+
+import com.google.common.collect.Lists;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.jpa.dao.DaoConfig;
+import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
+import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.annotation.*;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.server.IResourceProvider;
+import ca.uhn.fhir.rest.server.RestfulServer;
+
+/**
+ * Test the rest-hook subscriptions
+ */
+public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
+
+ private static List ourCreatedObservations = Lists.newArrayList();
+ private static int ourListenerPort;
+ private static RestfulServer ourListenerRestServer;
+ private static Server ourListenerServer;
+ private static String ourListenerServerBase;
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu3Test.class);
+ private static List ourUpdatedObservations = Lists.newArrayList();
+
+ @After
+ public void afterUnregisterRestHookListener() {
+ myDaoConfig.setAllowMultipleDelete(true);
+ ourLog.info("Deleting all subscriptions");
+ ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
+ ourLog.info("Done deleting all subscriptions");
+ myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
+
+ ourRestServer.unregisterInterceptor(ourRestHookSubscriptionInterceptor);
+ }
+
+ @Before
+ public void beforeRegisterRestHookListener() {
+// ourRestHookSubscriptionInterceptor.set
+ ourRestServer.registerInterceptor(ourRestHookSubscriptionInterceptor);
+ }
+
+ @Before
+ public void beforeReset() {
+ ourCreatedObservations.clear();
+ ourUpdatedObservations.clear();
+ }
+
+ private Subscription createSubscription(String criteria, String payload, String endpoint) {
+ 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(criteria);
+
+ Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
+ channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
+ channel.setPayload(payload);
+ channel.setEndpoint(endpoint);
+ subscription.setChannel(channel);
+
+ MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
+ subscription.setId(methodOutcome.getId().getIdPart());
+
+ return subscription;
+ }
+
+ private Observation sendObservation(String code, String system) {
+ Observation observation = new Observation();
+ CodeableConcept codeableConcept = new CodeableConcept();
+ observation.setCode(codeableConcept);
+ Coding coding = codeableConcept.addCoding();
+ coding.setCode(code);
+ coding.setSystem(system);
+
+ observation.setStatus(Observation.ObservationStatus.FINAL);
+
+ MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
+
+ String observationId = methodOutcome.getId().getIdPart();
+ observation.setId(observationId);
+
+ return observation;
+ }
+
+ @Test
+ public void testRestHookSubscriptionJson() throws Exception {
+ String payload = "application/json";
+
+ String code = "1000000050";
+ String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
+ String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
+
+ Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
+ Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
+
+ Observation observation1 = sendObservation(code, "SNOMED-CT");
+
+ // Should see 1 subscription notification
+ Thread.sleep(500);
+ assertEquals(1, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
+ Assert.assertNotNull(subscriptionTemp);
+
+ subscriptionTemp.setCriteria(criteria1);
+ ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
+
+
+ Observation observation2 = sendObservation(code, "SNOMED-CT");
+
+ // Should see two subscription notifications
+ Thread.sleep(500);
+ assertEquals(3, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
+
+ Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
+ CodeableConcept codeableConcept = new CodeableConcept();
+ observation3.setCode(codeableConcept);
+ Coding coding = codeableConcept.addCoding();
+ coding.setCode(code + "111");
+ coding.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
+
+ // Should see no subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
+
+ CodeableConcept codeableConcept1 = new CodeableConcept();
+ observation3a.setCode(codeableConcept1);
+ Coding coding1 = codeableConcept1.addCoding();
+ coding1.setCode(code);
+ coding1.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(1, ourUpdatedObservations.size());
+
+ Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
+ Assert.assertFalse(observation1.getId().isEmpty());
+ Assert.assertFalse(observation2.getId().isEmpty());
+ }
+
+ @Test
+ public void testRestHookSubscriptionXml() throws Exception {
+ String payload = "application/xml";
+
+ String code = "1000000050";
+ String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
+ String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
+
+ Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
+ Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
+
+ Observation observation1 = sendObservation(code, "SNOMED-CT");
+
+ // Should see 1 subscription notification
+ Thread.sleep(500);
+ assertEquals(1, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
+ Assert.assertNotNull(subscriptionTemp);
+
+ subscriptionTemp.setCriteria(criteria1);
+ ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
+
+
+ Observation observation2 = sendObservation(code, "SNOMED-CT");
+
+ // Should see two subscription notifications
+ Thread.sleep(500);
+ assertEquals(3, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ ourClient.delete().resourceById(new IdDt("Subscription", subscription2.getId())).execute();
+
+ Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
+ CodeableConcept codeableConcept = new CodeableConcept();
+ observation3.setCode(codeableConcept);
+ Coding coding = codeableConcept.addCoding();
+ coding.setCode(code + "111");
+ coding.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
+
+ // Should see no subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(0, ourUpdatedObservations.size());
+
+ Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
+
+ CodeableConcept codeableConcept1 = new CodeableConcept();
+ observation3a.setCode(codeableConcept1);
+ Coding coding1 = codeableConcept1.addCoding();
+ coding1.setCode(code);
+ coding1.setSystem("SNOMED-CT");
+ ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
+
+ // Should see only one subscription notification
+ Thread.sleep(500);
+ assertEquals(4, ourCreatedObservations.size());
+ assertEquals(1, ourUpdatedObservations.size());
+
+ Assert.assertFalse(subscription1.getId().equals(subscription2.getId()));
+ Assert.assertFalse(observation1.getId().isEmpty());
+ Assert.assertFalse(observation2.getId().isEmpty());
+ }
+
+ @BeforeClass
+ public static void startListenerServer() throws Exception {
+ ourListenerPort = RandomServerPortProvider.findFreePort();
+ ourListenerRestServer = new RestfulServer(FhirContext.forDstu3());
+ ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
+
+ ObservationListener obsListener = new ObservationListener();
+ ourListenerRestServer.setResourceProviders(obsListener);
+
+ ourListenerServer = new Server(ourListenerPort);
+
+ ServletContextHandler proxyHandler = new ServletContextHandler();
+ proxyHandler.setContextPath("/");
+
+ ServletHolder servletHolder = new ServletHolder();
+ servletHolder.setServlet(ourListenerRestServer);
+ proxyHandler.addServlet(servletHolder, "/fhir/context/*");
+
+ ourListenerServer.setHandler(proxyHandler);
+ ourListenerServer.start();
+ }
+
+ @AfterClass
+ public static void stopListenerServer() throws Exception {
+ ourListenerServer.stop();
+ }
+
+ public static class ObservationListener implements IResourceProvider {
+
+ @Create
+ public MethodOutcome create(@ResourceParam Observation theObservation) {
+ ourLog.info("Received Listener Create");
+ ourCreatedObservations.add(theObservation);
+ return new MethodOutcome(new IdType("Observation/1"), true);
+ }
+
+ @Override
+ public Class extends IBaseResource> getResourceType() {
+ return Observation.class;
+ }
+
+ @Update
+ public MethodOutcome update(@ResourceParam Observation theObservation) {
+ ourLog.info("Received Listener Update");
+ ourUpdatedObservations.add(theObservation);
+ return new MethodOutcome(new IdType("Observation/1"), false);
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RestHookTestDstu3WithSubscriptionResponseCriteriaIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3WithSubscriptionResponseCriteriaIT.java
similarity index 98%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RestHookTestDstu3WithSubscriptionResponseCriteriaIT.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3WithSubscriptionResponseCriteriaIT.java
index 23b0419546b..d1c4ed3442d 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/RestHookTestDstu3WithSubscriptionResponseCriteriaIT.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/RestHookTestDstu3WithSubscriptionResponseCriteriaIT.java
@@ -16,7 +16,7 @@
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
@@ -36,7 +36,7 @@ import org.slf4j.Logger;
@Ignore
public class RestHookTestDstu3WithSubscriptionResponseCriteriaIT {
- private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithSubscriptionIdDstu3IT.class);
+ private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSubscriptionWithSubscriptionIdDstu3Test.class);
@Test
public void testRestHookSubscription() {
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/ResthookSubscriptionDstu2TestsIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/ResthookSubscriptionDstu2TestsIT.java
similarity index 99%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/ResthookSubscriptionDstu2TestsIT.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/ResthookSubscriptionDstu2TestsIT.java
index 3e5288200dc..61e2f6e40c0 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/ResthookSubscriptionDstu2TestsIT.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/ResthookSubscriptionDstu2TestsIT.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/ResthookSubscriptionDstu3TestsIT.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/ResthookSubscriptionDstu3TestsIT.java
similarity index 99%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/ResthookSubscriptionDstu3TestsIT.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/ResthookSubscriptionDstu3TestsIT.java
index b143a61fd36..f2dcfd61067 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/ResthookSubscriptionDstu3TestsIT.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/ResthookSubscriptionDstu3TestsIT.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.rest.client.IGenericClient;
import org.hl7.fhir.dstu3.model.DateTimeType;
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java
new file mode 100644
index 00000000000..f7b4da78748
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/SocketImplementation.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2017 Cognitive Medical Systems, Inc (http://www.cognitivemedicine.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * @author Jeff Chung
+ */
+
+package ca.uhn.fhir.jpa.subscription;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+import org.slf4j.Logger;
+
+import ca.uhn.fhir.rest.server.EncodingEnum;
+
+@WebSocket
+public class SocketImplementation {
+
+ private static final Logger ourLog = org.slf4j.LoggerFactory.getLogger(SocketImplementation.class);
+ private String myCriteria;
+ protected String myError;
+ protected boolean myGotBound;
+ private List myMessages = new ArrayList();
+ protected int myPingCount;
+ protected String mySubsId;
+ private Session session;
+
+ public SocketImplementation(String theCriteria, EncodingEnum theEncoding) {
+ myCriteria = theCriteria;
+ }
+
+ public List getMessages() {
+ return myMessages;
+ }
+
+ public void keepAlive() {
+ if (this.session != null) {
+ try {
+ session.getRemote().sendString("keep alive");
+ } catch (Throwable t) {
+ ourLog.error("Failure", t);
+ }
+ }
+ }
+
+ /**
+ * This method is executed when the client is connecting to the server.
+ * In this case, we are sending a message to create the subscription dynamiclly
+ *
+ * @param session
+ */
+ @OnWebSocketConnect
+ public void onConnect(Session session) {
+ ourLog.info("Got connect: {}", session);
+ this.session = session;
+ try {
+ String sending = "bind " + myCriteria;
+ ourLog.info("Sending: {}", sending);
+ session.getRemote().sendString(sending);
+
+ ourLog.info("Connection: DONE");
+ } catch (Throwable t) {
+ t.printStackTrace();
+ ourLog.error("Failure", t);
+ }
+ }
+
+ /**
+ * This is the message handler for the client
+ *
+ * @param theMsg
+ */
+ @OnWebSocketMessage
+ public void onMessage(String theMsg) {
+ ourLog.info("Got msg: " + theMsg);
+ myMessages.add(theMsg);
+
+ if (theMsg.startsWith("bound ")) {
+ myGotBound = true;
+ mySubsId = (theMsg.substring("bound ".length()));
+ } else if (myGotBound && theMsg.startsWith("add " + mySubsId + "\n")) {
+ String text = theMsg.substring(("add " + mySubsId + "\n").length());
+ ourLog.info("text: " + text);
+ } else {
+ myError = "Unexpected message: " + theMsg;
+ }
+ }
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/TminusTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/TminusTest.java
similarity index 99%
rename from hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/TminusTest.java
rename to hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/TminusTest.java
index 0650cd6a7da..318c8940f56 100644
--- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/subscription/TminusTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/TminusTest.java
@@ -15,7 +15,7 @@
*
* @author Jeff Chung
*/
-package ca.uhn.fhir.jpa.demo.subscription;
+package ca.uhn.fhir.jpa.subscription;
import ca.uhn.fhir.jpa.service.TMinusService;
import org.hl7.fhir.dstu3.model.DateTimeType;
diff --git a/hapi-fhir-jpaserver-example/pom.xml b/hapi-fhir-jpaserver-example/pom.xml
index 29ad78582b8..3ff8d4578e2 100644
--- a/hapi-fhir-jpaserver-example/pom.xml
+++ b/hapi-fhir-jpaserver-example/pom.xml
@@ -206,6 +206,15 @@