Reimplemented ServerEndpointExporter to avoid BeanPostProcessor role
Issue: SPR-12340
This commit is contained in:
@@ -19,6 +19,7 @@ package org.springframework.web.socket.server.standard;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.websocket.DeploymentException;
|
import javax.websocket.DeploymentException;
|
||||||
@@ -27,7 +28,7 @@ import javax.websocket.server.ServerEndpoint;
|
|||||||
import javax.websocket.server.ServerEndpointConfig;
|
import javax.websocket.server.ServerEndpointConfig;
|
||||||
|
|
||||||
import org.springframework.beans.factory.InitializingBean;
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.web.context.support.WebApplicationObjectSupport;
|
import org.springframework.web.context.support.WebApplicationObjectSupport;
|
||||||
@@ -41,7 +42,7 @@ import org.springframework.web.context.support.WebApplicationObjectSupport;
|
|||||||
*
|
*
|
||||||
* <p>When this class is used, by declaring it in Spring configuration, it should be
|
* <p>When this class is used, by declaring it in Spring configuration, it should be
|
||||||
* possible to turn off a Servlet container's scan for WebSocket endpoints. This can be
|
* possible to turn off a Servlet container's scan for WebSocket endpoints. This can be
|
||||||
* done with the help of the {@code <absolute-ordering>} element in web.xml.
|
* done with the help of the {@code <absolute-ordering>} element in {@code web.xml}.
|
||||||
*
|
*
|
||||||
* @author Rossen Stoyanchev
|
* @author Rossen Stoyanchev
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
@@ -50,19 +51,27 @@ import org.springframework.web.context.support.WebApplicationObjectSupport;
|
|||||||
* @see SpringConfigurator
|
* @see SpringConfigurator
|
||||||
* @see ServletServerContainerFactoryBean
|
* @see ServletServerContainerFactoryBean
|
||||||
*/
|
*/
|
||||||
public class ServerEndpointExporter extends WebApplicationObjectSupport implements BeanPostProcessor, InitializingBean {
|
public class ServerEndpointExporter extends WebApplicationObjectSupport
|
||||||
|
implements InitializingBean, SmartInitializingSingleton {
|
||||||
private ServerContainer serverContainer;
|
|
||||||
|
|
||||||
private List<Class<?>> annotatedEndpointClasses;
|
private List<Class<?>> annotatedEndpointClasses;
|
||||||
|
|
||||||
private Set<Class<?>> annotatedEndpointBeanTypes;
|
private ServerContainer serverContainer;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly list annotated endpoint types that should be registered on startup. This
|
||||||
|
* can be done if you wish to turn off a Servlet container's scan for endpoints, which
|
||||||
|
* goes through all 3rd party jars in the, and rely on Spring configuration instead.
|
||||||
|
* @param annotatedEndpointClasses {@link ServerEndpoint}-annotated types
|
||||||
|
*/
|
||||||
|
public void setAnnotatedEndpointClasses(Class<?>... annotatedEndpointClasses) {
|
||||||
|
this.annotatedEndpointClasses = Arrays.asList(annotatedEndpointClasses);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the JSR-356 {@link ServerContainer} to use for endpoint registration.
|
* Set the JSR-356 {@link ServerContainer} to use for endpoint registration.
|
||||||
* If not set, the container is going to be retrieved via the {@code ServletContext}.
|
* If not set, the container is going to be retrieved via the {@code ServletContext}.
|
||||||
* @since 4.1
|
|
||||||
*/
|
*/
|
||||||
public void setServerContainer(ServerContainer serverContainer) {
|
public void setServerContainer(ServerContainer serverContainer) {
|
||||||
this.serverContainer = serverContainer;
|
this.serverContainer = serverContainer;
|
||||||
@@ -75,33 +84,6 @@ public class ServerEndpointExporter extends WebApplicationObjectSupport implemen
|
|||||||
return this.serverContainer;
|
return this.serverContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Explicitly list annotated endpoint types that should be registered on startup. This
|
|
||||||
* can be done if you wish to turn off a Servlet container's scan for endpoints, which
|
|
||||||
* goes through all 3rd party jars in the, and rely on Spring configuration instead.
|
|
||||||
* @param annotatedEndpointClasses {@link ServerEndpoint}-annotated types
|
|
||||||
*/
|
|
||||||
public void setAnnotatedEndpointClasses(Class<?>... annotatedEndpointClasses) {
|
|
||||||
this.annotatedEndpointClasses = Arrays.asList(annotatedEndpointClasses);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initApplicationContext(ApplicationContext context) {
|
|
||||||
// Initializes ServletContext given a WebApplicationContext
|
|
||||||
super.initApplicationContext(context);
|
|
||||||
|
|
||||||
// Retrieve beans which are annotated with @ServerEndpoint
|
|
||||||
this.annotatedEndpointBeanTypes = new LinkedHashSet<Class<?>>();
|
|
||||||
String[] beanNames = context.getBeanNamesForAnnotation(ServerEndpoint.class);
|
|
||||||
for (String beanName : beanNames) {
|
|
||||||
Class<?> beanType = context.getType(beanName);
|
|
||||||
if (logger.isInfoEnabled()) {
|
|
||||||
logger.info("Detected @ServerEndpoint bean '" + beanName + "', registering it as an endpoint by type");
|
|
||||||
}
|
|
||||||
this.annotatedEndpointBeanTypes.add(beanType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initServletContext(ServletContext servletContext) {
|
protected void initServletContext(ServletContext servletContext) {
|
||||||
if (this.serverContainer == null) {
|
if (this.serverContainer == null) {
|
||||||
@@ -110,64 +92,76 @@ public class ServerEndpointExporter extends WebApplicationObjectSupport implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isContextRequired() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() {
|
public void afterPropertiesSet() {
|
||||||
Assert.state(getServerContainer() != null, "javax.websocket.server.ServerContainer not available");
|
Assert.state(getServerContainer() != null, "javax.websocket.server.ServerContainer not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterSingletonsInstantiated() {
|
||||||
registerEndpoints();
|
registerEndpoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Actually register the endpoints. Called by {@link #afterPropertiesSet()}.
|
* Actually register the endpoints. Called by {@link #afterSingletonsInstantiated()}.
|
||||||
* @since 4.1
|
|
||||||
*/
|
*/
|
||||||
protected void registerEndpoints() {
|
protected void registerEndpoints() {
|
||||||
Set<Class<?>> endpointClasses = new LinkedHashSet<Class<?>>();
|
Set<Class<?>> endpointClasses = new LinkedHashSet<Class<?>>();
|
||||||
if (this.annotatedEndpointClasses != null) {
|
if (this.annotatedEndpointClasses != null) {
|
||||||
endpointClasses.addAll(this.annotatedEndpointClasses);
|
endpointClasses.addAll(this.annotatedEndpointClasses);
|
||||||
}
|
}
|
||||||
if (this.annotatedEndpointBeanTypes != null) {
|
|
||||||
endpointClasses.addAll(this.annotatedEndpointBeanTypes);
|
ApplicationContext context = getApplicationContext();
|
||||||
|
if (context != null) {
|
||||||
|
String[] endpointNames = context.getBeanNamesForAnnotation(ServerEndpoint.class);
|
||||||
|
for (String beanName : endpointNames) {
|
||||||
|
Class<?> beanType = context.getType(beanName);
|
||||||
|
endpointClasses.add(beanType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Class<?> endpointClass : endpointClasses) {
|
for (Class<?> endpointClass : endpointClasses) {
|
||||||
registerEndpoint(endpointClass);
|
registerEndpoint(endpointClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context != null) {
|
||||||
|
Map<String, ServerEndpointConfig> endpointConfigMap = context.getBeansOfType(ServerEndpointConfig.class);
|
||||||
|
for (Map.Entry<String, ServerEndpointConfig> configEntry : endpointConfigMap.entrySet()) {
|
||||||
|
String beanName = configEntry.getKey();
|
||||||
|
ServerEndpointConfig endpointConfig = configEntry.getValue();
|
||||||
|
registerEndpoint(endpointConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerEndpoint(Class<?> endpointClass) {
|
private void registerEndpoint(Class<?> endpointClass) {
|
||||||
try {
|
try {
|
||||||
if (logger.isInfoEnabled()) {
|
if (logger.isInfoEnabled()) {
|
||||||
logger.info("Registering @ServerEndpoint type: " + endpointClass);
|
logger.info("Registering @ServerEndpoint class: " + endpointClass);
|
||||||
}
|
}
|
||||||
getServerContainer().addEndpoint(endpointClass);
|
getServerContainer().addEndpoint(endpointClass);
|
||||||
}
|
}
|
||||||
catch (DeploymentException ex) {
|
catch (DeploymentException ex) {
|
||||||
throw new IllegalStateException("Failed to register @ServerEndpoint type " + endpointClass, ex);
|
throw new IllegalStateException("Failed to register @ServerEndpoint class: " + endpointClass, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void registerEndpoint(ServerEndpointConfig endpointConfig) {
|
||||||
@Override
|
try {
|
||||||
public Object postProcessBeforeInitialization(Object bean, String beanName) {
|
if (logger.isInfoEnabled()) {
|
||||||
return bean;
|
logger.info("Registering ServerEndpointConfig: " + endpointConfig);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object postProcessAfterInitialization(Object bean, String beanName) {
|
|
||||||
if (bean instanceof ServerEndpointConfig) {
|
|
||||||
ServerEndpointConfig endpointConfig = (ServerEndpointConfig) bean;
|
|
||||||
try {
|
|
||||||
if (logger.isInfoEnabled()) {
|
|
||||||
logger.info("Registering bean '" + beanName +
|
|
||||||
"' as javax.websocket.Endpoint under path " + endpointConfig.getPath());
|
|
||||||
}
|
|
||||||
getServerContainer().addEndpoint(endpointConfig);
|
|
||||||
}
|
|
||||||
catch (DeploymentException ex) {
|
|
||||||
throw new IllegalStateException("Failed to deploy Endpoint bean with name '" + bean + "'", ex);
|
|
||||||
}
|
}
|
||||||
|
getServerContainer().addEndpoint(endpointConfig);
|
||||||
|
}
|
||||||
|
catch (DeploymentException ex) {
|
||||||
|
throw new IllegalStateException("Failed to register ServerEndpointConfig: " + endpointConfig, ex);
|
||||||
}
|
}
|
||||||
return bean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class ServerEndpointRegistration extends ServerEndpointConfig.Configurato
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link ServerEndpointRegistration} instance from an
|
* Create a new {@link ServerEndpointRegistration} instance from an
|
||||||
* {@code javax.webscoket.Endpoint} class.
|
* {@code javax.websocket.Endpoint} class.
|
||||||
* @param path the endpoint path
|
* @param path the endpoint path
|
||||||
* @param endpointClass the endpoint class
|
* @param endpointClass the endpoint class
|
||||||
*/
|
*/
|
||||||
@@ -202,4 +202,9 @@ public class ServerEndpointRegistration extends ServerEndpointConfig.Configurato
|
|||||||
return super.getNegotiatedExtensions(installed, requested);
|
return super.getNegotiatedExtensions(installed, requested);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ServerEndpointRegistration for path '" + getPath() + "': " + getEndpointClass();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,30 +67,33 @@ public class ServerEndpointExporterTests {
|
|||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addAnnotatedEndpointBeans() throws Exception {
|
public void addAnnotatedEndpointClasses() throws Exception {
|
||||||
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class);
|
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class);
|
||||||
this.exporter.setApplicationContext(this.webAppContext);
|
this.exporter.setApplicationContext(this.webAppContext);
|
||||||
this.exporter.afterPropertiesSet();
|
this.exporter.afterPropertiesSet();
|
||||||
|
this.exporter.afterSingletonsInstantiated();
|
||||||
|
|
||||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
|
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
|
||||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
|
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addAnnotatedEndpointBeansWithServletContextOnly() throws Exception {
|
public void addAnnotatedEndpointClassesWithServletContextOnly() throws Exception {
|
||||||
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class, AnnotatedDummyEndpointBean.class);
|
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class, AnnotatedDummyEndpointBean.class);
|
||||||
this.exporter.setServletContext(this.servletContext);
|
this.exporter.setServletContext(this.servletContext);
|
||||||
this.exporter.afterPropertiesSet();
|
this.exporter.afterPropertiesSet();
|
||||||
|
this.exporter.afterSingletonsInstantiated();
|
||||||
|
|
||||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
|
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
|
||||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
|
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addAnnotatedEndpointBeansWithServerContainerOnly() throws Exception {
|
public void addAnnotatedEndpointClassesWithExplicitServerContainerOnly() throws Exception {
|
||||||
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class, AnnotatedDummyEndpointBean.class);
|
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class, AnnotatedDummyEndpointBean.class);
|
||||||
this.exporter.setServerContainer(this.serverContainer);
|
this.exporter.setServerContainer(this.serverContainer);
|
||||||
this.exporter.afterPropertiesSet();
|
this.exporter.afterPropertiesSet();
|
||||||
|
this.exporter.afterSingletonsInstantiated();
|
||||||
|
|
||||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
|
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
|
||||||
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
|
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
|
||||||
@@ -98,31 +101,40 @@ public class ServerEndpointExporterTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addServerEndpointConfigBean() throws Exception {
|
public void addServerEndpointConfigBean() throws Exception {
|
||||||
|
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
|
||||||
|
this.webAppContext.getBeanFactory().registerSingleton("dummyEndpoint", endpointRegistration);
|
||||||
|
|
||||||
this.exporter.setApplicationContext(this.webAppContext);
|
this.exporter.setApplicationContext(this.webAppContext);
|
||||||
this.exporter.afterPropertiesSet();
|
this.exporter.afterPropertiesSet();
|
||||||
|
this.exporter.afterSingletonsInstantiated();
|
||||||
|
|
||||||
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
|
|
||||||
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint");
|
|
||||||
verify(this.serverContainer).addEndpoint(endpointRegistration);
|
verify(this.serverContainer).addEndpoint(endpointRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addServerEndpointConfigBeanWithServletContextOnly() throws Exception {
|
public void addServerEndpointConfigBeanWithExplicitServletContext() throws Exception {
|
||||||
|
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
|
||||||
|
this.webAppContext.getBeanFactory().registerSingleton("dummyEndpoint", endpointRegistration);
|
||||||
|
|
||||||
this.exporter.setServletContext(this.servletContext);
|
this.exporter.setServletContext(this.servletContext);
|
||||||
|
this.exporter.setApplicationContext(this.webAppContext);
|
||||||
this.exporter.afterPropertiesSet();
|
this.exporter.afterPropertiesSet();
|
||||||
|
this.exporter.afterSingletonsInstantiated();
|
||||||
|
|
||||||
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
|
|
||||||
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint");
|
|
||||||
verify(this.serverContainer).addEndpoint(endpointRegistration);
|
verify(this.serverContainer).addEndpoint(endpointRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addServerEndpointConfigBeanWithServerContainerOnly() throws Exception {
|
public void addServerEndpointConfigBeanWithExplicitServerContainer() throws Exception {
|
||||||
this.exporter.setServerContainer(this.serverContainer);
|
|
||||||
this.exporter.afterPropertiesSet();
|
|
||||||
|
|
||||||
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
|
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
|
||||||
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint");
|
this.webAppContext.getBeanFactory().registerSingleton("dummyEndpoint", endpointRegistration);
|
||||||
|
this.servletContext.removeAttribute("javax.websocket.server.ServerContainer");
|
||||||
|
|
||||||
|
this.exporter.setServerContainer(this.serverContainer);
|
||||||
|
this.exporter.setApplicationContext(this.webAppContext);
|
||||||
|
this.exporter.afterPropertiesSet();
|
||||||
|
this.exporter.afterSingletonsInstantiated();
|
||||||
|
|
||||||
verify(this.serverContainer).addEndpoint(endpointRegistration);
|
verify(this.serverContainer).addEndpoint(endpointRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user