INT-4345: ServerWebSocketContainer: Fix Lifecycle

JIRA: https://jira.spring.io/browse/INT-4345

Resolves https://github.com/spring-projects/spring-integration/issues/2238

The `DefaultHandshakeHandler` doesn't implement `SmartLifecycle`
(just `Lifecycle`) there it doesn't delegate its `start()` to the
`RequestUpgradeStrategy`, e.g. `JettyRequestUpgradeStrategy`.
On the other hand `DefaultHandshakeHandler` can be started as a dependant
`Lifecycle` from some other `SmartLifecycle`, like it happens with the
`WebSocketHandlerMapping`

* Implement `SmartLifecycle` for the `ServerWebSocketContainer` and
delegate its lifecycle to the provided `HandshakeHandler`
* Fix `WebSocketInboundChannelAdapter` to properly implement `doStop()`
with the propagation to the provided `webSocketContainer`
* Fix deprecation warning in the `StompMessageHandlerWebSocketIntegrationTests`

**Cherry-pick to 4.3.x**
This commit is contained in:
Artem Bilan
2017-09-27 17:33:41 -04:00
committed by Gary Russell
parent 077b5ffffc
commit bf1fef39db
4 changed files with 102 additions and 3 deletions

View File

@@ -91,6 +91,7 @@ import org.springframework.web.socket.sockjs.client.WebSocketTransport;
/**
* @author Artem Bilan
* @author Gary Russell
*
* @since 4.2
*/
@ContextConfiguration(classes = StompMessageHandlerWebSocketIntegrationTests.ContextConfiguration.class)
@@ -269,7 +270,7 @@ public class StompMessageHandlerWebSocketIntegrationTests extends LogAdjustingTe
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
registration.interceptors(new ChannelInterceptorAdapter() {
private final AtomicBoolean invoked = new AtomicBoolean();

View File

@@ -18,6 +18,8 @@ package org.springframework.integration.websocket;
import java.util.Arrays;
import org.springframework.context.Lifecycle;
import org.springframework.context.SmartLifecycle;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@@ -46,7 +48,8 @@ import org.springframework.web.socket.sockjs.transport.TransportHandler;
* @author Gary Russell
* @since 4.1
*/
public class ServerWebSocketContainer extends IntegrationWebSocketContainer implements WebSocketConfigurer {
public class ServerWebSocketContainer extends IntegrationWebSocketContainer
implements WebSocketConfigurer, SmartLifecycle {
private final String[] paths;
@@ -60,6 +63,10 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
private String[] origins;
private boolean autoStartup = true;
private int phase = 0;
public ServerWebSocketContainer(String... paths) {
this.paths = paths;
}
@@ -178,6 +185,51 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer impl
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
public void setPhase(int phase) {
this.phase = phase;
}
@Override
public boolean isAutoStartup() {
return this.autoStartup;
}
@Override
public int getPhase() {
return this.phase;
}
@Override
public boolean isRunning() {
return this.handshakeHandler instanceof Lifecycle && ((Lifecycle) this.handshakeHandler).isRunning();
}
@Override
public synchronized void start() {
if (this.handshakeHandler instanceof Lifecycle && !isRunning()) {
((Lifecycle) this.handshakeHandler).start();
}
}
@Override
public void stop() {
if (isRunning()) {
((Lifecycle) this.handshakeHandler).start();
}
}
@Override
public void stop(Runnable callback) {
if (isRunning()) {
((Lifecycle) this.handshakeHandler).stop();
}
callback.run();
}
/**
* @see org.springframework.web.socket.config.annotation.SockJsServiceRegistration
*/

View File

@@ -61,6 +61,7 @@ import org.springframework.web.socket.messaging.SessionConnectedEvent;
/**
* @author Artem Bilan
*
* @since 4.1
*/
public class WebSocketInboundChannelAdapter extends MessageProducerSupport
@@ -255,6 +256,9 @@ public class WebSocketInboundChannelAdapter extends MessageProducerSupport
@Override
protected void doStop() {
this.active = false;
if (this.webSocketContainer instanceof Lifecycle) {
((Lifecycle) this.webSocketContainer).stop();
}
}
private boolean isActive() {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2014-2016 the original author or authors.
* Copyright 2014-2017 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.
@@ -23,6 +23,8 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.nio.ByteBuffer;
import java.util.Collections;
@@ -41,6 +43,7 @@ import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.Lifecycle;
import org.springframework.context.PayloadApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -86,12 +89,16 @@ import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
import org.springframework.web.socket.messaging.StompSubProtocolHandler;
import org.springframework.web.socket.messaging.SubProtocolHandler;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
/**
* @author Artem Bilan
*
* @since 4.1
*/
@ContextConfiguration(classes = WebSocketServerTests.ContextConfiguration.class)
@@ -115,6 +122,9 @@ public class WebSocketServerTests {
@Value("#{server.serverContext.getBean('webSocketEvents')}")
private PollableChannel webSocketEvents;
@Value("#{server.serverContext.getBean('requestUpgradeStrategy')}")
private Lifecycle requestUpgradeStrategy;
@Test
public void testWebSocketOutboundMessageHandler() throws Exception {
StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE);
@@ -151,6 +161,8 @@ public class WebSocketServerTests {
Message<?> event = this.webSocketEvents.receive(10000);
assertNotNull(event);
assertThat(event.getPayload(), instanceOf(WebSocketSession.class));
verify(this.requestUpgradeStrategy).start();
}
@Test
@@ -250,9 +262,20 @@ public class WebSocketServerTests {
return new TestWebSocketHandlerDecoratorFactory();
}
@Bean
public RequestUpgradeStrategy requestUpgradeStrategy() {
return spy(new MyTomcatRequestUpgradeStrategy());
}
@Bean
public DefaultHandshakeHandler handshakeHandler() {
return new DefaultHandshakeHandler(requestUpgradeStrategy());
}
@Bean
public ServerWebSocketContainer serverWebSocketContainer() {
return new ServerWebSocketContainer("/ws")
.setHandshakeHandler(handshakeHandler())
.setDecoratorFactories(testWebSocketHandlerDecoratorFactory())
.setAllowedOrigins("http://foo.com")
.withSockJs();
@@ -353,4 +376,23 @@ public class WebSocketServerTests {
}
public static class MyTomcatRequestUpgradeStrategy extends TomcatRequestUpgradeStrategy implements Lifecycle {
@Override
public void start() {
}
@Override
public void stop() {
}
@Override
public boolean isRunning() {
return true;
}
}
}