Skip to content

Commit 911ddd3

Browse files
authored
Apply Nullability to spring-integration-websocket module
Related to: #10083 * Change `payloadType` to `Class<?>` * Initialize arrays with empty arrays instead of null * Use `{}` instead of `new T[0]` for empty arrays * Add missing `@Nullable` Signed-off-by: Jooyoung Pyoung <[email protected]>
1 parent 51e6320 commit 911ddd3

16 files changed

+112
-58
lines changed

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ClientWebSocketContainer.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.util.concurrent.CountDownLatch;
2323
import java.util.concurrent.TimeUnit;
2424

25+
import org.jspecify.annotations.Nullable;
26+
2527
import org.springframework.context.Lifecycle;
2628
import org.springframework.context.SmartLifecycle;
2729
import org.springframework.http.HttpHeaders;
@@ -60,11 +62,12 @@ public final class ClientWebSocketContainer extends IntegrationWebSocketContaine
6062

6163
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
6264

65+
@SuppressWarnings("NullAway.Init")
6366
private volatile CountDownLatch connectionLatch;
6467

65-
private volatile WebSocketSession clientSession;
68+
private volatile @Nullable WebSocketSession clientSession;
6669

67-
private volatile Throwable openConnectionException;
70+
private volatile @Nullable Throwable openConnectionException;
6871

6972
private volatile boolean connecting;
7073

