diff --git a/build.gradle b/build.gradle index ebb4636a6d..3c9187aca3 100644 --- a/build.gradle +++ b/build.gradle @@ -991,6 +991,7 @@ project("spring-test") { optional(project(":spring-web")) optional(project(":spring-webmvc")) optional(project(":spring-webmvc-portlet")) + optional(project(":spring-websocket")) optional("junit:junit:${junitVersion}") optional("org.testng:testng:${testngVersion}") optional("javax.inject:javax.inject:1") @@ -1002,6 +1003,7 @@ project("spring-test") { } optional("javax.portlet:portlet-api:2.0") optional("javax.el:javax.el-api:2.2.5") + optional("javax.websocket:javax.websocket-api:1.0") optional("org.aspectj:aspectjweaver:${aspectjVersion}") optional("org.codehaus.groovy:groovy-all:${groovyVersion}") optional("org.hamcrest:hamcrest-core:${hamcrestVersion}") diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java new file mode 100644 index 0000000000..9996e733e2 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainer.java @@ -0,0 +1,134 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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. + */ + +package org.springframework.test.context.web.socket; + +import java.io.IOException; +import java.net.URI; +import java.util.Collections; +import java.util.Set; + +import javax.websocket.ClientEndpointConfig; +import javax.websocket.DeploymentException; +import javax.websocket.Endpoint; +import javax.websocket.Extension; +import javax.websocket.Session; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpointConfig; + +/** + * Mock implementation of the {@link javax.websocket.server.ServerContainer} + * interface. + * + * @author Sam Brannen + * @since 4.3.1 + */ +class MockServerContainer implements ServerContainer { + + private long defaultAsyncSendTimeout; + + private long defaultMaxSessionIdleTimeout; + + private int defaultMaxBinaryMessageBufferSize; + + private int defaultMaxTextMessageBufferSize; + + + // --- WebSocketContainer -------------------------------------------------- + + @Override + public long getDefaultAsyncSendTimeout() { + return this.defaultAsyncSendTimeout; + } + + @Override + public void setAsyncSendTimeout(long timeout) { + this.defaultAsyncSendTimeout = timeout; + } + + @Override + public long getDefaultMaxSessionIdleTimeout() { + return this.defaultMaxSessionIdleTimeout; + } + + @Override + public void setDefaultMaxSessionIdleTimeout(long timeout) { + this.defaultMaxSessionIdleTimeout = timeout; + } + + @Override + public int getDefaultMaxBinaryMessageBufferSize() { + return this.defaultMaxBinaryMessageBufferSize; + } + + @Override + public void setDefaultMaxBinaryMessageBufferSize(int max) { + this.defaultMaxBinaryMessageBufferSize = max; + } + + @Override + public int getDefaultMaxTextMessageBufferSize() { + return this.defaultMaxTextMessageBufferSize; + } + + @Override + public void setDefaultMaxTextMessageBufferSize(int max) { + this.defaultMaxTextMessageBufferSize = max; + } + + @Override + public Set getInstalledExtensions() { + return Collections.emptySet(); + } + + @Override + public Session connectToServer(Object annotatedEndpointInstance, URI path) throws DeploymentException, IOException { + throw new UnsupportedOperationException("MockServerContainer does not support connectToServer(Object, URI)"); + } + + @Override + public Session connectToServer(Class annotatedEndpointClass, URI path) throws DeploymentException, IOException { + throw new UnsupportedOperationException("MockServerContainer does not support connectToServer(Class, URI)"); + } + + @Override + public Session connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path) + throws DeploymentException, IOException { + throw new UnsupportedOperationException( + "MockServerContainer does not support connectToServer(Endpoint, ClientEndpointConfig, URI)"); + } + + @Override + public Session connectToServer(Class endpointClass, ClientEndpointConfig cec, URI path) + throws DeploymentException, IOException { + throw new UnsupportedOperationException( + "MockServerContainer does not support connectToServer(Class, ClientEndpointConfig, URI)"); + } + + // --- ServerContainer ----------------------------------------------------- + + @Override + public void addEndpoint(Class endpointClass) throws DeploymentException { + throw new UnsupportedOperationException("MockServerContainer does not support addEndpoint(Class)"); + } + + @Override + public void addEndpoint(ServerEndpointConfig serverConfig) throws DeploymentException { + throw new UnsupportedOperationException( + "MockServerContainer does not support addEndpoint(ServerEndpointConfig)"); + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizer.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizer.java new file mode 100644 index 0000000000..f08d533723 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizer.java @@ -0,0 +1,42 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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. + */ + +package org.springframework.test.context.web.socket; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.MergedContextConfiguration; +import org.springframework.web.context.WebApplicationContext; + +/** + * {@link ContextCustomizer} that instantiates a new {@link MockServerContainer} + * and stores it in the {@code ServletContext} under the attribute named + * {@code "javax.websocket.server.ServerContainer"}. + * + * @author Sam Brannen + * @since 4.3.1 + */ +class MockServerContainerContextCustomizer implements ContextCustomizer { + + @Override + public void customizeContext(ConfigurableApplicationContext context, MergedContextConfiguration mergedConfig) { + if (context instanceof WebApplicationContext) { + WebApplicationContext wac = (WebApplicationContext) context; + wac.getServletContext().setAttribute("javax.websocket.server.ServerContainer", new MockServerContainer()); + } + } + +} diff --git a/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizerFactory.java b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizerFactory.java new file mode 100644 index 0000000000..d1adb79985 --- /dev/null +++ b/spring-test/src/main/java/org/springframework/test/context/web/socket/MockServerContainerContextCustomizerFactory.java @@ -0,0 +1,73 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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. + */ + +package org.springframework.test.context.web.socket; + +import java.util.List; + +import org.springframework.beans.BeanUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.ContextCustomizerFactory; +import org.springframework.util.ClassUtils; + +/** + * {@link ContextCustomizerFactory} which creates a {@link MockServerContainerContextCustomizer} + * if WebSocket support is present in the classpath and the test class is annotated + * with {@code @WebAppConfiguration}. + * + * @author Sam Brannen + * @since 4.3.1 + */ +class MockServerContainerContextCustomizerFactory implements ContextCustomizerFactory { + + private static final boolean webSocketPresent = ClassUtils.isPresent("javax.websocket.server.ServerContainer", + MockServerContainerContextCustomizerFactory.class.getClassLoader()); + + private static final String WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME = + "org.springframework.test.context.web.WebAppConfiguration"; + + private static final String MOCK_SERVER_CONTAINER_CONTEXT_CUSTOMIZER_CLASS_NAME = + "org.springframework.test.context.web.socket.MockServerContainerContextCustomizer"; + + + @Override + public ContextCustomizer createContextCustomizer(Class testClass, + List configAttributes) { + + if (webSocketPresent && isAnnotatedWithWebAppConfiguration(testClass)) { + try { + Class clazz = ClassUtils.forName(MOCK_SERVER_CONTAINER_CONTEXT_CUSTOMIZER_CLASS_NAME, + getClass().getClassLoader()); + return (ContextCustomizer) BeanUtils.instantiateClass(clazz); + } + catch (Throwable ex) { + throw new IllegalStateException("Failed to enable WebSocket test support; could not load class: " + + MOCK_SERVER_CONTAINER_CONTEXT_CUSTOMIZER_CLASS_NAME, ex); + } + } + + // Else, nothing to customize + return null; + } + + private static boolean isAnnotatedWithWebAppConfiguration(Class testClass) { + return AnnotatedElementUtils.findMergedAnnotationAttributes(testClass, + WEB_APP_CONFIGURATION_ANNOTATION_CLASS_NAME, false, false) != null; + } + +} diff --git a/spring-test/src/main/resources/META-INF/spring.factories b/spring-test/src/main/resources/META-INF/spring.factories index 012b38df91..30cd85a182 100644 --- a/spring-test/src/main/resources/META-INF/spring.factories +++ b/spring-test/src/main/resources/META-INF/spring.factories @@ -7,3 +7,8 @@ org.springframework.test.context.TestExecutionListener = \ org.springframework.test.context.support.DirtiesContextTestExecutionListener,\ org.springframework.test.context.transaction.TransactionalTestExecutionListener,\ org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener + +# Default ContextCustomizerFactory implementations for the Spring TestContext Framework +# +org.springframework.test.context.ContextCustomizerFactory = \ + org.springframework.test.context.web.socket.MockServerContainerContextCustomizerFactory diff --git a/spring-test/src/test/java/org/springframework/test/context/web/socket/WebSocketServletServerContainerFactoryBeanTests.java b/spring-test/src/test/java/org/springframework/test/context/web/socket/WebSocketServletServerContainerFactoryBeanTests.java new file mode 100644 index 0000000000..f35b87f9fe --- /dev/null +++ b/spring-test/src/test/java/org/springframework/test/context/web/socket/WebSocketServletServerContainerFactoryBeanTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2016 the original author or authors. + * + * 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. + */ + +package org.springframework.test.context.web.socket; + +import javax.websocket.server.ServerContainer; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.socket.config.annotation.EnableWebSocket; +import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; + +import static org.junit.Assert.*; + +/** + * Integration tests that validate support for {@link ServletServerContainerFactoryBean} + * in conjunction with {@link WebAppConfiguration @WebAppConfiguration} and the + * Spring TestContext Framework. + * + * @author Sam Brannen + * @since 4.3.1 + */ +@RunWith(SpringRunner.class) +@WebAppConfiguration +public class WebSocketServletServerContainerFactoryBeanTests { + + @Autowired + ServerContainer serverContainer; + + + @Test + public void servletServerContainerFactoryBeanSupport() { + assertEquals(42, serverContainer.getDefaultMaxTextMessageBufferSize()); + } + + + @Configuration + @EnableWebSocket + static class WebSocketConfig { + + @Bean + ServletServerContainerFactoryBean createWebSocketContainer() { + ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); + container.setMaxTextMessageBufferSize(42); + return container; + } + } + +} diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java index 54b38bac97..7fe861464f 100644 --- a/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java +++ b/spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServletServerContainerFactoryBean.java @@ -53,7 +53,7 @@ public class ServletServerContainerFactoryBean private Integer maxTextMessageBufferSize; private Integer maxBinaryMessageBufferSize; - + private ServletContext servletContext; private ServerContainer serverContainer;