@Nullable all the way: null-safety at field level

This commits extends nullability declarations to the field level, formalizing the interaction between methods and their underlying fields and therefore avoiding any nullability mismatch.

Issue: SPR-15720
This commit is contained in:
Juergen Hoeller
2017-06-30 01:53:45 +02:00
parent c4694c3f5c
commit cc74a2891a
936 changed files with 6090 additions and 2806 deletions

View File

@@ -145,6 +145,7 @@ public final class CloseStatus {
private final int code;
@Nullable
private final String reason;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-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.
@@ -18,6 +18,8 @@ package org.springframework.web.socket;
import java.nio.charset.StandardCharsets;
import org.springframework.lang.Nullable;
/**
* A text WebSocket message.
*
@@ -26,6 +28,7 @@ import java.nio.charset.StandardCharsets;
*/
public final class TextMessage extends AbstractWebSocketMessage<String> {
@Nullable
private final byte[] bytes;

View File

@@ -79,6 +79,7 @@ public interface WebSocketSession extends Closeable {
/**
* Return the address of the remote client.
*/
@Nullable
InetSocketAddress getRemoteAddress();
/**

View File

@@ -45,6 +45,7 @@ public abstract class AbstractWebSocketSession<T> implements NativeWebSocketSess
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
@Nullable
private T nativeSession;
@@ -67,6 +68,7 @@ public abstract class AbstractWebSocketSession<T> implements NativeWebSocketSess
@Override
public T getNativeSession() {
Assert.state(this.nativeSession != null, "WebSocket session not yet initialized");
return this.nativeSession;
}

View File

@@ -32,6 +32,7 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.socket.BinaryMessage;
@@ -54,16 +55,22 @@ import org.springframework.web.socket.adapter.AbstractWebSocketSession;
*/
public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
@Nullable
private String id;
@Nullable
private URI uri;
@Nullable
private HttpHeaders headers;
@Nullable
private String acceptedProtocol;
@Nullable
private List<WebSocketExtension> extensions;
@Nullable
private Principal user;
@@ -90,19 +97,19 @@ public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
@Override
public String getId() {
checkNativeSessionInitialized();
Assert.state(this.id != null, "WebSocket session is not yet initialized");
return this.id;
}
@Override
public URI getUri() {
checkNativeSessionInitialized();
Assert.state(this.uri != null, "WebSocket session is not yet initialized");
return this.uri;
}
@Override
public HttpHeaders getHandshakeHeaders() {
checkNativeSessionInitialized();
Assert.state(this.headers != null, "WebSocket session is not yet initialized");
return this.headers;
}
@@ -114,7 +121,7 @@ public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
@Override
public List<WebSocketExtension> getExtensions() {
checkNativeSessionInitialized();
Assert.state(this.extensions != null, "WebSocket session is not yet initialized");
return this.extensions;
}

View File

@@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.websocket.CloseReason;
import javax.websocket.CloseReason.CloseCodes;
import javax.websocket.Extension;
@@ -32,6 +31,7 @@ import javax.websocket.Session;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
@@ -50,20 +50,27 @@ import org.springframework.web.socket.adapter.AbstractWebSocketSession;
*/
public class StandardWebSocketSession extends AbstractWebSocketSession<Session> {
@Nullable
private String id;
@Nullable
private URI uri;
private final HttpHeaders handshakeHeaders;
@Nullable
private String acceptedProtocol;
@Nullable
private List<WebSocketExtension> extensions;
@Nullable
private Principal user;
@Nullable
private final InetSocketAddress localAddress;
@Nullable
private final InetSocketAddress remoteAddress;
@@ -88,7 +95,7 @@ public class StandardWebSocketSession extends AbstractWebSocketSession<Session>
* @param localAddress the address on which the request was received
* @param remoteAddress the address of the remote client
* @param user the user associated with the session; if {@code null} we'll
* fallback on the user available in the underlying WebSocket session
* fallback on the user available in the underlying WebSocket session
*/
public StandardWebSocketSession(@Nullable HttpHeaders headers, @Nullable Map<String, Object> attributes,
@Nullable InetSocketAddress localAddress, @Nullable InetSocketAddress remoteAddress,
@@ -105,13 +112,13 @@ public class StandardWebSocketSession extends AbstractWebSocketSession<Session>
@Override
public String getId() {
checkNativeSessionInitialized();
Assert.state(this.id != null, "WebSocket session is not yet initialized");
return this.id;
}
@Override
public URI getUri() {
checkNativeSessionInitialized();
Assert.state(this.uri != null, "WebSocket session is not yet initialized");
return this.uri;
}
@@ -128,7 +135,7 @@ public class StandardWebSocketSession extends AbstractWebSocketSession<Session>
@Override
public List<WebSocketExtension> getExtensions() {
checkNativeSessionInitialized();
Assert.state(this.extensions != null, "WebSocket session is not yet initialized");
return this.extensions;
}

View File

@@ -43,6 +43,7 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
private final WebSocketHandler webSocketHandler;
@Nullable
private WebSocketSession webSocketSession;
private WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
@@ -140,7 +141,7 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
future.addCallback(new ListenableFutureCallback<WebSocketSession>() {
@Override
public void onSuccess(WebSocketSession result) {
public void onSuccess(@Nullable WebSocketSession result) {
webSocketSession = result;
logger.info("Successfully connected");
}
@@ -153,7 +154,9 @@ public class WebSocketConnectionManager extends ConnectionManagerSupport {
@Override
protected void closeConnection() throws Exception {
this.webSocketSession.close();
if (this.webSocketSession != null) {
this.webSocketSession.close();
}
}
@Override

View File

@@ -63,6 +63,7 @@ public class JettyWebSocketClient extends AbstractWebSocketClient implements Lif
private final Object lifecycleMonitor = new Object();
@Nullable
private AsyncListenableTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
@@ -85,9 +86,8 @@ public class JettyWebSocketClient extends AbstractWebSocketClient implements Lif
/**
* Set an {@link AsyncListenableTaskExecutor} to use when opening connections.
* If this property is set to {@code null}, calls to any of the
* If this property is set to {@code null}, calls to any of the
* {@code doHandshake} methods will block until the connection is established.
*
* <p>By default an instance of {@code SimpleAsyncTaskExecutor} is used.
*/
public void setTaskExecutor(@Nullable AsyncListenableTaskExecutor taskExecutor) {
@@ -97,6 +97,7 @@ public class JettyWebSocketClient extends AbstractWebSocketClient implements Lif
/**
* Return the configured {@link TaskExecutor}.
*/
@Nullable
public AsyncListenableTaskExecutor getTaskExecutor() {
return this.taskExecutor;
}

View File

@@ -25,6 +25,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.socket.client.ConnectionManagerSupport;
import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
@@ -41,27 +42,30 @@ import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
*/
public class AnnotatedEndpointConnectionManager extends ConnectionManagerSupport implements BeanFactoryAware {
@Nullable
private final Object endpoint;
@Nullable
private final BeanCreatingHandlerProvider<Object> endpointProvider;
private WebSocketContainer webSocketContainer = ContainerProvider.getWebSocketContainer();
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("AnnotatedEndpointConnectionManager-");
@Nullable
private volatile Session session;
public AnnotatedEndpointConnectionManager(Object endpoint, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
this.endpointProvider = null;
this.endpoint = endpoint;
this.endpointProvider = null;
}
public AnnotatedEndpointConnectionManager(Class<?> endpointClass, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
this.endpointProvider = new BeanCreatingHandlerProvider<>(endpointClass);
this.endpoint = null;
this.endpointProvider = new BeanCreatingHandlerProvider<>(endpointClass);
}
@@ -104,7 +108,11 @@ public class AnnotatedEndpointConnectionManager extends ConnectionManagerSupport
if (logger.isInfoEnabled()) {
logger.info("Connecting to WebSocket at " + getUri());
}
Object endpointToUse = (endpoint != null) ? endpoint : endpointProvider.getHandler();
Object endpointToUse = endpoint;
if (endpointToUse == null) {
Assert.state(endpointProvider != null, "No endpoint set");
endpointToUse = endpointProvider.getHandler();
}
session = webSocketContainer.connectToServer(endpointToUse, getUri());
logger.info("Successfully connected to WebSocket");
}
@@ -117,8 +125,9 @@ public class AnnotatedEndpointConnectionManager extends ConnectionManagerSupport
@Override
protected void closeConnection() throws Exception {
try {
if (isConnected()) {
this.session.close();
Session session = this.session;
if (session != null && session.isOpen()) {
session.close();
}
}
finally {
@@ -128,7 +137,8 @@ public class AnnotatedEndpointConnectionManager extends ConnectionManagerSupport
@Override
protected boolean isConnected() {
return (this.session != null && this.session.isOpen());
Session session = this.session;
return (session != null && session.isOpen());
}
}

View File

@@ -32,6 +32,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.socket.client.ConnectionManagerSupport;
import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
@@ -48,8 +49,10 @@ import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
*/
public class EndpointConnectionManager extends ConnectionManagerSupport implements BeanFactoryAware {
@Nullable
private final Endpoint endpoint;
@Nullable
private final BeanCreatingHandlerProvider<Endpoint> endpointProvider;
private final ClientEndpointConfig.Builder configBuilder = ClientEndpointConfig.Builder.create();
@@ -58,21 +61,22 @@ public class EndpointConnectionManager extends ConnectionManagerSupport implemen
private TaskExecutor taskExecutor = new SimpleAsyncTaskExecutor("EndpointConnectionManager-");
@Nullable
private volatile Session session;
public EndpointConnectionManager(Endpoint endpoint, String uriTemplate, Object... uriVariables) {
super(uriTemplate, uriVariables);
Assert.notNull(endpoint, "endpoint must not be null");
this.endpointProvider = null;
this.endpoint = endpoint;
this.endpointProvider = null;
}
public EndpointConnectionManager(Class<? extends Endpoint> endpointClass, String uriTemplate, Object... uriVars) {
super(uriTemplate, uriVars);
Assert.notNull(endpointClass, "endpointClass must not be null");
this.endpointProvider = new BeanCreatingHandlerProvider<>(endpointClass);
this.endpoint = null;
this.endpointProvider = new BeanCreatingHandlerProvider<>(endpointClass);
}
@@ -135,7 +139,11 @@ public class EndpointConnectionManager extends ConnectionManagerSupport implemen
if (logger.isInfoEnabled()) {
logger.info("Connecting to WebSocket at " + getUri());
}
Endpoint endpointToUse = (endpoint != null) ? endpoint : endpointProvider.getHandler();
Endpoint endpointToUse = endpoint;
if (endpointToUse == null) {
Assert.state(endpointProvider != null, "No endpoint set");
endpointToUse = endpointProvider.getHandler();
}
ClientEndpointConfig endpointConfig = configBuilder.build();
session = getWebSocketContainer().connectToServer(endpointToUse, endpointConfig, getUri());
logger.info("Successfully connected to WebSocket");
@@ -149,8 +157,9 @@ public class EndpointConnectionManager extends ConnectionManagerSupport implemen
@Override
protected void closeConnection() throws Exception {
try {
if (isConnected()) {
this.session.close();
Session session = this.session;
if (session != null && session.isOpen()) {
session.close();
}
}
finally {
@@ -160,7 +169,8 @@ public class EndpointConnectionManager extends ConnectionManagerSupport implemen
@Override
protected boolean isConnected() {
return (this.session != null && this.session.isOpen());
Session session = this.session;
return (session != null && session.isOpen());
}
}

View File

@@ -62,6 +62,7 @@ public class StandardWebSocketClient extends AbstractWebSocketClient {
private final Map<String,Object> userProperties = new HashMap<>();
@Nullable
private AsyncListenableTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
@@ -107,7 +108,7 @@ public class StandardWebSocketClient extends AbstractWebSocketClient {
/**
* Set an {@link AsyncListenableTaskExecutor} to use when opening connections.
* If this property is set to {@code null}, calls to any of the
* If this property is set to {@code null}, calls to any of the
* {@code doHandshake} methods will block until the connection is established.
* <p>By default, an instance of {@code SimpleAsyncTaskExecutor} is used.
*/
@@ -118,6 +119,7 @@ public class StandardWebSocketClient extends AbstractWebSocketClient {
/**
* Return the configured {@link TaskExecutor}.
*/
@Nullable
public AsyncListenableTaskExecutor getTaskExecutor() {
return this.taskExecutor;
}

View File

@@ -54,18 +54,25 @@ public class WebSocketMessageBrokerStats {
private static final Log logger = LogFactory.getLog(WebSocketMessageBrokerStats.class);
@Nullable
private SubProtocolWebSocketHandler webSocketHandler;
@Nullable
private StompSubProtocolHandler stompSubProtocolHandler;
@Nullable
private StompBrokerRelayMessageHandler stompBrokerRelay;
@Nullable
private ThreadPoolExecutor inboundChannelExecutor;
@Nullable
private ThreadPoolExecutor outboundChannelExecutor;
@Nullable
private ScheduledThreadPoolExecutor sockJsTaskScheduler;
@Nullable
private ScheduledFuture<?> loggingTask;
private long loggingPeriod = 30 * 60 * 1000;
@@ -78,6 +85,9 @@ public class WebSocketMessageBrokerStats {
@Nullable
private StompSubProtocolHandler initStompSubProtocolHandler() {
if (this.webSocketHandler == null) {
return null;
}
for (SubProtocolHandler handler : this.webSocketHandler.getProtocolHandlers()) {
if (handler instanceof StompSubProtocolHandler) {
return (StompSubProtocolHandler) handler;
@@ -104,12 +114,12 @@ public class WebSocketMessageBrokerStats {
public void setSockJsTaskScheduler(ThreadPoolTaskScheduler sockJsTaskScheduler) {
this.sockJsTaskScheduler = sockJsTaskScheduler.getScheduledThreadPoolExecutor();
this.loggingTask = initLoggingTask(1 * 60 * 1000);
this.loggingTask = initLoggingTask(60 * 1000);
}
@Nullable
private ScheduledFuture<?> initLoggingTask(long initialDelay) {
if (logger.isInfoEnabled() && this.loggingPeriod > 0) {
if (this.sockJsTaskScheduler != null && this.loggingPeriod > 0 && logger.isInfoEnabled()) {
return this.sockJsTaskScheduler.scheduleAtFixedRate(() ->
logger.info(WebSocketMessageBrokerStats.this.toString()),
initialDelay, this.loggingPeriod, TimeUnit.MILLISECONDS);

View File

@@ -46,14 +46,17 @@ public abstract class AbstractWebSocketHandlerRegistration<M> implements WebSock
private final MultiValueMap<WebSocketHandler, String> handlerMap = new LinkedMultiValueMap<>();
@Nullable
private HandshakeHandler handshakeHandler;
private final List<HandshakeInterceptor> interceptors = new ArrayList<>();
private final List<String> allowedOrigins = new ArrayList<>();
@Nullable
private SockJsServiceRegistration sockJsServiceRegistration;
@Nullable
private TaskScheduler scheduler;
@@ -86,6 +89,7 @@ public abstract class AbstractWebSocketHandlerRegistration<M> implements WebSock
return this;
}
@Nullable
protected HandshakeHandler getHandshakeHandler() {
return this.handshakeHandler;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -21,6 +21,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.MultiValueMap;
@@ -42,20 +43,20 @@ public class ServletWebSocketHandlerRegistry implements WebSocketHandlerRegistry
private final List<ServletWebSocketHandlerRegistration> registrations = new ArrayList<>(4);
@Nullable
private TaskScheduler scheduler;
private int order = 1;
@Nullable
private UrlPathHelper urlPathHelper;
public ServletWebSocketHandlerRegistry() {
this.scheduler = null;
}
/**
* Deprecated constructor with a TaskScheduler for SockJS use.
*
* @deprecated as of 5.0 a TaskScheduler is not provided upfront, not until
* it is obvious that it is needed, see {@link #requiresTaskScheduler()} and
* {@link #setTaskScheduler}.
@@ -95,6 +96,7 @@ public class ServletWebSocketHandlerRegistry implements WebSocketHandlerRegistry
this.urlPathHelper = urlPathHelper;
}
@Nullable
public UrlPathHelper getUrlPathHelper() {
return this.urlPathHelper;
}
@@ -142,7 +144,7 @@ public class ServletWebSocketHandlerRegistry implements WebSocketHandlerRegistry
private void updateTaskScheduler(ServletWebSocketHandlerRegistration registration) {
SockJsServiceRegistration sockJsRegistration = registration.getSockJsServiceRegistration();
if (sockJsRegistration != null && sockJsRegistration.getTaskScheduler() == null) {
if (sockJsRegistration != null && this.scheduler != null && sockJsRegistration.getTaskScheduler() == null) {
sockJsRegistration.setTaskScheduler(this.scheduler);
}
}

View File

@@ -41,20 +41,28 @@ import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsServ
*/
public class SockJsServiceRegistration {
@Nullable
private TaskScheduler scheduler;
@Nullable
private String clientLibraryUrl;
@Nullable
private Integer streamBytesLimit;
@Nullable
private Boolean sessionCookieNeeded;
@Nullable
private Long heartbeatTime;
@Nullable
private Long disconnectDelay;
@Nullable
private Integer httpMessageCacheSize;
@Nullable
private Boolean webSocketEnabled;
private final List<TransportHandler> transportHandlers = new ArrayList<>();
@@ -65,8 +73,10 @@ public class SockJsServiceRegistration {
private final List<String> allowedOrigins = new ArrayList<>();
@Nullable
private Boolean suppressCors;
@Nullable
private SockJsMessageCodec messageCodec;
@@ -75,7 +85,6 @@ public class SockJsServiceRegistration {
/**
* Deprecated constructor with a TaskScheduler.
*
* @deprecated as of 5.0 a TaskScheduler is not provided upfront, not until
* it is obvious that it is needed; call {@link #getTaskScheduler()} to check
* and then {@link #setTaskScheduler(TaskScheduler)} to set it before a call
@@ -91,7 +100,7 @@ public class SockJsServiceRegistration {
* A scheduler instance to use for scheduling SockJS heart-beats.
*/
public SockJsServiceRegistration setTaskScheduler(TaskScheduler scheduler) {
Assert.notNull(scheduler, "TaskScheduler is required.");
Assert.notNull(scheduler, "TaskScheduler is required");
this.scheduler = scheduler;
return this;
}
@@ -302,9 +311,9 @@ public class SockJsServiceRegistration {
}
private TransportHandlingSockJsService createSockJsService() {
Assert.state(this.scheduler != null, "No TaskScheduler available");
Assert.state(this.transportHandlers.isEmpty() || this.transportHandlerOverrides.isEmpty(),
"Specify either TransportHandlers or TransportHandler overrides, not both");
return (!this.transportHandlers.isEmpty() ?
new TransportHandlingSockJsService(this.scheduler, this.transportHandlers) :
new DefaultSockJsService(this.scheduler, this.transportHandlerOverrides));

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
import org.springframework.util.MultiValueMap;
@@ -51,6 +52,7 @@ public class WebMvcStompEndpointRegistry implements StompEndpointRegistry {
private int order = 1;
@Nullable
private UrlPathHelper urlPathHelper;
private final SubProtocolWebSocketHandler subProtocolWebSocketHandler;
@@ -127,6 +129,7 @@ public class WebMvcStompEndpointRegistry implements StompEndpointRegistry {
this.urlPathHelper = urlPathHelper;
}
@Nullable
protected UrlPathHelper getUrlPathHelper() {
return this.urlPathHelper;
}

View File

@@ -20,6 +20,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
@@ -35,7 +36,6 @@ import org.springframework.web.socket.sockjs.SockJsService;
import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
import org.springframework.web.socket.sockjs.transport.handler.WebSocketTransportHandler;
/**
* An abstract base class for configuring STOMP over WebSocket/SockJS endpoints.
*
@@ -50,12 +50,14 @@ public class WebMvcStompWebSocketEndpointRegistration implements StompWebSocketE
private final TaskScheduler sockJsTaskScheduler;
@Nullable
private HandshakeHandler handshakeHandler;
private final List<HandshakeInterceptor> interceptors = new ArrayList<>();
private final List<String> allowedOrigins = new ArrayList<>();
@Nullable
private SockJsServiceRegistration registration;

View File

@@ -20,6 +20,7 @@ import java.util.Date;
import java.util.concurrent.ScheduledFuture;
import org.springframework.context.annotation.Bean;
import org.springframework.lang.Nullable;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@@ -33,8 +34,10 @@ import org.springframework.web.servlet.HandlerMapping;
*/
public class WebSocketConfigurationSupport {
@Nullable
private ServletWebSocketHandlerRegistry handlerRegistry;
@Nullable
private TaskScheduler scheduler;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -17,8 +17,10 @@
package org.springframework.web.socket.config.annotation;
import org.springframework.beans.factory.config.CustomScopeConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.lang.Nullable;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.SimpSessionScope;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
@@ -48,6 +50,7 @@ import org.springframework.web.socket.messaging.WebSocketAnnotationMethodMessage
*/
public abstract class WebSocketMessageBrokerConfigurationSupport extends AbstractMessageBrokerConfiguration {
@Nullable
private WebSocketTransportRegistration transportRegistration;
@@ -67,7 +70,10 @@ public abstract class WebSocketMessageBrokerConfigurationSupport extends Abstrac
WebSocketHandler handler = decorateWebSocketHandler(subProtocolWebSocketHandler());
WebMvcStompEndpointRegistry registry = new WebMvcStompEndpointRegistry(handler,
getTransportRegistration(), messageBrokerTaskScheduler());
registry.setApplicationContext(getApplicationContext());
ApplicationContext applicationContext = getApplicationContext();
if (applicationContext != null) {
registry.setApplicationContext(applicationContext);
}
registerStompEndpoints(registry);
return registry.getHandlerMapping();
}
@@ -126,8 +132,12 @@ public abstract class WebSocketMessageBrokerConfigurationSupport extends Abstrac
protected MappingJackson2MessageConverter createJacksonConverter() {
MappingJackson2MessageConverter messageConverter = super.createJacksonConverter();
// Use Jackson builder in order to have JSR-310 and Joda-Time modules registered automatically
messageConverter.setObjectMapper(Jackson2ObjectMapperBuilder.json()
.applicationContext(this.getApplicationContext()).build());
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
ApplicationContext applicationContext = getApplicationContext();
if (applicationContext != null) {
builder.applicationContext(applicationContext);
}
messageConverter.setObjectMapper(builder.build());
return messageConverter;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -20,6 +20,7 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@@ -34,6 +35,7 @@ public class BeanCreatingHandlerProvider<T> implements BeanFactoryAware {
private final Class<? extends T> handlerType;
@Nullable
private AutowireCapableBeanFactory beanFactory;

View File

@@ -35,6 +35,7 @@ public abstract class AbstractSubProtocolEvent extends ApplicationEvent {
private final Message<byte[]> message;
@Nullable
private final Principal user;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -89,8 +89,7 @@ public class SessionDisconnectEvent extends AbstractSubProtocolEvent {
@Override
public String toString() {
return "SessionDisconnectEvent[sessionId=" + this.sessionId + ", " +
(this.status != null ? this.status.toString() : "closeStatus=null") + "]";
return "SessionDisconnectEvent[sessionId=" + this.sessionId + ", " + this.status.toString() + "]";
}
}

View File

@@ -90,6 +90,7 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
private static final byte[] EMPTY_PAYLOAD = new byte[0];
@Nullable
private StompSubProtocolErrorHandler errorHandler;
private int messageSizeLimit = 64 * 1024;
@@ -100,12 +101,15 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
private final Map<String, BufferingStompDecoder> decoders = new ConcurrentHashMap<>();
@Nullable
private MessageHeaderInitializer headerInitializer;
private final Map<String, Principal> stompAuthentications = new ConcurrentHashMap<>();
@Nullable
private Boolean immutableMessageInterceptorPresent;
@Nullable
private ApplicationEventPublisher eventPublisher;
private final Stats stats = new Stats();
@@ -289,13 +293,13 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
}
if (this.eventPublisher != null) {
if (isConnect) {
publishEvent(new SessionConnectEvent(this, message, user));
publishEvent(this.eventPublisher, new SessionConnectEvent(this, message, user));
}
else if (StompCommand.SUBSCRIBE.equals(headerAccessor.getCommand())) {
publishEvent(new SessionSubscribeEvent(this, message, user));
publishEvent(this.eventPublisher, new SessionSubscribeEvent(this, message, user));
}
else if (StompCommand.UNSUBSCRIBE.equals(headerAccessor.getCommand())) {
publishEvent(new SessionUnsubscribeEvent(this, message, user));
publishEvent(this.eventPublisher, new SessionUnsubscribeEvent(this, message, user));
}
}
}
@@ -372,9 +376,9 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
return false;
}
private void publishEvent(ApplicationEvent event) {
private void publishEvent(ApplicationEventPublisher publisher, ApplicationEvent event) {
try {
this.eventPublisher.publishEvent(event);
publisher.publishEvent(event);
}
catch (Throwable ex) {
if (logger.isErrorEnabled()) {
@@ -418,7 +422,7 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
SimpAttributes simpAttributes = new SimpAttributes(session.getId(), session.getAttributes());
SimpAttributesContextHolder.setAttributes(simpAttributes);
Principal user = getUser(session);
publishEvent(new SessionConnectedEvent(this, (Message<byte[]>) message, user));
publishEvent(this.eventPublisher, new SessionConnectedEvent(this, (Message<byte[]>) message, user));
}
finally {
SimpAttributesContextHolder.resetAttributes();
@@ -604,7 +608,7 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
SimpAttributesContextHolder.setAttributes(simpAttributes);
if (this.eventPublisher != null) {
Principal user = getUser(session);
publishEvent(new SessionDisconnectEvent(this, message, session.getId(), closeStatus, user));
publishEvent(this.eventPublisher, new SessionDisconnectEvent(this, message, session.getId(), closeStatus, user));
}
outputChannel.send(message);
}

View File

@@ -89,6 +89,7 @@ public class SubProtocolWebSocketHandler
private final Set<SubProtocolHandler> protocolHandlers = new LinkedHashSet<>();
@Nullable
private SubProtocolHandler defaultProtocolHandler;
private final Map<String, WebSocketSessionHolder> sessions = new ConcurrentHashMap<>();

View File

@@ -59,6 +59,7 @@ public abstract class AbstractStandardUpgradeStrategy implements RequestUpgradeS
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private volatile List<WebSocketExtension> extensions;
@@ -84,11 +85,13 @@ public abstract class AbstractStandardUpgradeStrategy implements RequestUpgradeS
@Override
public List<WebSocketExtension> getSupportedExtensions(ServerHttpRequest request) {
if (this.extensions == null) {
List<WebSocketExtension> extensions = this.extensions;
if (extensions == null) {
HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
this.extensions = getInstalledExtensions(getContainer(servletRequest));
extensions = getInstalledExtensions(getContainer(servletRequest));
this.extensions = extensions;
}
return this.extensions;
return extensions;
}
protected List<WebSocketExtension> getInstalledExtensions(WebSocketContainer container) {

View File

@@ -55,8 +55,10 @@ import org.springframework.web.context.support.WebApplicationObjectSupport;
public class ServerEndpointExporter extends WebApplicationObjectSupport
implements InitializingBean, SmartInitializingSingleton {
@Nullable
private List<Class<?>> annotatedEndpointClasses;
@Nullable
private ServerContainer serverContainer;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -30,6 +30,7 @@ import javax.websocket.server.ServerEndpointConfig;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.socket.handler.BeanCreatingHandlerProvider;
@@ -56,10 +57,12 @@ public class ServerEndpointRegistration extends ServerEndpointConfig.Configurato
private final String path;
private final BeanCreatingHandlerProvider<Endpoint> endpointProvider;
@Nullable
private final Endpoint endpoint;
@Nullable
private final BeanCreatingHandlerProvider<Endpoint> endpointProvider;
private List<Class<? extends Encoder>> encoders = new ArrayList<>();
private List<Class<? extends Decoder>> decoders = new ArrayList<>();
@@ -71,20 +74,6 @@ public class ServerEndpointRegistration extends ServerEndpointConfig.Configurato
private final Map<String, Object> userProperties = new HashMap<>();
/**
* Create a new {@link ServerEndpointRegistration} instance from an
* {@code javax.websocket.Endpoint} class.
* @param path the endpoint path
* @param endpointClass the endpoint class
*/
public ServerEndpointRegistration(String path, Class<? extends Endpoint> endpointClass) {
Assert.hasText(path, "path must not be empty");
Assert.notNull(endpointClass, "endpointClass must not be null");
this.path = path;
this.endpointProvider = new BeanCreatingHandlerProvider<>(endpointClass);
this.endpoint = null;
}
/**
* Create a new {@link ServerEndpointRegistration} instance from an
* {@code javax.websocket.Endpoint} instance.
@@ -95,8 +84,22 @@ public class ServerEndpointRegistration extends ServerEndpointConfig.Configurato
Assert.hasText(path, "path must not be empty");
Assert.notNull(endpoint, "endpoint must not be null");
this.path = path;
this.endpointProvider = null;
this.endpoint = endpoint;
this.endpointProvider = null;
}
/**
* Create a new {@link ServerEndpointRegistration} instance from an
* {@code javax.websocket.Endpoint} class.
* @param path the endpoint path
* @param endpointClass the endpoint class
*/
public ServerEndpointRegistration(String path, Class<? extends Endpoint> endpointClass) {
Assert.hasText(path, "path must not be empty");
Assert.notNull(endpointClass, "endpointClass must not be null");
this.path = path;
this.endpoint = null;
this.endpointProvider = new BeanCreatingHandlerProvider<>(endpointClass);
}
@@ -107,11 +110,23 @@ public class ServerEndpointRegistration extends ServerEndpointConfig.Configurato
@Override
public Class<? extends Endpoint> getEndpointClass() {
return (this.endpoint != null ? this.endpoint.getClass() : this.endpointProvider.getHandlerType());
if (this.endpoint != null) {
return this.endpoint.getClass();
}
else {
Assert.state(this.endpointProvider != null, "No endpoint set");
return this.endpointProvider.getHandlerType();
}
}
public Endpoint getEndpoint() {
return (this.endpoint != null) ? this.endpoint : this.endpointProvider.getHandler();
if (this.endpoint != null) {
return this.endpoint;
}
else {
Assert.state(this.endpointProvider != null, "No endpoint set");
return this.endpointProvider.getHandler();
}
}
public void setSubprotocols(List<String> protocols) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -22,6 +22,7 @@ import javax.websocket.server.ServerContainer;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.context.ServletContextAware;
@@ -46,16 +47,22 @@ import org.springframework.web.context.ServletContextAware;
public class ServletServerContainerFactoryBean
implements FactoryBean<WebSocketContainer>, ServletContextAware, InitializingBean {
@Nullable
private Long asyncSendTimeout;
@Nullable
private Long maxSessionIdleTimeout;
@Nullable
private Integer maxTextMessageBufferSize;
@Nullable
private Integer maxBinaryMessageBufferSize;
@Nullable
private ServletContext servletContext;
@Nullable
private ServerContainer serverContainer;
@@ -63,7 +70,8 @@ public class ServletServerContainerFactoryBean
this.asyncSendTimeout = timeoutInMillis;
}
public long getAsyncSendTimeout() {
@Nullable
public Long getAsyncSendTimeout() {
return this.asyncSendTimeout;
}
@@ -71,6 +79,7 @@ public class ServletServerContainerFactoryBean
this.maxSessionIdleTimeout = timeoutInMillis;
}
@Nullable
public Long getMaxSessionIdleTimeout() {
return this.maxSessionIdleTimeout;
}
@@ -79,6 +88,7 @@ public class ServletServerContainerFactoryBean
this.maxTextMessageBufferSize = bufferSize;
}
@Nullable
public Integer getMaxTextMessageBufferSize() {
return this.maxTextMessageBufferSize;
}
@@ -87,6 +97,7 @@ public class ServletServerContainerFactoryBean
this.maxBinaryMessageBufferSize = bufferSize;
}
@Nullable
public Integer getMaxBinaryMessageBufferSize() {
return this.maxBinaryMessageBufferSize;
}

View File

@@ -28,6 +28,7 @@ import org.springframework.lang.Nullable;
@SuppressWarnings("serial")
public class SockJsException extends NestedRuntimeException {
@Nullable
private final String sessionId;
@@ -55,6 +56,7 @@ public class SockJsException extends NestedRuntimeException {
/**
* Return the SockJS session id.
*/
@Nullable
public String getSockJsSessionId() {
return this.sessionId;
}

View File

@@ -59,8 +59,10 @@ public abstract class AbstractClientSockJsSession implements WebSocketSession {
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
@Nullable
private volatile State state = State.NEW;
@Nullable
private volatile CloseStatus closeStatus;
@@ -315,15 +317,19 @@ public abstract class AbstractClientSockJsSession implements WebSocketSession {
}
public void afterTransportClosed(@Nullable CloseStatus closeStatus) {
this.closeStatus = (this.closeStatus != null ? this.closeStatus : closeStatus);
Assert.state(this.closeStatus != null, "CloseStatus not available");
CloseStatus cs = this.closeStatus;
if (cs == null) {
cs = closeStatus;
this.closeStatus = closeStatus;
}
Assert.state(cs != null, "CloseStatus not available");
if (logger.isDebugEnabled()) {
logger.debug("Transport closed with " + this.closeStatus + " in " + this);
logger.debug("Transport closed with " + cs + " in " + this);
}
this.state = State.CLOSED;
try {
this.webSocketHandler.afterConnectionClosed(this, this.closeStatus);
this.webSocketHandler.afterConnectionClosed(this, cs);
}
catch (Throwable ex) {
logger.error("WebSocketHandler.afterConnectionClosed threw an exception", ex);

View File

@@ -61,14 +61,17 @@ class DefaultTransportRequest implements TransportRequest {
private SockJsMessageCodec codec;
@Nullable
private Principal user;
private long timeoutValue;
@Nullable
private TaskScheduler timeoutScheduler;
private final List<Runnable> timeoutTasks = new ArrayList<>();
@Nullable
private DefaultTransportRequest fallbackRequest;
@@ -191,7 +194,7 @@ class DefaultTransportRequest implements TransportRequest {
}
@Override
public void onSuccess(WebSocketSession session) {
public void onSuccess(@Nullable WebSocketSession session) {
if (this.handled.compareAndSet(false, true)) {
this.future.set(session);
}

View File

@@ -165,6 +165,7 @@ public class RestTemplateXhrTransport extends AbstractXhrTransport {
private final HttpHeaders headers;
@Nullable
private final String body;
public XhrRequestCallback(HttpHeaders headers) {
@@ -178,9 +179,7 @@ public class RestTemplateXhrTransport extends AbstractXhrTransport {
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
if (this.headers != null) {
request.getHeaders().putAll(this.headers);
}
request.getHeaders().putAll(this.headers);
if (this.body != null) {
StreamUtils.copy(this.body, SockJsFrame.CHARSET, request.getBody());
}

View File

@@ -79,12 +79,15 @@ public class SockJsClient implements WebSocketClient, Lifecycle {
private final List<Transport> transports;
@Nullable
private String[] httpHeaderNames;
private InfoReceiver infoReceiver;
@Nullable
private SockJsMessageCodec messageCodec;
@Nullable
private TaskScheduler connectTimeoutScheduler;
private volatile boolean running = false;
@@ -171,7 +174,7 @@ public class SockJsClient implements WebSocketClient, Lifecycle {
* Jackson2SockJsMessageCodec} is used if Jackson is on the classpath.
*/
public void setMessageCodec(SockJsMessageCodec messageCodec) {
Assert.notNull(messageCodec, "'messageCodec' is required");
Assert.notNull(messageCodec, "SockJsMessageCodec is required");
this.messageCodec = messageCodec;
}
@@ -179,6 +182,7 @@ public class SockJsClient implements WebSocketClient, Lifecycle {
* Return the SockJsMessageCodec to use.
*/
public SockJsMessageCodec getMessageCodec() {
Assert.state(this.messageCodec != null, "No SockJsMessageCodec set");
return this.messageCodec;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-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.
@@ -19,6 +19,7 @@ package org.springframework.web.socket.sockjs.client;
import java.net.URI;
import java.util.UUID;
import org.springframework.lang.Nullable;
import org.springframework.util.IdGenerator;
import org.springframework.util.JdkIdGenerator;
import org.springframework.web.socket.sockjs.transport.TransportType;
@@ -39,10 +40,13 @@ public class SockJsUrlInfo {
private final URI sockJsUrl;
@Nullable
private String serverId;
@Nullable
private String sessionId;
@Nullable
private UUID uuid;

View File

@@ -39,6 +39,7 @@ import org.springframework.web.socket.adapter.NativeWebSocketSession;
*/
public class WebSocketClientSockJsSession extends AbstractClientSockJsSession implements NativeWebSocketSession {
@Nullable
private WebSocketSession webSocketSession;
@@ -51,6 +52,7 @@ public class WebSocketClientSockJsSession extends AbstractClientSockJsSession im
@Override
public Object getNativeSession() {
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession;
}
@@ -62,54 +64,50 @@ public class WebSocketClientSockJsSession extends AbstractClientSockJsSession im
@Override
public InetSocketAddress getLocalAddress() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getLocalAddress();
}
@Override
public InetSocketAddress getRemoteAddress() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getRemoteAddress();
}
@Override
public String getAcceptedProtocol() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getAcceptedProtocol();
}
@Override
public void setTextMessageSizeLimit(int messageSizeLimit) {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
this.webSocketSession.setTextMessageSizeLimit(messageSizeLimit);
}
@Override
public int getTextMessageSizeLimit() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getTextMessageSizeLimit();
}
@Override
public void setBinaryMessageSizeLimit(int messageSizeLimit) {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
this.webSocketSession.setBinaryMessageSizeLimit(messageSizeLimit);
}
@Override
public int getBinaryMessageSizeLimit() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getBinaryMessageSizeLimit();
}
@Override
public List<WebSocketExtension> getExtensions() {
checkDelegateSessionInitialized();
return this.webSocketSession.getExtensions();
}
private void checkDelegateSessionInitialized() {
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getExtensions();
}
public void initializeDelegateSession(WebSocketSession session) {
@@ -118,6 +116,7 @@ public class WebSocketClientSockJsSession extends AbstractClientSockJsSession im
@Override
protected void sendInternal(TextMessage textMessage) throws IOException {
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
this.webSocketSession.sendMessage(textMessage);
}

View File

@@ -58,13 +58,11 @@ public class XhrClientSockJsSession extends AbstractClientSockJsSession {
XhrTransport transport, SettableListenableFuture<WebSocketSession> connectFuture) {
super(request, handler, connectFuture);
Assert.notNull(transport, "'transport' is required");
Assert.notNull(transport, "XhrTransport is required");
this.transport = transport;
this.headers = request.getHttpRequestHeaders();
this.sendHeaders = new HttpHeaders();
if (this.headers != null) {
this.sendHeaders.putAll(this.headers);
}
this.sendHeaders.putAll(this.headers);
this.sendHeaders.setContentType(MediaType.APPLICATION_JSON);
this.sendUrl = request.getSockJsUrlInfo().getTransportUrl(TransportType.XHR_SEND);
}

View File

@@ -69,12 +69,14 @@ public class TransportHandlingSockJsService extends AbstractSockJsService implem
private final Map<TransportType, TransportHandler> handlers = new HashMap<>();
@Nullable
private SockJsMessageCodec messageCodec;
private final List<HandshakeInterceptor> interceptors = new ArrayList<>();
private final Map<String, SockJsSession> sessions = new ConcurrentHashMap<>();
@Nullable
private ScheduledFuture<?> sessionCleanupTask;
private boolean running;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -17,6 +17,7 @@
package org.springframework.web.socket.sockjs.transport.handler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -69,7 +70,7 @@ public class SockJsWebSocketHandler extends TextWebSocketHandler implements SubP
webSocketHandler = WebSocketHandlerDecorator.unwrap(webSocketHandler);
this.subProtocols = ((webSocketHandler instanceof SubProtocolCapable) ?
new ArrayList<>(((SubProtocolCapable) webSocketHandler).getSubProtocols()) : null);
new ArrayList<>(((SubProtocolCapable) webSocketHandler).getSubProtocols()) : Collections.emptyList());
}
@Override

View File

@@ -25,7 +25,6 @@ import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import javax.servlet.ServletRequest;
import org.springframework.http.HttpHeaders;
@@ -33,6 +32,8 @@ import org.springframework.http.server.ServerHttpAsyncRequestControl;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.web.filter.ShallowEtagHeaderFilter;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketExtension;
@@ -53,22 +54,31 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
private final Queue<String> messageCache;
@Nullable
private volatile URI uri;
@Nullable
private volatile HttpHeaders handshakeHeaders;
@Nullable
private volatile Principal principal;
@Nullable
private volatile InetSocketAddress localAddress;
@Nullable
private volatile InetSocketAddress remoteAddress;
@Nullable
private volatile String acceptedProtocol;
@Nullable
private volatile ServerHttpResponse response;
@Nullable
private volatile SockJsFrameFormat frameFormat;
@Nullable
private volatile ServerHttpAsyncRequestControl asyncRequestControl;
private boolean readyToSend;
@@ -84,12 +94,16 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
@Override
public URI getUri() {
return this.uri;
URI uri = this.uri;
Assert.state(uri != null, "No initial request yet");
return uri;
}
@Override
public HttpHeaders getHandshakeHeaders() {
return this.handshakeHeaders;
HttpHeaders headers = this.handshakeHeaders;
Assert.state(headers != null, "No initial request yet");
return headers;
}
@Override
@@ -202,8 +216,9 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
try {
this.response = response;
this.frameFormat = frameFormat;
this.asyncRequestControl = request.getAsyncRequestControl(response);
this.asyncRequestControl.start(-1);
ServerHttpAsyncRequestControl control = request.getAsyncRequestControl(response);
this.asyncRequestControl = control;
control.start(-1);
disableShallowEtagHeaderFilter(request);
// Let "our" handler know before sending the open frame to the remote handler
delegateConnectionEstablished();
@@ -241,8 +256,9 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
}
this.response = response;
this.frameFormat = frameFormat;
this.asyncRequestControl = request.getAsyncRequestControl(response);
this.asyncRequestControl.start(-1);
ServerHttpAsyncRequestControl control = request.getAsyncRequestControl(response);
this.asyncRequestControl = control;
control.start(-1);
disableShallowEtagHeaderFilter(request);
handleRequestInternal(request, response, false);
this.readyToSend = isActive();
@@ -329,12 +345,16 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
@Override
protected void writeFrameInternal(SockJsFrame frame) throws IOException {
if (isActive()) {
String formattedFrame = this.frameFormat.format(frame);
if (logger.isTraceEnabled()) {
logger.trace("Writing to HTTP response: " + formattedFrame);
SockJsFrameFormat frameFormat = this.frameFormat;
ServerHttpResponse response = this.response;
if (frameFormat != null && response != null) {
String formattedFrame = frameFormat.format(frame);
if (logger.isTraceEnabled()) {
logger.trace("Writing to HTTP response: " + formattedFrame);
}
response.getBody().write(formattedFrame.getBytes(SockJsFrame.CHARSET));
response.flush();
}
this.response.getBody().write(formattedFrame.getBytes(SockJsFrame.CHARSET));
this.response.flush();
}
}

View File

@@ -107,8 +107,10 @@ public abstract class AbstractSockJsSession implements SockJsSession {
private volatile long timeLastActive = this.timeCreated;
@Nullable
private ScheduledFuture<?> heartbeatFuture;
@Nullable
private HeartbeatTask heartbeatTask;
private volatile boolean heartbeatDisabled;

View File

@@ -26,6 +26,7 @@ import java.util.Queue;
import java.util.concurrent.LinkedBlockingDeque;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.CloseStatus;
@@ -47,6 +48,7 @@ import org.springframework.web.socket.sockjs.transport.SockJsServiceConfig;
*/
public class WebSocketServerSockJsSession extends AbstractSockJsSession implements NativeWebSocketSession {
@Nullable
private WebSocketSession webSocketSession;
private volatile boolean openFrameSent;
@@ -61,7 +63,7 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession implemen
public WebSocketServerSockJsSession(String id, SockJsServiceConfig config,
WebSocketHandler handler, Map<String, Object> attributes) {
WebSocketHandler handler, @Nullable Map<String, Object> attributes) {
super(id, config, handler, attributes);
}
@@ -69,76 +71,73 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession implemen
@Override
public URI getUri() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getUri();
}
@Override
public HttpHeaders getHandshakeHeaders() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getHandshakeHeaders();
}
@Override
public Principal getPrincipal() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getPrincipal();
}
@Override
public InetSocketAddress getLocalAddress() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getLocalAddress();
}
@Override
public InetSocketAddress getRemoteAddress() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getRemoteAddress();
}
@Override
public String getAcceptedProtocol() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getAcceptedProtocol();
}
@Override
public void setTextMessageSizeLimit(int messageSizeLimit) {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
this.webSocketSession.setTextMessageSizeLimit(messageSizeLimit);
}
@Override
public int getTextMessageSizeLimit() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getTextMessageSizeLimit();
}
@Override
public void setBinaryMessageSizeLimit(int messageSizeLimit) {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
this.webSocketSession.setBinaryMessageSizeLimit(messageSizeLimit);
}
@Override
public int getBinaryMessageSizeLimit() {
checkDelegateSessionInitialized();
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getBinaryMessageSizeLimit();
}
@Override
public List<WebSocketExtension> getExtensions() {
checkDelegateSessionInitialized();
return this.webSocketSession.getExtensions();
}
private void checkDelegateSessionInitialized() {
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return this.webSocketSession.getExtensions();
}
@Override
public Object getNativeSession() {
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
return (this.webSocketSession instanceof NativeWebSocketSession ?
((NativeWebSocketSession) this.webSocketSession).getNativeSession() : this.webSocketSession);
}
@@ -215,6 +214,7 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession implemen
@Override
protected void writeFrameInternal(SockJsFrame frame) throws IOException {
Assert.state(this.webSocketSession != null, "WebSocketSession not yet initialized");
if (logger.isTraceEnabled()) {
logger.trace("Writing " + frame);
}
@@ -228,7 +228,9 @@ public class WebSocketServerSockJsSession extends AbstractSockJsSession implemen
synchronized (this.disconnectLock) {
if (isActive()) {
this.disconnected = true;
this.webSocketSession.close(status);
if (this.webSocketSession != null) {
this.webSocketSession.close(status);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-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.
@@ -75,7 +75,7 @@ public class XhrTransportTests {
TestXhrTransport transport = new TestXhrTransport();
transport.sendMessageResponseToReturn = new ResponseEntity<>(HttpStatus.BAD_REQUEST);
URI url = new URI("http://example.com");
transport.executeSendRequest(url, null, new TextMessage("payload"));
transport.executeSendRequest(url, new HttpHeaders(), new TextMessage("payload"));
}
@Test
@@ -86,6 +86,7 @@ public class XhrTransportTests {
TransportRequest request = mock(TransportRequest.class);
given(request.getSockJsUrlInfo()).willReturn(new SockJsUrlInfo(new URI("http://example.com")));
given(request.getHandshakeHeaders()).willReturn(handshakeHeaders);
given(request.getHttpRequestHeaders()).willReturn(new HttpHeaders());
TestXhrTransport transport = new TestXhrTransport();
WebSocketHandler handler = mock(WebSocketHandler.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-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.
@@ -16,6 +16,8 @@
package org.springframework.web.socket.sockjs.transport.handler;
import java.util.Collections;
import org.junit.Test;
import org.springframework.messaging.SubscribableChannel;
@@ -59,7 +61,7 @@ public class SockJsWebSocketHandlerTests {
WebSocketServerSockJsSession session = new WebSocketServerSockJsSession("1", service, handler, null);
SockJsWebSocketHandler sockJsHandler = new SockJsWebSocketHandler(service, handler, session);
assertNull(sockJsHandler.getSubProtocols());
assertEquals(Collections.emptyList(), sockJsHandler.getSubProtocols());
}
}