Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	src/main/java/ca/uhn/fhir/jpa/starter/JpaRestfulServer.java
#	src/main/resources/hapi.properties
#	src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR4IT.java
This commit is contained in:
Jessie James Cosare
2019-03-15 23:09:09 +08:00
11 changed files with 317 additions and 91 deletions

View File

@@ -0,0 +1,26 @@
package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
public class ApplicationContext extends AnnotationConfigWebApplicationContext {
public ApplicationContext() {
FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion();
if (fhirVersion == FhirVersionEnum.DSTU2) {
register(FhirServerConfigDstu2.class, FhirServerConfigCommon.class);
} else if (fhirVersion == FhirVersionEnum.DSTU3) {
register(FhirServerConfigDstu3.class, FhirServerConfigCommon.class);
} else if (fhirVersion == FhirVersionEnum.R4) {
register(FhirServerConfigR4.class, FhirServerConfigCommon.class);
} else {
throw new IllegalStateException();
}
if (HapiProperties.getSubscriptionWebsocketEnabled()) {
register(ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig.class);
}
}
}

View File

@@ -53,18 +53,21 @@ public class FhirServerConfigCommon {
retVal.setFetchSizeDefaultMaximum(maxFetchSize); retVal.setFetchSizeDefaultMaximum(maxFetchSize);
ourLog.info("Server configured to have a maximum fetch size of " + (maxFetchSize == Integer.MAX_VALUE? "'unlimited'": maxFetchSize)); ourLog.info("Server configured to have a maximum fetch size of " + (maxFetchSize == Integer.MAX_VALUE? "'unlimited'": maxFetchSize));
// You can enable these if you want to support Subscriptions from your server // Subscriptions are enabled by channel type
if (HapiProperties.getSubscriptionRestHookEnabled()) { if (HapiProperties.getSubscriptionRestHookEnabled()) {
ourLog.info("Enabling REST-hook subscriptions"); ourLog.info("Enabling REST-hook subscriptions");
retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.RESTHOOK); retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.RESTHOOK);
} }
if (HapiProperties.getSubscriptionEmailEnabled()) { if (HapiProperties.getSubscriptionEmailEnabled()) {
ourLog.info("Enabling email subscriptions"); ourLog.info("Enabling email subscriptions");
retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.EMAIL); retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.EMAIL);
} }
if (HapiProperties.getSubscriptionWebsocketEnabled()) {
ourLog.info("Enabling websocket subscriptions");
retVal.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.WEBSOCKET);
}
return retVal; return retVal;
} }
@Bean @Bean

View File

