Skip to content

Commit 10328f1

Browse files
committed
Reimplemented ServerEndpointExporter to avoid BeanPostProcessor role
Issue: SPR-12340
1 parent 92ad02a commit 10328f1

File tree

3 files changed

+83
-72
lines changed

3 files changed

+83
-72
lines changed

spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServerEndpointExporter.java

+54-60
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.util.Arrays;
2020
import java.util.LinkedHashSet;
2121
import java.util.List;
22+
import java.util.Map;
2223
import java.util.Set;
2324
import javax.servlet.ServletContext;
2425
import javax.websocket.DeploymentException;
@@ -27,7 +28,7 @@
2728
import javax.websocket.server.ServerEndpointConfig;
2829

2930
import org.springframework.beans.factory.InitializingBean;
30-
import org.springframework.beans.factory.config.BeanPostProcessor;
31+
import org.springframework.beans.factory.SmartInitializingSingleton;
3132
import org.springframework.context.ApplicationContext;
3233
import org.springframework.util.Assert;
3334
import org.springframework.web.context.support.WebApplicationObjectSupport;
@@ -41,7 +42,7 @@
4142
*
4243
* <p>When this class is used, by declaring it in Spring configuration, it should be
4344
* possible to turn off a Servlet container's scan for WebSocket endpoints. This can be
44-
* done with the help of the {@code <absolute-ordering>} element in web.xml.
45+
* done with the help of the {@code <absolute-ordering>} element in {@code web.xml}.
4546
*
4647
* @author Rossen Stoyanchev
4748
* @author Juergen Hoeller
@@ -50,19 +51,27 @@
5051
* @see SpringConfigurator
5152
* @see ServletServerContainerFactoryBean
5253
*/
53-
public class ServerEndpointExporter extends WebApplicationObjectSupport implements BeanPostProcessor, InitializingBean {
54-
55-
private ServerContainer serverContainer;
54+
public class ServerEndpointExporter extends WebApplicationObjectSupport
55+
implements InitializingBean, SmartInitializingSingleton {
5656

5757
private List<Class<?>> annotatedEndpointClasses;
5858

59-
private Set<Class<?>> annotatedEndpointBeanTypes;
59+
private ServerContainer serverContainer;
60+
6061

62+
/**
63+
* Explicitly list annotated endpoint types that should be registered on startup. This
64+
* can be done if you wish to turn off a Servlet container's scan for endpoints, which
65+
* goes through all 3rd party jars in the, and rely on Spring configuration instead.
66+
* @param annotatedEndpointClasses {@link ServerEndpoint}-annotated types
67+
*/
68+
public void setAnnotatedEndpointClasses(Class<?>... annotatedEndpointClasses) {
69+
this.annotatedEndpointClasses = Arrays.asList(annotatedEndpointClasses);
70+
}
6171

6272
/**
6373
* Set the JSR-356 {@link ServerContainer} to use for endpoint registration.
6474
* If not set, the container is going to be retrieved via the {@code ServletContext}.
65-
* @since 4.1
6675
*/
6776
public void setServerContainer(ServerContainer serverContainer) {
6877
this.serverContainer = serverContainer;
@@ -75,33 +84,6 @@ protected ServerContainer getServerContainer() {
7584
return this.serverContainer;
7685
}
7786

78-
/**
79-
* Explicitly list annotated endpoint types that should be registered on startup. This
80-
* can be done if you wish to turn off a Servlet container's scan for endpoints, which
81-
* goes through all 3rd party jars in the, and rely on Spring configuration instead.
82-
* @param annotatedEndpointClasses {@link ServerEndpoint}-annotated types
83-
*/
84-
public void setAnnotatedEndpointClasses(Class<?>... annotatedEndpointClasses) {
85-
this.annotatedEndpointClasses = Arrays.asList(annotatedEndpointClasses);
86-
}
87-
88-
@Override
89-
protected void initApplicationContext(ApplicationContext context) {
90-
// Initializes ServletContext given a WebApplicationContext
91-
super.initApplicationContext(context);
92-
93-
// Retrieve beans which are annotated with @ServerEndpoint
94-
this.annotatedEndpointBeanTypes = new LinkedHashSet<Class<?>>();
95-
String[] beanNames = context.getBeanNamesForAnnotation(ServerEndpoint.class);
96-
for (String beanName : beanNames) {
97-
Class<?> beanType = context.getType(beanName);
98-
if (logger.isInfoEnabled()) {
99-
logger.info("Detected @ServerEndpoint bean '" + beanName + "', registering it as an endpoint by type");
100-
}
101-
this.annotatedEndpointBeanTypes.add(beanType);
102-
}
103-
}
104-
10587
@Override
10688
protected void initServletContext(ServletContext servletContext) {
10789
if (this.serverContainer == null) {
@@ -110,64 +92,76 @@ protected void initServletContext(ServletContext servletContext) {
11092
}
11193
}
11294

95+
@Override
96+
protected boolean isContextRequired() {
97+
return false;
98+
}
11399

114100
@Override
115101
public void afterPropertiesSet() {
116102
Assert.state(getServerContainer() != null, "javax.websocket.server.ServerContainer not available");
103+
}
104+
105+
@Override
106+
public void afterSingletonsInstantiated() {
117107
registerEndpoints();
118108
}
119109

110+
120111
/**
121-
* Actually register the endpoints. Called by {@link #afterPropertiesSet()}.
122-
* @since 4.1
112+
* Actually register the endpoints. Called by {@link #afterSingletonsInstantiated()}.
123113
*/
124114
protected void registerEndpoints() {
125115
Set<Class<?>> endpointClasses = new LinkedHashSet<Class<?>>();
126116
if (this.annotatedEndpointClasses != null) {
127117
endpointClasses.addAll(this.annotatedEndpointClasses);
128118
}
129-
if (this.annotatedEndpointBeanTypes != null) {
130-
endpointClasses.addAll(this.annotatedEndpointBeanTypes);
119+
120+
ApplicationContext context = getApplicationContext();
121+
if (context != null) {
122+
String[] endpointNames = context.getBeanNamesForAnnotation(ServerEndpoint.class);
123+
for (String beanName : endpointNames) {
124+
Class<?> beanType = context.getType(beanName);
125+
endpointClasses.add(beanType);
126+
}
131127
}
128+
132129
for (Class<?> endpointClass : endpointClasses) {
133130
registerEndpoint(endpointClass);
134131
}
132+
133+
if (context != null) {
134+
Map<String, ServerEndpointConfig> endpointConfigMap = context.getBeansOfType(ServerEndpointConfig.class);
135+
for (Map.Entry<String, ServerEndpointConfig> configEntry : endpointConfigMap.entrySet()) {
136+
String beanName = configEntry.getKey();
137+
ServerEndpointConfig endpointConfig = configEntry.getValue();
138+
registerEndpoint(endpointConfig);
139+
}
140+
}
135141
}
136142

137143
private void registerEndpoint(Class<?> endpointClass) {
138144
try {
139145
if (logger.isInfoEnabled()) {
140-
logger.info("Registering @ServerEndpoint type: " + endpointClass);
146+
logger.info("Registering @ServerEndpoint class: " + endpointClass);
141147
}
142148
getServerContainer().addEndpoint(endpointClass);
143149
}
144150
catch (DeploymentException ex) {
145-
throw new IllegalStateException("Failed to register @ServerEndpoint type " + endpointClass, ex);
151+
throw new IllegalStateException("Failed to register @ServerEndpoint class: " + endpointClass, ex);
146152
}
147153
}
148154

149-
150-
@Override
151-
public Object postProcessBeforeInitialization(Object bean, String beanName) {
152-
return bean;
153-
}
154-
155-
@Override
156-
public Object postProcessAfterInitialization(Object bean, String beanName) {
157-
if (bean instanceof ServerEndpointConfig) {
158-
ServerEndpointConfig endpointConfig = (ServerEndpointConfig) bean;
159-
try {
160-
if (logger.isInfoEnabled()) {
161-
logger.info("Registering bean '" + beanName +
162-
"' as javax.websocket.Endpoint under path " + endpointConfig.getPath());
163-
}
164-
getServerContainer().addEndpoint(endpointConfig);
165-
}
166-
catch (DeploymentException ex) {
167-
throw new IllegalStateException("Failed to deploy Endpoint bean with name '" + bean + "'", ex);
155+
private void registerEndpoint(ServerEndpointConfig endpointConfig) {
156+
try {
157+
if (logger.isInfoEnabled()) {
158+
logger.info("Registering ServerEndpointConfig: " + endpointConfig);
168159
}
160+
getServerContainer().addEndpoint(endpointConfig);
161+
}
162+
catch (DeploymentException ex) {
163+
throw new IllegalStateException("Failed to register ServerEndpointConfig: " + endpointConfig, ex);
169164
}
170-
return bean;
171165
}
172166

173167
}

spring-websocket/src/main/java/org/springframework/web/socket/server/standard/ServerEndpointRegistration.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public class ServerEndpointRegistration extends ServerEndpointConfig.Configurato
7373

7474
/**
7575
* Create a new {@link ServerEndpointRegistration} instance from an
76-
* {@code javax.webscoket.Endpoint} class.
76+
* {@code javax.websocket.Endpoint} class.
7777
* @param path the endpoint path
7878
* @param endpointClass the endpoint class
7979
*/
@@ -202,4 +202,9 @@ public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<E
202202
return super.getNegotiatedExtensions(installed, requested);
203203
}
204204

205+
206+
@Override
207+
public String toString() {
208+
return "ServerEndpointRegistration for path '" + getPath() + "': " + getEndpointClass();
209+
}
205210
}

spring-websocket/src/test/java/org/springframework/web/socket/server/standard/ServerEndpointExporterTests.java

+23-11
Original file line numberDiff line numberDiff line change
@@ -67,62 +67,74 @@ public void setup() {
6767

6868

6969
@Test
70-
public void addAnnotatedEndpointBeans() throws Exception {
70+
public void addAnnotatedEndpointClasses() throws Exception {
7171
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class);
7272
this.exporter.setApplicationContext(this.webAppContext);
7373
this.exporter.afterPropertiesSet();
74+
this.exporter.afterSingletonsInstantiated();
7475

7576
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
7677
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
7778
}
7879

7980
@Test
80-
public void addAnnotatedEndpointBeansWithServletContextOnly() throws Exception {
81+
public void addAnnotatedEndpointClassesWithServletContextOnly() throws Exception {
8182
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class, AnnotatedDummyEndpointBean.class);
8283
this.exporter.setServletContext(this.servletContext);
8384
this.exporter.afterPropertiesSet();
85+
this.exporter.afterSingletonsInstantiated();
8486

8587
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
8688
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
8789
}
8890

8991
@Test
90-
public void addAnnotatedEndpointBeansWithServerContainerOnly() throws Exception {
92+
public void addAnnotatedEndpointClassesWithExplicitServerContainerOnly() throws Exception {
9193
this.exporter.setAnnotatedEndpointClasses(AnnotatedDummyEndpoint.class, AnnotatedDummyEndpointBean.class);
9294
this.exporter.setServerContainer(this.serverContainer);
9395
this.exporter.afterPropertiesSet();
96+
this.exporter.afterSingletonsInstantiated();
9497

9598
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpoint.class);
9699
verify(this.serverContainer).addEndpoint(AnnotatedDummyEndpointBean.class);
97100
}
98101

99102
@Test
100103
public void addServerEndpointConfigBean() throws Exception {
104+
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
105+
this.webAppContext.getBeanFactory().registerSingleton("dummyEndpoint", endpointRegistration);
106+
101107
this.exporter.setApplicationContext(this.webAppContext);
102108
this.exporter.afterPropertiesSet();
109+
this.exporter.afterSingletonsInstantiated();
103110

104-
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
105-
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint");
106111
verify(this.serverContainer).addEndpoint(endpointRegistration);
107112
}
108113

109114
@Test
110-
public void addServerEndpointConfigBeanWithServletContextOnly() throws Exception {
115+
public void addServerEndpointConfigBeanWithExplicitServletContext() throws Exception {
116+
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
117+
this.webAppContext.getBeanFactory().registerSingleton("dummyEndpoint", endpointRegistration);
118+
111119
this.exporter.setServletContext(this.servletContext);
120+
this.exporter.setApplicationContext(this.webAppContext);
112121
this.exporter.afterPropertiesSet();
122+
this.exporter.afterSingletonsInstantiated();
113123

114-
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
115-
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint");
116124
verify(this.serverContainer).addEndpoint(endpointRegistration);
117125
}
118126

119127
@Test
120-
public void addServerEndpointConfigBeanWithServerContainerOnly() throws Exception {
128+
public void addServerEndpointConfigBeanWithExplicitServerContainer() throws Exception {
129+
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
130+
this.webAppContext.getBeanFactory().registerSingleton("dummyEndpoint", endpointRegistration);
131+
this.servletContext.removeAttribute("javax.websocket.server.ServerContainer");
132+
121133
this.exporter.setServerContainer(this.serverContainer);
134+
this.exporter.setApplicationContext(this.webAppContext);
122135
this.exporter.afterPropertiesSet();
136+
this.exporter.afterSingletonsInstantiated();
123137

124-
ServerEndpointRegistration endpointRegistration = new ServerEndpointRegistration("/dummy", new DummyEndpoint());
125-
this.exporter.postProcessAfterInitialization(endpointRegistration, "dummyEndpoint");
126138
verify(this.serverContainer).addEndpoint(endpointRegistration);
127139
}
128140

0 commit comments

Comments
 (0)