@@ -121,7 +124,7 @@ public void setConnectionTimeout(int connectionTimeout) {
121124
* @return the {@link #clientSession}, if established.
122125
*/
123126
@Override
124-
public WebSocketSession getSession(String sessionId) {
127+
public WebSocketSession getSession(@Nullable String sessionId) {
125128
if (isRunning()) {
126129
if (!isConnected() && !this.connecting) {
127130
stop();

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@
2727

2828
import org.apache.commons.logging.Log;
2929
import org.apache.commons.logging.LogFactory;
30+
import org.jspecify.annotations.Nullable;
3031

3132
import org.springframework.beans.factory.DisposableBean;
32-
import org.springframework.lang.Nullable;
3333
import org.springframework.util.Assert;
3434
import org.springframework.util.ReflectionUtils;
3535
import org.springframework.web.socket.CloseStatus;
@@ -80,14 +80,13 @@ public abstract class IntegrationWebSocketContainer implements DisposableBean {
8080

8181
private final List<String> supportedProtocols = new ArrayList<>();
8282

83-
private WebSocketListener messageListener;
83+
private @Nullable WebSocketListener messageListener;
8484

8585
private int sendTimeLimit = DEFAULT_SEND_TIME_LIMIT;
8686

8787
private int sendBufferSizeLimit = DEFAULT_SEND_BUFFER_SIZE;
8888

89-
@Nullable
90-
private ConcurrentWebSocketSessionDecorator.OverflowStrategy sendBufferOverflowStrategy;
89+
private ConcurrentWebSocketSessionDecorator.@Nullable OverflowStrategy sendBufferOverflowStrategy;
9190

9291
public void setSendTimeLimit(int sendTimeLimit) {
9392
this.sendTimeLimit = sendTimeLimit;
@@ -107,7 +106,7 @@ public void setSendBufferSizeLimit(int sendBufferSizeLimit) {
107106
* @see ConcurrentWebSocketSessionDecorator
108107
*/
109108
public void setSendBufferOverflowStrategy(
110-
@Nullable ConcurrentWebSocketSessionDecorator.OverflowStrategy overflowStrategy) {
109+
ConcurrentWebSocketSessionDecorator.@Nullable OverflowStrategy overflowStrategy) {
111110

112111
this.sendBufferOverflowStrategy = overflowStrategy;
113112
}
@@ -155,7 +154,7 @@ public Map<String, WebSocketSession> getSessions() {
155154
return Collections.unmodifiableMap(this.sessions);
156155
}
157156

158-
public WebSocketSession getSession(String sessionId) {
157+
public WebSocketSession getSession(@Nullable String sessionId) {
159158
WebSocketSession session = this.sessions.get(sessionId);
160159
Assert.notNull(session, () -> "Session not found for id '" + sessionId + "'");
161160
return session;

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/ServerWebSocketContainer.java

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import java.util.Arrays;
2020

21+
import org.jspecify.annotations.Nullable;
22+
2123
import org.springframework.context.Lifecycle;
2224
import org.springframework.context.SmartLifecycle;
2325
import org.springframework.integration.JavaUtils;
@@ -48,6 +50,7 @@
4850
* @author Artem Bilan
4951
* @author Gary Russell
5052
* @author Christian Tzolov
53+
* @author Jooyoung Pyoung
5154
*
5255
* @since 4.1
5356
*/
@@ -56,21 +59,21 @@ public class ServerWebSocketContainer extends IntegrationWebSocketContainer
5659

5760
private final String[] paths;
5861

59-
private HandshakeHandler handshakeHandler;
62+
private @Nullable HandshakeHandler handshakeHandler;
6063

61-
private HandshakeInterceptor[] interceptors;
64+
private HandshakeInterceptor[] interceptors = {};
6265

63-
private WebSocketHandlerDecoratorFactory[] decoratorFactories;
66+
private WebSocketHandlerDecoratorFactory @Nullable [] decoratorFactories;
6467

65-
private SockJsServiceOptions sockJsServiceOptions;
68+
private @Nullable SockJsServiceOptions sockJsServiceOptions;
6669

67-
private String[] origins;
70+
private String[] origins = {};
6871

6972
private boolean autoStartup = true;
7073

7174
private int phase = 0;
7275

73-
private TaskScheduler sockJsTaskScheduler;
76+
private @Nullable TaskScheduler sockJsTaskScheduler;
7477

7578
public ServerWebSocketContainer(String... paths) {
7679
Assert.notEmpty(paths, "'paths' must not be empty");
@@ -144,7 +147,7 @@ public void setSockJsTaskScheduler(TaskScheduler sockJsTaskScheduler) {
144147
this.sockJsTaskScheduler = sockJsTaskScheduler;
145148
}
146149

147-
public TaskScheduler getSockJsTaskScheduler() {
150+
public @Nullable TaskScheduler getSockJsTaskScheduler() {
148151
return this.sockJsTaskScheduler;
149152
}
150153

@@ -159,19 +162,25 @@ public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
159162
}
160163
}
161164

165+
Assert.notNull(this.handshakeHandler, "'handshakeHandler' must not be null");
162166
WebSocketHandlerRegistration registration = registry.addHandler(webSocketHandler, this.paths)
163-
.setHandshakeHandler(this.handshakeHandler)
164-
.addInterceptors(this.interceptors)
165-
.setAllowedOrigins(this.origins);
167+
.setHandshakeHandler(this.handshakeHandler);
166168

169+
configureRegistration(registration);
167170
configureSockJsOptionsIfAny(registration);
168171
}
169172

173+
private void configureRegistration(WebSocketHandlerRegistration registration) {
174+
registration.addInterceptors(this.interceptors);
175+
registration.setAllowedOrigins(this.origins);
176+
}
177+
170178
private void configureSockJsOptionsIfAny(WebSocketHandlerRegistration registration) {
171179
if (this.sockJsServiceOptions != null) {
172180
SockJsServiceRegistration sockJsServiceRegistration = registration.withSockJS();
173181
JavaUtils.INSTANCE
174-
.acceptIfCondition(this.sockJsServiceOptions.taskScheduler == null,
182+
.acceptIfCondition(this.sockJsServiceOptions.taskScheduler == null &&
183+
this.sockJsTaskScheduler != null,
175184
this.sockJsTaskScheduler, this.sockJsServiceOptions::setTaskScheduler)
176185
.acceptIfNotNull(this.sockJsServiceOptions.webSocketEnabled,
177186
sockJsServiceRegistration::setWebSocketEnabled)
@@ -227,8 +236,8 @@ public boolean isRunning() {
227236
public void start() {
228237
this.lock.lock();
229238
try {
230-
if (this.handshakeHandler instanceof Lifecycle && !isRunning()) {
231-
((Lifecycle) this.handshakeHandler).start();
239+
if (this.handshakeHandler instanceof Lifecycle lifeCycleHandler && !isRunning()) {
240+
lifeCycleHandler.start();
232241
}
233242
}
234243
finally {
@@ -238,15 +247,15 @@ public void start() {
238247

239248
@Override
240249
public void stop() {
241-
if (isRunning()) {
242-
((Lifecycle) this.handshakeHandler).stop();
250+
if (this.handshakeHandler instanceof Lifecycle lifeCycleHandler && isRunning()) {
251+
lifeCycleHandler.stop();
243252
}
244253
}
245254

246255
@Override
247256
public void stop(Runnable callback) {
248-
if (isRunning()) {
249-
((Lifecycle) this.handshakeHandler).stop();
257+
if (this.handshakeHandler instanceof Lifecycle lifeCycleHandler && isRunning()) {
258+
lifeCycleHandler.stop();
250259
}
251260
callback.run();
252261
}
@@ -256,27 +265,27 @@ public void stop(Runnable callback) {
256265
*/
257266
public static class SockJsServiceOptions {
258267

259-
private TaskScheduler taskScheduler;
268+
private @Nullable TaskScheduler taskScheduler;
260269

261-
private String clientLibraryUrl;
270+
private @Nullable String clientLibraryUrl;
262271

263-
private Integer streamBytesLimit;
272+
private @Nullable Integer streamBytesLimit;
264273

265-
private Boolean sessionCookieNeeded;
274+
private @Nullable Boolean sessionCookieNeeded;
266275

267-
private Long heartbeatTime;
276+
private @Nullable Long heartbeatTime;
268277

269-
private Long disconnectDelay;
278+
private @Nullable Long disconnectDelay;
270279

271-
private Integer httpMessageCacheSize;
280+
private @Nullable Integer httpMessageCacheSize;
272281

273-
private Boolean webSocketEnabled;
282+
private @Nullable Boolean webSocketEnabled;
274283

275-
private TransportHandler[] transportHandlers;
284+
private TransportHandler @Nullable [] transportHandlers;
276285

277-
private SockJsMessageCodec messageCodec;
286+
private @Nullable SockJsMessageCodec messageCodec;
278287

279-
private Boolean suppressCors;
288+
private @Nullable Boolean suppressCors;
280289

281290
public SockJsServiceOptions setTaskScheduler(TaskScheduler taskScheduler) {
282291
this.taskScheduler = taskScheduler;

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/IntegrationServletWebSocketHandlerRegistry.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,13 @@ class IntegrationServletWebSocketHandlerRegistry extends ServletWebSocketHandler
4949

5050
private final Map<WebSocketHandler, List<String>> dynamicRegistrations = new HashMap<>();
5151

52+
@SuppressWarnings("NullAway.Init")
5253
private ApplicationContext applicationContext;
5354

55+
@SuppressWarnings("NullAway.Init")
5456
private TaskScheduler sockJsTaskScheduler;
5557

58+
@SuppressWarnings("NullAway.Init")
5659
private volatile IntegrationDynamicWebSocketHandlerMapping dynamicHandlerMapping;
5760

5861
IntegrationServletWebSocketHandlerRegistry() {
@@ -124,6 +127,7 @@ void removeRegistration(ServerWebSocketContainer serverWebSocketContainer) {
124127

125128
private static final class DynamicHandlerRegistrationProxy implements WebSocketHandlerRegistry {
126129

130+
@SuppressWarnings("NullAway.Init")
127131
private IntegrationDynamicWebSocketHandlerRegistration registration;
128132

129133
DynamicHandlerRegistrationProxy() {
@@ -141,6 +145,7 @@ public WebSocketHandlerRegistration addHandler(WebSocketHandler webSocketHandler
141145
private static final class IntegrationDynamicWebSocketHandlerRegistration
142146
extends ServletWebSocketHandlerRegistration {
143147

148+
@SuppressWarnings("NullAway.Init")
144149
private WebSocketHandler handler;
145150

146151
IntegrationDynamicWebSocketHandlerRegistration() {

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/config/WebSocketIntegrationConfigurationInitializer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,10 @@ private void registerEnableWebSocketIfNecessary(BeanDefinitionRegistry registry)
120120

121121
static class WebSocketHandlerMappingFactoryBean extends AbstractFactoryBean<HandlerMapping> {
122122

123+
@SuppressWarnings("NullAway.Init")
123124
private IntegrationServletWebSocketHandlerRegistry registry;
124125

126+
@SuppressWarnings("NullAway.Init")
125127
private ThreadPoolTaskScheduler sockJsTaskScheduler;
126128

127129
public void setRegistry(IntegrationServletWebSocketHandlerRegistry registry) {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/**
22
* Contains parser classes for the WebSockets namespace support.
33
*/
4+
5+
@org.jspecify.annotations.NullMarked
46
package org.springframework.integration.websocket.config;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Provides classes which represent WebSocket event components.
3+
*/
4+
5+
@org.jspecify.annotations.NullMarked
6+
package org.springframework.integration.websocket.event;

spring-integration-websocket/src/main/java/org/springframework/integration/websocket/inbound/WebSocketInboundChannelAdapter.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
import java.util.List;
2222
import java.util.ListIterator;
2323
import java.util.Map;
24-
import java.util.concurrent.atomic.AtomicReference;
24+
25+
import org.jspecify.annotations.Nullable;
2526

2627
import org.springframework.context.ApplicationEventPublisher;
2728
import org.springframework.context.ApplicationEventPublisherAware;
@@ -67,6 +68,7 @@
6768
*
6869
* @author Artem Bilan
6970
* @author Ngoc Nhan
71+
* @author Jooyoung Pyoung
7072
*
7173
* @since 4.1
7274
*/
@@ -99,17 +101,18 @@ public class WebSocketInboundChannelAdapter extends MessageProducerSupport
99101

100102
private final MessageChannel subProtocolHandlerChannel;
101103

102-
private final AtomicReference<Class<?>> payloadType = new AtomicReference<>(String.class);
104+
private Class<?> payloadType = String.class;
103105

106+
@SuppressWarnings("NullAway.Init")
104107
private ApplicationEventPublisher eventPublisher;
105108

106-
private List<MessageConverter> messageConverters;
109+
private @Nullable List<MessageConverter> messageConverters;
107110

108111
private boolean mergeWithDefaultConverters = false;
109112

110113
private boolean useBroker;
111114

112-
private AbstractBrokerMessageHandler brokerHandler;
115+
private @Nullable AbstractBrokerMessageHandler brokerHandler;
113116

114117
public WebSocketInboundChannelAdapter(IntegrationWebSocketContainer webSocketContainer) {
115118
this(webSocketContainer, new SubProtocolHandlerRegistry(new PassThruSubProtocolHandler()));
@@ -163,7 +166,7 @@ public void setMergeWithDefaultConverters(boolean mergeWithDefaultConverters) {
163166
*/
164167
public void setPayloadType(Class<?> payloadType) {
165168
Assert.notNull(payloadType, "'payloadType' must not be null");
166-
this.payloadType.set(payloadType);
169+
this.payloadType = payloadType;
167170
}
168171

169172
/**
@@ -303,7 +306,7 @@ public boolean isActive() {
303306
return active;
304307
}
305308

306-
@SuppressWarnings("unchecked")
309+
@SuppressWarnings({"unchecked", "NullAway"}) // Dataflow analysis limitation
307310
private void handleMessageAndSend(final Message<?> message) {
308311
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.wrap(message);
309312
StompCommand stompCommand = (StompCommand) headerAccessor.getHeader("stompCommand");
@@ -335,8 +338,8 @@ else if (StompCommand.RECEIPT.equals(stompCommand)) {
335338
}
336339
}
337340

338-
private boolean isProcessingTypeOrCommand(SimpMessageHeaderAccessor headerAccessor, StompCommand stompCommand,
339-
SimpMessageType messageType) {
341+
private boolean isProcessingTypeOrCommand(SimpMessageHeaderAccessor headerAccessor, @Nullable StompCommand stompCommand,
342+
@Nullable SimpMessageType messageType) {
340343

341344
return (messageType == null // NOSONAR pretty simple logic
342345
|| SimpMessageType.MESSAGE.equals(messageType)
@@ -346,7 +349,8 @@ private boolean isProcessingTypeOrCommand(SimpMessageHeaderAccessor headerAccess
346349
&& !checkDestinationPrefix(headerAccessor.getDestination());
347350
}
348351

349-
private boolean checkDestinationPrefix(String destination) {
352+
@SuppressWarnings("NullAway") // Dataflow analysis limitation
353+
private boolean checkDestinationPrefix(@Nullable String destination) {
350354
if (this.useBroker) {
351355
Collection<String> destinationPrefixes = this.brokerHandler.getDestinationPrefixes();
352356
if ((destination == null) || CollectionUtils.isEmpty(destinationPrefixes)) {
@@ -374,11 +378,11 @@ private void produceConnectAckMessage(Message<?> message, SimpMessageHeaderAcces
374378
}
375379

376380
private void produceMessage(Message<?> message, SimpMessageHeaderAccessor headerAccessor) {
377-
Object payload = this.messageConverter.fromMessage(message, this.payloadType.get());
381+
Object payload = this.messageConverter.fromMessage(message, this.payloadType);
378382
Assert.state(payload != null,
379383
() -> "The message converter '" + this.messageConverter +
380384
"' produced no payload for message '" + message +
381-
"' and expected payload type: " + this.payloadType.get());
385+
"' and expected payload type: " + this.payloadType);
382386
Message<Object> messageToSend =
383387
getMessageBuilderFactory()
384388
.withPayload(payload)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
/**
22
* Provides classes which represent inbound WebSocket components.
33
*/
4+
5+
@org.jspecify.annotations.NullMarked
46
package org.springframework.integration.websocket.inbound;

0 commit comments

Comments
 (0)