@@ -18,6 +18,7 @@ public class FhirServerConfigR4 extends BaseJavaConfigR4 {
@Autowired @Autowired
private DataSource myDataSource; private DataSource myDataSource;
/** /**
* We override the paging provider definition so that we can customize * We override the paging provider definition so that we can customize
* the default/max page sizes for search results. You can set these however * the default/max page sizes for search results. You can set these however

View File

@@ -38,6 +38,7 @@ public class HapiProperties {
static final String SERVER_NAME = "server.name"; static final String SERVER_NAME = "server.name";
static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled"; static final String SUBSCRIPTION_EMAIL_ENABLED = "subscription.email.enabled";
static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.enabled"; static final String SUBSCRIPTION_RESTHOOK_ENABLED = "subscription.resthook.enabled";
static final String SUBSCRIPTION_WEBSOCKET_ENABLED = "subscription.websocket.enabled";
static final String TEST_PORT = "test.port"; static final String TEST_PORT = "test.port";
static final String TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS = "tester.config.refuse_to_fetch_third_party_urls"; static final String TESTER_CONFIG_REFUSE_TO_FETCH_THIRD_PARTY_URLS = "tester.config.refuse_to_fetch_third_party_urls";
@@ -235,11 +236,11 @@ public class HapiProperties {
} }
public static Boolean getAllowMultipleDelete() { public static Boolean getAllowMultipleDelete() {
return HapiProperties.getBooleanProperty(ALLOW_MULTIPLE_DELETE, true); return HapiProperties.getBooleanProperty(ALLOW_MULTIPLE_DELETE, false);
} }
public static Boolean getAllowExternalReferences() { public static Boolean getAllowExternalReferences() {
return HapiProperties.getBooleanProperty(ALLOW_EXTERNAL_REFERENCES, true); return HapiProperties.getBooleanProperty(ALLOW_EXTERNAL_REFERENCES, false);
} }
public static Boolean getExpungeEnabled() { public static Boolean getExpungeEnabled() {
@@ -271,10 +272,14 @@ public class HapiProperties {
} }
public static Boolean getSubscriptionEmailEnabled() { public static Boolean getSubscriptionEmailEnabled() {
return HapiProperties.getBooleanProperty(SUBSCRIPTION_EMAIL_ENABLED, true); return HapiProperties.getBooleanProperty(SUBSCRIPTION_EMAIL_ENABLED, false);
} }
public static Boolean getSubscriptionRestHookEnabled() { public static Boolean getSubscriptionRestHookEnabled() {
return HapiProperties.getBooleanProperty(SUBSCRIPTION_RESTHOOK_ENABLED, true); return HapiProperties.getBooleanProperty(SUBSCRIPTION_RESTHOOK_ENABLED, false);
}
public static Boolean getSubscriptionWebsocketEnabled() {
return HapiProperties.getBooleanProperty(SUBSCRIPTION_WEBSOCKET_ENABLED, false);
} }
} }

View File

@@ -5,6 +5,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.model.interceptor.executor.InterceptorService; import ca.uhn.fhir.jpa.model.interceptor.executor.InterceptorService;
import ca.uhn.fhir.jpa.model.interceptor.api.IInterceptorRegistry;
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2;
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider; import ca.uhn.fhir.jpa.provider.SubscriptionTriggeringProvider;
@@ -17,6 +18,8 @@ import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader; import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
import ca.uhn.fhir.jpa.subscription.module.interceptor.SubscriptionDebugLogInterceptor; import ca.uhn.fhir.jpa.subscription.module.interceptor.SubscriptionDebugLogInterceptor;
import ca.uhn.fhir.jpa.subscription.SubscriptionActivatingInterceptor;
import ca.uhn.fhir.jpa.subscription.SubscriptionMatcherInterceptor;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
@@ -26,8 +29,8 @@ import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Meta; import org.hl7.fhir.dstu3.model.Meta;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.context.ApplicationContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import java.util.Arrays; import java.util.Arrays;
@@ -36,12 +39,6 @@ import java.util.List;
public class JpaRestfulServer extends RestfulServer { public class JpaRestfulServer extends RestfulServer {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private AnnotationConfigApplicationContext appCtx;
@Override
public void destroy() {
appCtx.close();
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@@ -52,34 +49,29 @@ public class JpaRestfulServer extends RestfulServer {
* Create a FhirContext object that uses the version of FHIR * Create a FhirContext object that uses the version of FHIR
* specified in the properties file. * specified in the properties file.
*/ */
FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion(); ApplicationContext appCtx = (ApplicationContext) getServletContext().getAttribute("org.springframework.web.context.WebApplicationContext.ROOT");
setFhirContext(new FhirContext(fhirVersion));
appCtx = new AnnotationConfigApplicationContext();
/* /*
* ResourceProviders are fetched from the Spring context * ResourceProviders are fetched from the Spring context
*/ */
FhirVersionEnum fhirVersion = HapiProperties.getFhirVersion();
List<IResourceProvider> resourceProviders; List<IResourceProvider> resourceProviders;
Object systemProvider; Object systemProvider;
if (fhirVersion == FhirVersionEnum.DSTU2) { if (fhirVersion == FhirVersionEnum.DSTU2) {
appCtx.register(FhirServerConfigDstu2.class, FhirServerConfigCommon.class);
appCtx.refresh();
resourceProviders = appCtx.getBean("myResourceProvidersDstu2", List.class); resourceProviders = appCtx.getBean("myResourceProvidersDstu2", List.class);
systemProvider = appCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class); systemProvider = appCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class);
} else if (fhirVersion == FhirVersionEnum.DSTU3) { } else if (fhirVersion == FhirVersionEnum.DSTU3) {
appCtx.register(FhirServerConfigDstu3.class, FhirServerConfigCommon.class);
appCtx.refresh();
resourceProviders = appCtx.getBean("myResourceProvidersDstu3", List.class); resourceProviders = appCtx.getBean("myResourceProvidersDstu3", List.class);
systemProvider = appCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class); systemProvider = appCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class);
} else if (fhirVersion == FhirVersionEnum.R4) { } else if (fhirVersion == FhirVersionEnum.R4) {
appCtx.register(FhirServerConfigR4.class, FhirServerConfigCommon.class);
appCtx.refresh();
resourceProviders = appCtx.getBean("myResourceProvidersR4", List.class); resourceProviders = appCtx.getBean("myResourceProvidersR4", List.class);
systemProvider = appCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class); systemProvider = appCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class);
} else { } else {
throw new IllegalStateException(); throw new IllegalStateException();
} }
setFhirContext(appCtx.getBean(FhirContext.class));
registerProviders(resourceProviders); registerProviders(resourceProviders);
registerProvider(systemProvider); registerProvider(systemProvider);
@@ -200,12 +192,23 @@ public class JpaRestfulServer extends RestfulServer {
CorsInterceptor interceptor = new CorsInterceptor(config); CorsInterceptor interceptor = new CorsInterceptor(config);
registerInterceptor(interceptor); registerInterceptor(interceptor);
// Enable the use of subscriptions // If subscriptions are enabled, we want to register the interceptor that
SubscriptionInterceptorLoader subscriptionInterceptorLoader = appCtx.getBean(SubscriptionInterceptorLoader.class); // will activate them and match results against them
subscriptionInterceptorLoader.registerInterceptors(); if (HapiProperties.getSubscriptionWebsocketEnabled() ||
// Subscription debug logging HapiProperties.getSubscriptionEmailEnabled() ||
InterceptorService interceptorService = (InterceptorService) appCtx.getBean("interceptorService"); HapiProperties.getSubscriptionRestHookEnabled()) {
interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor());
// Loads subscription interceptors (SubscriptionActivatingInterceptor, SubscriptionMatcherInterceptor)
// with activation of scheduled subscription
SubscriptionInterceptorLoader subscriptionInterceptorLoader = appCtx.getBean(SubscriptionInterceptorLoader.class);
subscriptionInterceptorLoader.registerInterceptors();
// Subscription debug logging
InterceptorService interceptorService = (InterceptorService) appCtx.getBean("interceptorService");
interceptorService.registerInterceptor(new SubscriptionDebugLogInterceptor());
}
} }
} }

