Skip to content

Commit d2a7fed

Browse files
authored
GH-10083: Apply NullAbility to core endpoint package
Related to: #10083 * return `Nullable MessageChannel` on `getOutputChannel()` Signed-off-by: Jiandong Ma <[email protected]>
1 parent 8ebc22c commit d2a7fed

17 files changed

+125
-95
lines changed

spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractEndpoint.java

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

1919
import java.util.concurrent.locks.ReentrantLock;
2020

21+
import org.jspecify.annotations.Nullable;
22+
2123
import org.springframework.beans.factory.DisposableBean;
2224
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
2325
import org.springframework.integration.context.IntegrationContextUtils;
@@ -50,9 +52,9 @@ public abstract class AbstractEndpoint extends IntegrationObjectSupport
5052

5153
protected final ReentrantLock lifecycleLock = new ReentrantLock(); // NOSONAR
5254

53-
private String role;
55+
private @Nullable String role;
5456

55-
private SmartLifecycleRoleController roleController;
57+
private @Nullable SmartLifecycleRoleController roleController;
5658

5759
private boolean autoStartup = true;
5860

@@ -81,11 +83,11 @@ public void setPhase(int phase) {
8183
* @see org.springframework.context.SmartLifecycle
8284
* @see org.springframework.integration.support.SmartLifecycleRoleController
8385
*/
84-
public void setRole(String role) {
86+
public void setRole(@Nullable String role) {
8587
this.role = role;
8688
}
8789

88-
public String getRole() {
90+
public @Nullable String getRole() {
8991
return this.role;
9092
}
9193

spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractFetchLimitingMessageSource.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.integration.endpoint;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
import org.springframework.integration.support.management.MessageSourceManagement;
2022

2123
/**
@@ -43,7 +45,7 @@ public int getMaxFetchSize() {
4345
}
4446

4547
@Override
46-
protected Object doReceive() {
48+
protected @Nullable Object doReceive() {
4749
return doReceive(this.maxFetchSize);
4850
}
4951

@@ -55,6 +57,6 @@ protected Object doReceive() {
5557
* necessary.
5658
* @return The value returned.
5759
*/
58-
protected abstract Object doReceive(int maxFetchSizeToReceive);
60+
protected abstract @Nullable Object doReceive(int maxFetchSizeToReceive);
5961

6062
}

spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractMessageSource.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,20 @@ public abstract class AbstractMessageSource<T> extends AbstractExpressionEvaluat
6161

6262
private final Set<MeterFacade> meters = ConcurrentHashMap.newKeySet();
6363

64-
private Map<String, Expression> headerExpressions;
64+
private @Nullable Map<String, Expression> headerExpressions;
6565

66+
@SuppressWarnings("NullAway.Init")
6667
private String beanName;
6768

68-
private String managedType;
69+
private @Nullable String managedType;
6970

70-
private String managedName;
71+
private @Nullable String managedName;
7172

7273
private boolean loggingEnabled = true;
7374

74-
private MetricsCaptor metricsCaptor;
75+
private @Nullable MetricsCaptor metricsCaptor;
7576

76-
private CounterFacade receiveCounter;
77+
private @Nullable CounterFacade receiveCounter;
7778

7879
public void setHeaderExpressions(@Nullable Map<String, Expression> headerExpressions) {
7980
if (!CollectionUtils.isEmpty(headerExpressions)) {
@@ -97,7 +98,7 @@ public void setManagedType(String managedType) {
9798
}
9899

99100
@Override
100-
public String getManagedType() {
101+
public @Nullable String getManagedType() {
101102
return this.managedType;
102103
}
103104

@@ -107,7 +108,7 @@ public void setManagedName(String managedName) {
107108
}
108109

109110
@Override
110-
public String getManagedName() {
111+
public @Nullable String getManagedName() {
111112
return this.managedName;
112113
}
113114

@@ -133,7 +134,7 @@ public ManagementOverrides getOverrides() {
133134
}
134135

135136
@Override
136-
public final Message<T> receive() {
137+
public final @Nullable Message<T> receive() {
137138
try {
138139
return buildMessage(doReceive());
139140
}
@@ -146,12 +147,12 @@ public final Message<T> receive() {
146147
}
147148

148149
@SuppressWarnings("unchecked")
149-
protected Message<T> buildMessage(Object result) {
150+
protected @Nullable Message<T> buildMessage(@Nullable Object result) {
150151
if (result == null) {
151152
return null;
152153
}
153154
Message<?> message;
154-
Map<String, Object> headers = evaluateHeaders();
155+
Map<String, @Nullable Object> headers = evaluateHeaders();
155156
if (result instanceof AbstractIntegrationMessageBuilder<?> messageBuilder) {
156157
if (!CollectionUtils.isEmpty(headers)) {
157158
messageBuilder.copyHeaders(headers);
@@ -190,9 +191,10 @@ private void incrementReceiveCounter() {
190191
this.receiveCounter.increment();
191192
}
192193

194+
@SuppressWarnings("NullAway") // dataflow analysis limitation
193195
private CounterFacade createCounter(boolean success, String exception) {
194196
CounterFacade counter = this.metricsCaptor.counterBuilder(RECEIVE_COUNTER_NAME)
195-
.tag("name", getComponentName() == null ? "unknown" : getComponentName())
197+
.tag("name", getComponentName())
196198
.tag("type", "source")
197199
.tag("result", success ? "success" : "failure")
198200
.tag("exception", exception)
@@ -203,7 +205,7 @@ private CounterFacade createCounter(boolean success, String exception) {
203205
}
204206

205207
@Nullable
206-
private Map<String, Object> evaluateHeaders() {
208+
private Map<String, @Nullable Object> evaluateHeaders() {
207209
return CollectionUtils.isEmpty(this.headerExpressions)
208210
? null
209211
: ExpressionEvalMap.from(this.headerExpressions)

spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractPollingEndpoint.java

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@
6262
import org.springframework.transaction.support.TransactionSynchronization;
6363
import org.springframework.transaction.support.TransactionSynchronizationManager;
6464
import org.springframework.util.Assert;
65-
import org.springframework.util.ClassUtils;
6665
import org.springframework.util.CollectionUtils;
6766
import org.springframework.util.ErrorHandler;
6867
import org.springframework.util.ReflectionUtils;
@@ -98,27 +97,29 @@ public abstract class AbstractPollingEndpoint extends AbstractEndpoint implement
9897

9998
private boolean syncExecutor = true;
10099

101-
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
100+
@SuppressWarnings("NullAway.Init")
101+
private ClassLoader beanClassLoader;
102102

103103
private Trigger trigger = new PeriodicTrigger(Duration.ofMillis(DEFAULT_POLLING_PERIOD));
104104

105-
private ErrorHandler errorHandler;
105+
private @Nullable ErrorHandler errorHandler;
106106

107107
private boolean errorHandlerIsDefault;
108108

109-
private List<Advice> adviceChain;
109+
private @Nullable List<Advice> adviceChain;
110110

111-
private TransactionSynchronizationFactory transactionSynchronizationFactory;
111+
private @Nullable TransactionSynchronizationFactory transactionSynchronizationFactory;
112112

113113
private volatile long maxMessagesPerPoll = -1;
114114

115-
private volatile Callable<Message<?>> pollingTask;
115+
@SuppressWarnings("NullAway.Init")
116+
private volatile Callable<@Nullable Message<?>> pollingTask;
116117

117-
private volatile Flux<Message<?>> pollingFlux;
118+
private volatile @Nullable Flux<Message<?>> pollingFlux;
118119

119-
private volatile Subscription subscription;
120+
private volatile @Nullable Subscription subscription;
120121

121-
private volatile ScheduledFuture<?> runningTask;
122+
private volatile @Nullable ScheduledFuture<?> runningTask;
122123

123124
private volatile boolean initialized;
124125

@@ -127,11 +128,11 @@ public AbstractPollingEndpoint() {
127128
this.setPhase(Integer.MAX_VALUE / 2);
128129
}
129130

130-
public void setTaskExecutor(Executor taskExecutor) {
131+
public void setTaskExecutor(@Nullable Executor taskExecutor) {
131132
this.taskExecutor = (taskExecutor != null ? taskExecutor : new SyncTaskExecutor());
132133
this.syncExecutor = this.taskExecutor instanceof SyncTaskExecutor
133-
|| (this.taskExecutor instanceof ErrorHandlingTaskExecutor
134-
&& ((ErrorHandlingTaskExecutor) this.taskExecutor).isSyncExecutor());
134+
|| (this.taskExecutor instanceof ErrorHandlingTaskExecutor errorHandlingTaskExecutor
135+
&& errorHandlingTaskExecutor.isSyncExecutor());
135136
}
136137

137138
protected Executor getTaskExecutor() {
@@ -142,11 +143,11 @@ protected boolean isSyncExecutor() {
142143
return this.syncExecutor;
143144
}
144145

145-
public void setTrigger(Trigger trigger) {
146+
public void setTrigger(@Nullable Trigger trigger) {
146147
this.trigger = (trigger != null ? trigger : new PeriodicTrigger(Duration.ofMillis(DEFAULT_POLLING_PERIOD)));
147148
}
148149

149-
public void setAdviceChain(List<Advice> adviceChain) {
150+
public void setAdviceChain(@Nullable List<Advice> adviceChain) {
150151
this.adviceChain = adviceChain;
151152
}
152153

@@ -167,7 +168,7 @@ public long getMaxMessagesPerPoll() {
167168
return this.maxMessagesPerPoll;
168169
}
169170

170-
public void setErrorHandler(ErrorHandler errorHandler) {
171+
public void setErrorHandler(@Nullable ErrorHandler errorHandler) {
171172
this.errorHandler = errorHandler;
172173
}
173174

@@ -177,7 +178,7 @@ public void setBeanClassLoader(ClassLoader classLoader) {
177178
}
178179

179180
public void setTransactionSynchronizationFactory(
180-
TransactionSynchronizationFactory transactionSynchronizationFactory) {
181+
@Nullable TransactionSynchronizationFactory transactionSynchronizationFactory) {
181182

182183
this.transactionSynchronizationFactory = transactionSynchronizationFactory;
183184
}
@@ -188,7 +189,7 @@ public void setTransactionSynchronizationFactory(
188189
* @return the channel or null.
189190
* @since 4.3
190191
*/
191-
public MessageChannel getDefaultErrorChannel() {
192+
public @Nullable MessageChannel getDefaultErrorChannel() {
192193
if (!this.errorHandlerIsDefault && this.errorHandler
193194
instanceof MessagePublishingErrorHandler messagePublishingErrorHandler) {
194195

@@ -255,11 +256,11 @@ protected boolean isReactive() {
255256
return false;
256257
}
257258

258-
protected Flux<Message<?>> getPollingFlux() {
259+
protected @Nullable Flux<Message<?>> getPollingFlux() {
259260
return this.pollingFlux;
260261
}
261262

262-
protected Object getReceiveMessageSource() {
263+
protected @Nullable Object getReceiveMessageSource() {
263264
return null;
264265
}
265266

@@ -275,7 +276,7 @@ protected void onInit() {
275276
return;
276277
}
277278
Assert.notNull(this.trigger, "Trigger is required");
278-
if (this.taskExecutor != null && !(this.taskExecutor instanceof ErrorHandlingTaskExecutor)) {
279+
if (!(this.taskExecutor instanceof ErrorHandlingTaskExecutor)) {
279280
if (this.errorHandler == null) {
280281
this.errorHandler = ChannelUtils.getErrorHandler(getBeanFactory());
281282
this.errorHandlerIsDefault = true;
@@ -314,21 +315,20 @@ protected void doStart() {
314315
}
315316
else {
316317
TaskScheduler taskScheduler = getTaskScheduler();
317-
Assert.state(taskScheduler != null, "unable to start polling, no taskScheduler available");
318318
this.runningTask = taskScheduler.schedule(createPoller(), this.trigger);
319319
}
320320
}
321321

322322
@SuppressWarnings("unchecked")
323-
private Callable<Message<?>> createPollingTask() {
323+
private Callable<@Nullable Message<?>> createPollingTask() {
324324
List<Advice> receiveOnlyAdviceChain = null;
325325
if (!CollectionUtils.isEmpty(this.adviceChain)) {
326326
receiveOnlyAdviceChain = this.adviceChain.stream()
327327
.filter(this::isReceiveOnlyAdvice)
328328
.toList();
329329
}
330330

331-
Callable<Message<?>> task = this::doPoll;
331+
Callable<@Nullable Message<?>> task = this::doPoll;
332332

333333
List<Advice> advices = this.adviceChain;
334334
if (!CollectionUtils.isEmpty(advices)) {
@@ -338,7 +338,7 @@ private Callable<Message<?>> createPollingTask() {
338338
.filter(advice -> !isReceiveOnlyAdvice(advice))
339339
.forEach(proxyFactory::addAdvice);
340340
}
341-
task = (Callable<Message<?>>) proxyFactory.getProxy(this.beanClassLoader);
341+
task = (Callable<@Nullable Message<?>>) proxyFactory.getProxy(this.beanClassLoader);
342342
}
343343
if (!CollectionUtils.isEmpty(receiveOnlyAdviceChain)) {
344344
applyReceiveOnlyAdviceChain(receiveOnlyAdviceChain);
@@ -418,39 +418,44 @@ private Flux<Message<?>> createFluxGenerator() {
418418
.doOnSubscribe(subs -> this.subscription = subs);
419419
}
420420

421-
private Message<?> pollForMessage() {
421+
private @Nullable Message<?> pollForMessage() {
422422
Exception pollingTaskError = null;
423423
try {
424424
return this.pollingTask.call();
425425
}
426426
catch (Exception ex) {
427427
pollingTaskError = ex;
428-
if (ex instanceof MessagingException messagingException) { // NOSONAR
428+
if (ex instanceof MessagingException messagingException) {
429429
throw messagingException;
430430
}
431431
else {
432432
Message<?> failedMessage = null;
433433
if (this.transactionSynchronizationFactory != null) {
434-
Object resource = TransactionSynchronizationManager.getResource(getResourceToBind());
434+
Object resource = null;
435+
Object resourceToBind = getResourceToBind();
436+
if (resourceToBind != null) {
437+
resource = TransactionSynchronizationManager.getResource(resourceToBind);
438+
}
435439
if (resource instanceof IntegrationResourceHolder integrationResourceHolder) {
436440
failedMessage = integrationResourceHolder.getMessage();
437441
}
438442
}
439-
throw new MessagingException(failedMessage, ex); // NOSONAR (null failedMessage)
443+
throw failedMessage == null ? new MessagingException((String) null, ex)
444+
: new MessagingException(failedMessage, ex);
440445
}
441446
}
442447
finally {
443448
if (this.transactionSynchronizationFactory != null) {
444449
Object resource = getResourceToBind();
445-
if (TransactionSynchronizationManager.hasResource(resource)) {
450+
if (resource != null && TransactionSynchronizationManager.hasResource(resource)) {
446451
TransactionSynchronizationManager.unbindResource(resource);
447452
}
448453
}
449454
donePollingTask(pollingTaskError);
450455
}
451456
}
452457

453-
private Message<?> doPoll() {
458+
private @Nullable Message<?> doPoll() {
454459
IntegrationResourceHolder holder = bindResourceHolderIfNecessary(getResourceKey(), getResourceToBind());
455460
Message<?> message = null;
456461
try {
@@ -517,7 +522,7 @@ protected void doStop() {
517522
* if no message is immediately available.
518523
* @return The message or null.
519524
*/
520-
protected abstract Message<?> receiveMessage();
525+
protected abstract @Nullable Message<?> receiveMessage();
521526

522527
/**
523528
* Handle a message.
@@ -530,7 +535,7 @@ protected void doStop() {
530535
* synchronization.
531536
* @return The resource, or null if transaction synchronization is not required.
532537
*/
533-
protected Object getResourceToBind() {
538+
protected @Nullable Object getResourceToBind() {
534539
return null;
535540
}
536541

@@ -540,14 +545,14 @@ protected Object getResourceToBind() {
540545
* {@link org.springframework.integration.transaction.ExpressionEvaluatingTransactionSynchronizationProcessor}
541546
* makes this attribute available as a variable in SpEL expressions.
542547
* @return The key, or null (default) if the resource shouldn't be
543-
* made available as a attribute.
548+
* made available as an attribute.
544549
*/
545-
protected String getResourceKey() {
550+
protected @Nullable String getResourceKey() {
546551
return null;
547552
}
548553

549554
@Nullable
550-
private IntegrationResourceHolder bindResourceHolderIfNecessary(String key, Object resource) {
555+
private IntegrationResourceHolder bindResourceHolderIfNecessary(@Nullable String key, @Nullable Object resource) {
551556
if (this.transactionSynchronizationFactory != null && resource != null &&
552557
TransactionSynchronizationManager.isActualTransactionActive()) {
553558

0 commit comments

Comments
 (0)