diff --git a/.travis.yml b/.travis.yml old mode 100644 new mode 100755 diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/WebsocketDispatcherConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/WebsocketDispatcherConfig.java index 1f1b8813f5f..d4c7dbcd93f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/WebsocketDispatcherConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/WebsocketDispatcherConfig.java @@ -20,8 +20,10 @@ package ca.uhn.fhir.jpa.config; * #L% */ +import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.subscription.module.subscriber.websocket.SubscriptionWebsocketHandler; import org.springframework.beans.factory.annotation.Autowire; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Controller; @@ -35,10 +37,12 @@ import org.springframework.web.socket.handler.PerConnectionWebSocketHandler; @EnableWebSocket() @Controller public class WebsocketDispatcherConfig implements WebSocketConfigurer { + @Autowired + ModelConfig myModelConfig; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry theRegistry) { - theRegistry.addHandler(subscriptionWebSocketHandler(), "/websocket").setAllowedOrigins("*"); + theRegistry.addHandler(subscriptionWebSocketHandler(), myModelConfig.getWebsocketContextPath()).setAllowedOrigins("*"); } @Bean(autowire = Autowire.BY_TYPE) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index 79b3cc45d58..e0d55db4838 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -1580,6 +1580,21 @@ public class DaoConfig { myModelConfig.setEmailFromAddress(theEmailFromAddress); } + /** + * If websocket subscriptions are enabled, this defines the context path that listens to them. Default value "/websocket". + */ + + public String getWebsocketContextPath() { + return myModelConfig.getWebsocketContextPath(); + } + + /** + * If websocket subscriptions are enabled, this defines the context path that listens to them. Default value "/websocket". + */ + + public void setWebsocketContextPath(String theWebsocketContextPath) { + myModelConfig.setWebsocketContextPath(theWebsocketContextPath); + } public enum IndexEnabledEnum { ENABLED, diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java index dcea27d1c25..3c58314f65c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/SubscriptionsDstu3Test.java @@ -27,8 +27,6 @@ public class SubscriptionsDstu3Test extends BaseResourceProviderDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionsDstu3Test.class); - private static final String WEBSOCKET_PATH = "/websocket/dstu3"; - @Override public void beforeCreateInterceptor() { super.beforeCreateInterceptor(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java index eac3edd6280..77c114a9e77 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/SubscriptionsR4Test.java @@ -27,8 +27,6 @@ public class SubscriptionsR4Test extends BaseResourceProviderR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SubscriptionsR4Test.class); - private static final String WEBSOCKET_PATH = "/websocket/r4"; - @Override public void beforeCreateInterceptor() { super.beforeCreateInterceptor(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java index 4997528a8e5..692848a5260 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu2Test.java @@ -109,7 +109,7 @@ public class WebsocketWithSubscriptionIdDstu2Test extends BaseResourceProviderDs mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON); myWebSocketClient.start(); - URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket"); + URI echoUri = new URI("ws://localhost:" + ourPort + myModelConfig.getWebsocketContextPath()); ClientUpgradeRequest request = new ClientUpgradeRequest(); ourLog.info("Connecting to : {}", echoUri); Future connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java index 5ac36a04baa..a1cbdd421a6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdDstu3Test.java @@ -107,7 +107,7 @@ public class WebsocketWithSubscriptionIdDstu3Test extends BaseResourceProviderDs mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON); myWebSocketClient.start(); - URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket"); + URI echoUri = new URI("ws://localhost:" + ourPort + myModelConfig.getWebsocketContextPath()); ClientUpgradeRequest request = new ClientUpgradeRequest(); ourLog.info("Connecting to : {}", echoUri); Future connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdR4Test.java index 374b72efa37..57e360909b4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/websocket/WebsocketWithSubscriptionIdR4Test.java @@ -105,7 +105,7 @@ public class WebsocketWithSubscriptionIdR4Test extends BaseResourceProviderR4Tes mySocketImplementation = new SocketImplementation(mySubscriptionId, EncodingEnum.JSON); myWebSocketClient.start(); - URI echoUri = new URI("ws://localhost:" + ourPort + "/websocket"); + URI echoUri = new URI("ws://localhost:" + ourPort + myModelConfig.getWebsocketContextPath()); ClientUpgradeRequest request = new ClientUpgradeRequest(); ourLog.info("Connecting to : {}", echoUri); Future connection = myWebSocketClient.connect(mySocketImplementation, echoUri, request); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java index 22a2e6b90c7..30f1abd93a3 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ModelConfig.java @@ -47,6 +47,7 @@ public class ModelConfig { "http://hl7.org/fhir/codesystem-*", "http://hl7.org/fhir/StructureDefinition/*"))); + public static final String DEFAULT_WEBSOCKET_CONTEXT_PATH = "/websocket"; /** * update setter javadoc if default changes */ @@ -58,6 +59,7 @@ public class ModelConfig { private Set mySupportedSubscriptionTypes = new HashSet<>(); private String myEmailFromAddress = "noreply@unknown.com"; private boolean mySubscriptionMatchingEnabled = true; + private String myWebsocketContextPath = DEFAULT_WEBSOCKET_CONTEXT_PATH; /** * If set to {@code true} the default search params (i.e. the search parameters that are @@ -363,6 +365,22 @@ public class ModelConfig { myEmailFromAddress = theEmailFromAddress; } + /** + * If websocket subscriptions are enabled, this specifies the context path that listens to them. Default value "/websocket". + */ + + public String getWebsocketContextPath() { + return myWebsocketContextPath; + } + + /** + * If websocket subscriptions are enabled, this specifies the context path that listens to them. Default value "/websocket". + */ + + public void setWebsocketContextPath(String theWebsocketContextPath) { + myWebsocketContextPath = theWebsocketContextPath; + } + private static void validateTreatBaseUrlsAsLocal(String theUrl) { Validate.notBlank(theUrl, "Base URL must not be null or empty"); @@ -374,5 +392,4 @@ public class ModelConfig { } } - } diff --git a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java index 8c2f311064e..8215f25d41a 100644 --- a/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java +++ b/hapi-fhir-jpaserver-searchparam/src/main/java/ca/uhn/fhir/jpa/searchparam/retry/Retrier.java @@ -61,7 +61,7 @@ public class Retrier { @Override public void onError(RetryContext context, RetryCallback callback, Throwable throwable) { super.onError(context, callback, throwable); - ourLog.error("Retry failure " + context.getRetryCount() + "/" + theMaxRetries, throwable); + ourLog.error("Retry failure {}/{}: {}", context.getRetryCount(), theMaxRetries, throwable.getMessage()); } }; myRetryTemplate.registerListener(listener); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscription.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscription.java index 8ea6362d2c6..0ef59f43250 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscription.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscription.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.subscription.module.cache; * 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. @@ -30,10 +30,11 @@ import org.springframework.beans.factory.DisposableBean; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.SubscribableChannel; +import java.io.Closeable; import java.util.Collection; import java.util.HashSet; -public class ActiveSubscription { +public class ActiveSubscription implements Closeable { private static final Logger ourLog = LoggerFactory.getLogger(ActiveSubscription.class); private CanonicalSubscription mySubscription; @@ -62,20 +63,6 @@ public class ActiveSubscription { public void unregister(MessageHandler theMessageHandler) { if (mySubscribableChannel != null) { mySubscribableChannel.unsubscribe(theMessageHandler); - if (mySubscribableChannel instanceof DisposableBean) { - try { - ((DisposableBean) mySubscribableChannel).destroy(); - } catch (Exception e) { - ourLog.error("Failed to destroy channel bean", e); - } - } - } - - } - - public void unregisterAll() { - for (MessageHandler messageHandler : myDeliveryHandlerSet) { - unregister(messageHandler); } } @@ -103,4 +90,33 @@ public class ActiveSubscription { public void setFlagForDeletion(boolean theFlagForDeletion) { flagForDeletion = theFlagForDeletion; } + + @Override + public void close() { + for (MessageHandler messageHandler : myDeliveryHandlerSet) { + unregister(messageHandler); + } + if (mySubscribableChannel instanceof DisposableBean) { + try { + ((DisposableBean) mySubscribableChannel).destroy(); + } catch (Exception e) { + ourLog.error("Failed to destroy channel bean", e); + } + } + } + + public void removeChannel() { + if (mySubscribableChannel instanceof IRemovableChannel) { + ((IRemovableChannel)mySubscribableChannel).removeChannel(); + } + } + + /** + * Use close() instead + * KHS 15 Apr 2019 + */ + @Deprecated + public void unregisterAll() { + close(); + } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscriptionCache.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscriptionCache.java index b93b88566af..03dfc623ba2 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscriptionCache.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/ActiveSubscriptionCache.java @@ -51,7 +51,7 @@ class ActiveSubscriptionCache { myCache.put(theSubscriptionId, theValue); } - public void remove(String theSubscriptionId) { + public synchronized void remove(String theSubscriptionId) { Validate.notBlank(theSubscriptionId); ActiveSubscription activeSubscription = myCache.get(theSubscriptionId); @@ -59,7 +59,8 @@ class ActiveSubscriptionCache { return; } - activeSubscription.unregisterAll(); + activeSubscription.close(); + activeSubscription.removeChannel(); myCache.remove(theSubscriptionId); } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/IRemovableChannel.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/IRemovableChannel.java new file mode 100644 index 00000000000..8a391ab5b9e --- /dev/null +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/module/cache/IRemovableChannel.java @@ -0,0 +1,5 @@ +package ca.uhn.fhir.jpa.subscription.module.cache; + +public interface IRemovableChannel { + void removeChannel(); +} diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 47549f24b2c..62b1cfdb530 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -155,6 +155,15 @@ When validating DSTU3 QuestionnaireResponses that leverage the "enableWhen" functionality available in Questionnaire resources, the validation could sometimes fail incorrectly. + + Added new configuration parameter to DaoConfig and ModelConfig to specify the websocket context path. + (Before it was hardcoded to "/websocket"). + + + Added new IRemovableChannel interface. If a SubscriptionChannel implements this, then when a subscription + channel is destroyed (because its subscription is deleted) then the remove() method will be called on that + channel. +