View File

@@ -8,10 +8,16 @@ fhir_version=R4
# server, put the DNS name of that server here. # server, put the DNS name of that server here.
server_address=http://localhost:8080/fhir/ server_address=http://localhost:8080/fhir/
# For Jetty, use this:
# server_address=http://localhost:8080/hapi-fhir-jpaserver/fhir/
# This is the context path for the FHIR endpoint. If this is changed, the # This is the context path for the FHIR endpoint. If this is changed, the
# setting above should also be changed. # setting above should also be changed.
server.base=/fhir server.base=/fhir
# For Jetty, use this:
# server.base=/hapi-fhir-jpaserver/fhir
default_encoding=JSON default_encoding=JSON
etag_support=ENABLED etag_support=ENABLED
default_page_size=20 default_page_size=20
@@ -32,8 +38,6 @@ datasource.password=fhirPass
server.name=Local Tester server.name=Local Tester
server.id=home server.id=home
test.port= test.port=
subscription.email.enabled=true
subscription.resthook.enabled=true
hibernate.dialect=org.hibernate.dialect.MySQL5Dialect hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory hibernate.search.model_mapping=ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory
hibernate.format_sql=false hibernate.format_sql=false
@@ -48,3 +52,17 @@ hibernate.search.default.directory_provider=filesystem
hibernate.search.default.indexBase=target/lucenefiles hibernate.search.default.indexBase=target/lucenefiles
hibernate.search.lucene_version=LUCENE_CURRENT hibernate.search.lucene_version=LUCENE_CURRENT
tester.config.refuse_to_fetch_third_party_urls=false tester.config.refuse_to_fetch_third_party_urls=false
##################################################
# Subscriptions
##################################################
# Enable REST Hook Subscription Channel
subscription.resthook.enabled=true
# Enable Email Subscription Channel
subscription.email.enabled=false
# Enable Websocket Subscription Channel
subscription.websocket.enabled=false

