From ecf75bd6cd4a5046040d9b04ea1d23e392c8aa14 Mon Sep 17 00:00:00 2001 From: Jason Zhang Date: Tue, 25 Apr 2017 16:13:18 -0600 Subject: [PATCH] Add capability to support configuration --- .../websockets/WebsocketBundle.java | 44 +++++++++- .../WebsocketBundleConfiguration.java | 30 +++++++ .../websockets/WebsocketConfiguration.java | 85 +++++++++++++++++++ .../websockets/DropWizardWebsocketsTest.java | 14 +-- .../java/io/dropwizard/websockets/MyApp.java | 13 ++- .../websockets/MyAppConfiguration.java | 46 ++++++++++ src/test/resources/server.yml | 3 + 7 files changed, 219 insertions(+), 16 deletions(-) create mode 100644 src/main/java/io/dropwizard/websockets/WebsocketBundleConfiguration.java create mode 100644 src/main/java/io/dropwizard/websockets/WebsocketConfiguration.java create mode 100644 src/test/java/io/dropwizard/websockets/MyAppConfiguration.java diff --git a/src/main/java/io/dropwizard/websockets/WebsocketBundle.java b/src/main/java/io/dropwizard/websockets/WebsocketBundle.java index 03b33ad..9e6950e 100644 --- a/src/main/java/io/dropwizard/websockets/WebsocketBundle.java +++ b/src/main/java/io/dropwizard/websockets/WebsocketBundle.java @@ -22,10 +22,14 @@ */ package io.dropwizard.websockets; -import io.dropwizard.Bundle; +import com.google.common.base.Optional; +import io.dropwizard.Configuration; +import io.dropwizard.ConfiguredBundle; import io.dropwizard.metrics.jetty9.websockets.InstWebSocketServerContainerInitializer; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; +import io.dropwizard.util.Duration; +import io.dropwizard.util.Size; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.websocket.jsr356.server.ServerContainer; @@ -42,12 +46,13 @@ import static io.dropwizard.websockets.GeneralUtils.rethrow; -public class WebsocketBundle implements Bundle { +public class WebsocketBundle implements ConfiguredBundle { private final Collection endpointConfigs = new ArrayList<>(); private static final Logger LOG = LoggerFactory.getLogger(WebsocketBundle.class); volatile boolean starting = false; private ServerEndpointConfig.Configurator defaultConfigurator; + public static final WebsocketConfiguration DEFAULT_CONFIG = new WebsocketConfiguration(); public WebsocketBundle(ServerEndpointConfig.Configurator defaultConfigurator, Class... endpoints) { @@ -74,6 +79,14 @@ public void addEndpoint(ServerEndpointConfig epC) { throw new RuntimeException("can't add endpoint after starting lifecycle"); } + protected WebsocketConfiguration getWebsocketConfiguration(T configuration) { + if (configuration instanceof WebsocketBundleConfiguration) { + return ((WebsocketBundleConfiguration) configuration).getWebsocketConfiguration(); + } else { + return DEFAULT_CONFIG; + } + } + public void addEndpoint(Class clazz) { ServerEndpoint anno = clazz.getAnnotation(ServerEndpoint.class); if(anno == null){ @@ -93,7 +106,11 @@ public void initialize(Bootstrap bootstrap) { } @Override - public void run(Environment environment) { + public void run(T configuration, Environment environment) { + final WebsocketConfiguration websocketConfiguration = getWebsocketConfiguration(configuration); + if(websocketConfiguration == null) { + throw new RuntimeException("You need to provide an implementation of WebsocketBundleConfigurationInterface"); + } environment.lifecycle().addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() { @Override @@ -103,6 +120,8 @@ public void lifeCycleStarting(LifeCycle event) { ServerContainer wsContainer = InstWebSocketServerContainerInitializer. configureContext(environment.getApplicationContext(), environment.metrics()); + setWebsocketConfiguration(wsContainer, websocketConfiguration); + StringBuilder sb = new StringBuilder("Registering websocket endpoints: ") .append(System.lineSeparator()) .append(System.lineSeparator()); @@ -117,6 +136,25 @@ private void addEndpoint(ServerContainer wsContainer, ServerEndpointConfig conf, wsContainer.addEndpoint(conf); sb.append(String.format(" WS %s (%s)", conf.getPath(), conf.getEndpointClass().getName())).append(System.lineSeparator()); } + + private void setWebsocketConfiguration(ServerContainer serverContainer, WebsocketConfiguration websocketConfiguration) { + Optional idleTimeout = Optional.fromNullable(websocketConfiguration.getMaxSessionIdleTimeout()); + if (idleTimeout.isPresent()) { + serverContainer.setDefaultMaxSessionIdleTimeout(idleTimeout.get().toMilliseconds()); + } + Optional asyncTimeout = Optional.fromNullable(websocketConfiguration.getAsyncSendTimeout()); + if (asyncTimeout.isPresent()) { + serverContainer.setAsyncSendTimeout(asyncTimeout.get().toMilliseconds()); + } + Optional binarySize = Optional.fromNullable(websocketConfiguration.getMaxBinaryMessageBufferSize()); + if (binarySize.isPresent()) { + serverContainer.setDefaultMaxBinaryMessageBufferSize((int) binarySize.get().toBytes()); + } + Optional textSize = Optional.fromNullable(websocketConfiguration.getMaxTextMessageBufferSize()); + if (textSize.isPresent()) { + serverContainer.setDefaultMaxTextMessageBufferSize((int) textSize.get().toBytes()); + } + } }); } diff --git a/src/main/java/io/dropwizard/websockets/WebsocketBundleConfiguration.java b/src/main/java/io/dropwizard/websockets/WebsocketBundleConfiguration.java new file mode 100644 index 0000000..ab16697 --- /dev/null +++ b/src/main/java/io/dropwizard/websockets/WebsocketBundleConfiguration.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2017 LivePerson, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.dropwizard.websockets; + + +public interface WebsocketBundleConfiguration { + + WebsocketConfiguration getWebsocketConfiguration(); + +} diff --git a/src/main/java/io/dropwizard/websockets/WebsocketConfiguration.java b/src/main/java/io/dropwizard/websockets/WebsocketConfiguration.java new file mode 100644 index 0000000..c3b9af1 --- /dev/null +++ b/src/main/java/io/dropwizard/websockets/WebsocketConfiguration.java @@ -0,0 +1,85 @@ +/** + * The MIT License + * Copyright (c) 2017 LivePerson, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.dropwizard.websockets; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.dropwizard.util.Duration; +import io.dropwizard.util.Size; +import io.dropwizard.validation.MinDuration; +import io.dropwizard.validation.MinSize; + +public class WebsocketConfiguration { + + @MinDuration(0L) + private Duration maxSessionIdleTimeout; + + @MinDuration(0L) + private Duration asyncSendTimeout; + + @MinSize(1L) + private Size maxBinaryMessageBufferSize; + + @MinSize(1L) + private Size maxTextMessageBufferSize; + + @JsonProperty + public Duration getMaxSessionIdleTimeout() { + return maxSessionIdleTimeout; + } + + @JsonProperty + public void setMaxSessionIdleTimeout(Duration maxSessionIdleTimeout) { + this.maxSessionIdleTimeout = maxSessionIdleTimeout; + } + + @JsonProperty + public Duration getAsyncSendTimeout() { + return asyncSendTimeout; + } + + @JsonProperty + public void setAsyncSendTimeout(Duration asyncSendTimeout) { + this.asyncSendTimeout = asyncSendTimeout; + } + + @JsonProperty + public Size getMaxBinaryMessageBufferSize() { + return maxBinaryMessageBufferSize; + } + + @JsonProperty + public void setMaxBinaryMessageBufferSize(Size maxBinaryMessageBufferSize) { + this.maxBinaryMessageBufferSize = maxBinaryMessageBufferSize; + } + + @JsonProperty + public Size getMaxTextMessageBufferSize() { + return maxTextMessageBufferSize; + } + + @JsonProperty + public void setMaxTextMessageBufferSize(Size maxTextMessageBufferSize) { + this.maxTextMessageBufferSize = maxTextMessageBufferSize; + } + +} diff --git a/src/test/java/io/dropwizard/websockets/DropWizardWebsocketsTest.java b/src/test/java/io/dropwizard/websockets/DropWizardWebsocketsTest.java index d346d55..d39b41a 100644 --- a/src/test/java/io/dropwizard/websockets/DropWizardWebsocketsTest.java +++ b/src/test/java/io/dropwizard/websockets/DropWizardWebsocketsTest.java @@ -25,11 +25,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.io.Resources; -import java.io.IOException; -import java.net.URI; -import java.util.concurrent.CountDownLatch; -import static java.util.concurrent.TimeUnit.SECONDS; -import javax.websocket.Session; import junit.framework.Assert; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpGet; @@ -42,11 +37,18 @@ import org.glassfish.tyrus.client.ClientProperties; import org.glassfish.tyrus.ext.client.java8.SessionBuilder; import org.junit.After; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import javax.websocket.Session; +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.CountDownLatch; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.Assert.assertTrue; + public class DropWizardWebsocketsTest { @BeforeClass public static void setUpClass() throws InterruptedException, IOException { diff --git a/src/test/java/io/dropwizard/websockets/MyApp.java b/src/test/java/io/dropwizard/websockets/MyApp.java index 16ce8a8..8999cc1 100644 --- a/src/test/java/io/dropwizard/websockets/MyApp.java +++ b/src/test/java/io/dropwizard/websockets/MyApp.java @@ -27,7 +27,6 @@ import com.codahale.metrics.annotation.Timed; import com.codahale.metrics.health.HealthCheck; import io.dropwizard.Application; -import io.dropwizard.Configuration; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import org.eclipse.jetty.util.component.AbstractLifeCycle; @@ -47,7 +46,7 @@ import java.security.spec.InvalidKeySpecException; import java.util.concurrent.CountDownLatch; -public class MyApp extends Application { +public class MyApp extends Application { private final CountDownLatch cdl; MyApp(CountDownLatch cdl) { @@ -55,15 +54,15 @@ public class MyApp extends Application { } @Override - public void initialize(Bootstrap bootstrap) { - websocketBundle = new WebsocketBundle(AnnotatedEchoServer.class); + public void initialize(Bootstrap bootstrap) { + websocketBundle = new WebsocketBundle(AnnotatedEchoServer.class); bootstrap.addBundle(websocketBundle); } private WebsocketBundle websocketBundle; @Override - public void run(Configuration configuration, Environment environment) throws InvalidKeySpecException, NoSuchAlgorithmException, ServletException, DeploymentException { + public void run(MyAppConfiguration configuration, Environment environment) throws InvalidKeySpecException, NoSuchAlgorithmException, ServletException, DeploymentException { environment.lifecycle().addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() { @Override @@ -74,8 +73,8 @@ public void lifeCycleStarted(LifeCycle event) { environment.jersey().register(new MyResource()); environment.healthChecks().register("alive", new HealthCheck() { @Override - protected HealthCheck.Result check() throws Exception { - return HealthCheck.Result.healthy(); + protected Result check() throws Exception { + return Result.healthy(); } }); diff --git a/src/test/java/io/dropwizard/websockets/MyAppConfiguration.java b/src/test/java/io/dropwizard/websockets/MyAppConfiguration.java new file mode 100644 index 0000000..2aa50b6 --- /dev/null +++ b/src/test/java/io/dropwizard/websockets/MyAppConfiguration.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2017 LivePerson, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package io.dropwizard.websockets; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.dropwizard.Configuration; + +import javax.validation.Valid; + +public class MyAppConfiguration extends Configuration implements WebsocketBundleConfiguration { + + @Valid + private WebsocketConfiguration websocketConfiguration = new WebsocketConfiguration(); + + @Override + @JsonProperty("websockets") + public WebsocketConfiguration getWebsocketConfiguration() { + return websocketConfiguration; + } + + @JsonProperty("websockets") + public void setWebsocketConfiguration(WebsocketConfiguration websocketConfiguration) { + this.websocketConfiguration = websocketConfiguration; + } +} diff --git a/src/test/resources/server.yml b/src/test/resources/server.yml index 5c2ae86..a0d1db2 100644 --- a/src/test/resources/server.yml +++ b/src/test/resources/server.yml @@ -5,3 +5,6 @@ server: adminConnectors: - type: http port: 48081 + +websockets: + maxSessionIdleTimeout: 100s \ No newline at end of file