View File

@@ -5,23 +5,20 @@
metadata-complete="false" metadata-complete="false"
version="3.1"> version="3.1">
<!--
<listener> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> </listener>
<context-param> <context-param>
<param-name>contextClass</param-name> <param-name>contextClass</param-name>
<param-value> <param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext ca.uhn.fhir.jpa.starter.ApplicationContext
</param-value> </param-value>
</context-param> </context-param>
<context-param> <context-param>
<param-name>contextConfigLocation</param-name> <param-name>contextConfigLocation</param-name>
<param-value> <param-value>
ca.uhn.fhir.jpa.starter.FhirServerConfig
</param-value> </param-value>
</context-param> </context-param>
-->
<!-- Servlets --> <!-- Servlets -->
<servlet> <servlet>
@@ -33,7 +30,9 @@
</init-param> </init-param>
<init-param> <init-param>
<param-name>contextConfigLocation</param-name> <param-name>contextConfigLocation</param-name>
<param-value>ca.uhn.fhir.jpa.starter.FhirTesterConfig</param-value> <param-value>
ca.uhn.fhir.jpa.starter.FhirTesterConfig,
</param-value>
</init-param> </init-param>
<load-on-startup>2</load-on-startup> <load-on-startup>2</load-on-startup>
</servlet> </servlet>

View File

@@ -32,6 +32,7 @@ public class ExampleServerDstu2IT {
static { static {
HapiProperties.forceReload(); HapiProperties.forceReload();
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU2"); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU2");
HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr2;create=true");
HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort())); HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort()));
ourCtx = FhirContext.forDstu2(); ourCtx = FhirContext.forDstu2();
ourPort = HapiProperties.getTestPort(); ourPort = HapiProperties.getTestPort();

View File

@@ -31,6 +31,7 @@ public class ExampleServerDstu3IT {
static { static {
HapiProperties.forceReload(); HapiProperties.forceReload();
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU3"); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "DSTU3");
HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr3;create=true");
HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort())); HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort()));
ourCtx = FhirContext.forDstu3(); ourCtx = FhirContext.forDstu3();
ourPort = HapiProperties.getTestPort(); ourPort = HapiProperties.getTestPort();

View File

@@ -1,89 +1,170 @@
package ca.uhn.fhir.jpa.starter; package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.util.PortUtil; import ca.uhn.fhir.util.PortUtil;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.hl7.fhir.r4.model.Patient; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Subscription;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths; import java.net.URI;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static ca.uhn.fhir.util.TestUtil.waitForSize;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public class ExampleServerR4IT { public class ExampleServerR4IT {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR4IT.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerR4IT.class);
private static IGenericClient ourClient; private static IGenericClient ourClient;
private static FhirContext ourCtx; private static FhirContext ourCtx;
private static int ourPort; private static int ourPort;
private static Server ourServer; private static Server ourServer;
private static String ourServerBase; private static String ourServerBase;
static { static {
HapiProperties.forceReload(); HapiProperties.forceReload();
HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4"); HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:derby:memory:dbr4;create=true");
HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort())); HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4");
ourCtx = FhirContext.forR4(); HapiProperties.setProperty(HapiProperties.TEST_PORT, Integer.toString(PortUtil.findFreePort()));
ourPort = HapiProperties.getTestPort(); HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
} ourCtx = FhirContext.forR4();
ourPort = HapiProperties.getTestPort();
}
@Test @Test
public void testCreateAndRead() throws IOException { public void testCreateAndRead() throws IOException {
ourLog.info("Base URL is: http://localhost:" + ourPort + HapiProperties.getServerBase()); ourLog.info("Base URL is: http://localhost:" + ourPort + HapiProperties.getServerBase());
String methodName = "testCreateResourceConditional"; String methodName = "testCreateResourceConditional";
Patient pt = new Patient(); Patient pt = new Patient();
pt.addName().setFamily(methodName); pt.addName().setFamily(methodName);
IIdType id = ourClient.create().resource(pt).execute().getId(); IIdType id = ourClient.create().resource(pt).execute().getId();
Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute(); Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute();
assertEquals(methodName, pt2.getName().get(0).getFamily()); assertEquals(methodName, pt2.getName().get(0).getFamily());
} }
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass @Test
public static void beforeClass() throws Exception { public void testWebsocketSubscription() throws Exception {
String path = Paths.get("").toAbsolutePath().toString(); /*
* Create subscription
*/
Subscription subscription = new Subscription();
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
subscription.setCriteria("Observation?status=final");
ourLog.info("Project base path is: {}", path); Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
channel.setType(Subscription.SubscriptionChannelType.WEBSOCKET);
channel.setPayload("application/json");
subscription.setChannel(channel);
if (ourPort == 0) { MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
ourPort = RandomServerPortProvider.findFreePort(); IIdType mySubscriptionId = methodOutcome.getId();
}
ourServer = new Server(ourPort);
WebAppContext webAppContext = new WebAppContext(); // Wait for the subscription to be activated
webAppContext.setContextPath("/"); waitForSize(1, () -> ourClient.search().forResource(Subscription.class).where(Subscription.STATUS.exactly().code("active")).cacheControl(new CacheControlDirective().setNoCache(true)).returnBundle(Bundle.class).execute().getEntry().size());
webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml");
webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-starter");
webAppContext.setParentLoaderPriority(true);
ourServer.setHandler(webAppContext); /*
ourServer.start(); * Attach websocket
*/
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); WebSocketClient myWebSocketClient = new WebSocketClient();
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); SocketImplementation mySocketImplementation = new SocketImplementation(mySubscriptionId.getIdPart(), EncodingEnum.JSON);
ourServerBase = "http://localhost:" + ourPort + HapiProperties.getServerBase();
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
}
public static void main(String[] theArgs) throws Exception { myWebSocketClient.start();
ourPort = 8080; URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket");
beforeClass(); ClientUpgradeRequest request = new ClientUpgradeRequest();
} ourLog.info("Connecting to : {}", echoUri);
Future<Session> connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request);
Session session = connection.get(2, TimeUnit.SECONDS);
ourLog.info("Connected to WS: {}", session.isOpen());
/*
* Create a matching resource
*/
Observation obs = new Observation();
obs.setStatus(Observation.ObservationStatus.FINAL);
ourClient.create().resource(obs).execute();
// Give some time for the subscription to deliver
Thread.sleep(2000);
/*
* Ensure that we receive a ping on the websocket
*/
waitForSize(1, () -> mySocketImplementation.myPingCount);
/*
* Clean up
*/
ourClient.delete().resourceById(mySubscriptionId).execute();
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
/*
* This runs under maven, and I'm not sure how else to figure out the target directory from code..
*/
String path = ExampleServerR4IT.class.getClassLoader().getResource(".keep_hapi-fhir-jpaserver-starter").getPath();
path = new File(path).getParent();
path = new File(path).getParent();
path = new File(path).getParent();
ourLog.info("Project base path is: {}", path);
if (ourPort == 0) {
ourPort = RandomServerPortProvider.findFreePort();
}
ourServer = new Server(ourPort);
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/");
webAppContext.setDisplayName("HAPI FHIR");
webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml");
webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-starter");
webAppContext.setParentLoaderPriority(true);
ourServer.setHandler(webAppContext);
ourServer.start();
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
ourServerBase = "http://localhost:" + ourPort + HapiProperties.getServerBase();
ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
}
public static void main(String[] theArgs) throws Exception {
ourPort = 8080;
beforeClass();
}
} }

View File

@@ -0,0 +1,88 @@
package ca.uhn.fhir.jpa.starter;
import ca.uhn.fhir.rest.api.EncodingEnum;
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 java.util.ArrayList;
import java.util.List;
@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<String> myMessages = new ArrayList<String>();
protected int myPingCount;
protected String mySubsId;
private Session session;
public SocketImplementation(String theCriteria, EncodingEnum theEncoding) {
myCriteria = theCriteria;
}
public List<String> 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 if (theMsg.startsWith("ping ")) {
myPingCount++;
} else {
myError = "Unexpected message: " + theMsg;
}
}
}