Skip to content

Commit 2efd22b

Browse files
authored
spring-projectsGH-10083: Apply Nullability to spring-integration-mail
Related to: spring-projects#10083 Signed-off-by: Jiandong Ma <[email protected]>
1 parent cbb0c6f commit 2efd22b

19 files changed

+115
-73
lines changed

spring-integration-mail/src/main/java/org/springframework/integration/mail/AbstractMailReceiver.java

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import jakarta.mail.Store;
4141
import jakarta.mail.URLName;
4242
import jakarta.mail.internet.MimeMessage;
43+
import org.jspecify.annotations.Nullable;
4344

4445
import org.springframework.beans.factory.DisposableBean;
4546
import org.springframework.expression.Expression;
@@ -79,33 +80,34 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl
7980
*/
8081
public static final String DEFAULT_SI_USER_FLAG = "spring-integration-mail-adapter";
8182

82-
private final URLName url;
83+
private final @Nullable URLName url;
8384

8485
private final ReentrantReadWriteLock folderLock = new ReentrantReadWriteLock();
8586

8687
private final Lock folderReadLock = this.folderLock.readLock();
8788

8889
private final Lock folderWriteLock = this.folderLock.writeLock();
8990

90-
private String protocol;
91+
private @Nullable String protocol;
9192

9293
private int maxFetchSize = -1;
9394

94-
private Session session;
95+
private @Nullable Session session;
9596

9697
private boolean shouldDeleteMessages;
9798

9899
private int folderOpenMode = Folder.READ_ONLY;
99100

100101
private Properties javaMailProperties = new Properties();
101102

102-
private Authenticator javaMailAuthenticator;
103+
private @Nullable Authenticator javaMailAuthenticator;
103104

105+
@SuppressWarnings("NullAway.Init")
104106
private StandardEvaluationContext evaluationContext;
105107

106-
private Expression selectorExpression;
108+
private @Nullable Expression selectorExpression;
107109

108-
private HeaderMapper<MimeMessage> headerMapper;
110+
private @Nullable HeaderMapper<MimeMessage> headerMapper;
109111

110112
private String userFlag = DEFAULT_SI_USER_FLAG;
111113

@@ -117,9 +119,9 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl
117119

118120
private boolean flaggedAsFallback = true;
119121

120-
private volatile Store store;
122+
private volatile @Nullable Store store;
121123

122-
private volatile Folder folder;
124+
private volatile @Nullable Folder folder;
123125

124126
public AbstractMailReceiver() {
125127
this.url = null;
@@ -130,7 +132,7 @@ public AbstractMailReceiver(URLName urlName) {
130132
this.url = urlName;
131133
}
132134

133-
public AbstractMailReceiver(String url) {
135+
public AbstractMailReceiver(@Nullable String url) {
134136
if (url != null) {
135137
this.url = new URLName(url);
136138
}
@@ -139,7 +141,7 @@ public AbstractMailReceiver(String url) {
139141
}
140142
}
141143

142-
public void setSelectorExpression(Expression selectorExpression) {
144+
public void setSelectorExpression(@Nullable Expression selectorExpression) {
143145
this.selectorExpression = selectorExpression;
144146
}
145147

@@ -306,7 +308,7 @@ public void setFlaggedAsFallback(boolean flaggedAsFallback) {
306308
this.flaggedAsFallback = flaggedAsFallback;
307309
}
308310

309-
protected Folder getFolder() {
311+
protected @Nullable Folder getFolder() {
310312
return this.folder;
311313
}
312314

@@ -334,6 +336,7 @@ private void openSession() {
334336

335337
private void connectStoreIfNecessary() throws MessagingException {
336338
if (this.store == null) {
339+
Assert.state(this.session != null, "'session' should not be null");
337340
if (this.url != null) {
338341
this.store = this.session.getStore(this.url);
339342
}
@@ -345,7 +348,8 @@ else if (this.protocol != null) {
345348
}
346349
}
347350
if (!this.store.isConnected()) {
348-
this.logger.debug(() -> "connecting to store [" + this.store.getURLName() + "]");
351+
URLName urlName = this.store.getURLName();
352+
this.logger.debug(() -> "connecting to store [" + urlName + "]");
349353
this.store.connect();
350354
}
351355
}
@@ -360,7 +364,8 @@ protected void openFolder() throws MessagingException {
360364
connectStoreIfNecessary();
361365
}
362366
if (this.folder == null || !this.folder.exists()) {
363-
throw new IllegalStateException("no such folder [" + this.url.getFile() + "]");
367+
String file = this.url != null ? this.url.getFile() : "";
368+
throw new IllegalStateException("no such folder [" + file + "]");
364369
}
365370
if (this.folder.isOpen()) {
366371
return;
@@ -371,6 +376,7 @@ protected void openFolder() throws MessagingException {
371376
}
372377

373378
private Folder obtainFolderInstance() throws MessagingException {
379+
Assert.state(this.store != null, "'store' should not be null");
374380
if (this.url == null) {
375381
return this.store.getDefaultFolder();
376382
}
@@ -422,7 +428,9 @@ protected void closeFolder() {
422428
}
423429

424430
private MimeMessage[] searchAndFilterMessages() throws MessagingException {
425-
this.logger.debug(() -> "attempting to receive mail from folder [" + this.folder.getFullName() + "]");
431+
Assert.state(this.folder != null, "'folder' should not be null");
432+
String fullName = this.folder.getFullName();
433+
this.logger.debug(() -> "attempting to receive mail from folder [" + fullName + "]");
426434
Message[] messagesToProcess;
427435
Message[] messages = searchForNewMessages();
428436
if (this.maxFetchSize > 0 && messages.length > this.maxFetchSize) {
@@ -549,7 +557,9 @@ private void setMessageFlagsAndMaybeDeleteMessages(Message[] messages) throws Me
549557
private void setMessageFlags(Message[] filteredMessages) throws MessagingException {
550558
boolean recentFlagSupported = false;
551559

552-
Flags flags = getFolder().getPermanentFlags();
560+
Folder folder = getFolder();
561+
Assert.state(folder != null, "'folder' should not be null");
562+
Flags flags = folder.getPermanentFlags();
553563

554564
if (flags != null) {
555565
recentFlagSupported = flags.contains(Flags.Flag.RECENT);
@@ -612,6 +622,7 @@ protected void fetchMessages(Message[] messages) throws MessagingException {
612622
contentsProfile.add(FetchProfile.Item.ENVELOPE);
613623
contentsProfile.add(FetchProfile.Item.CONTENT_INFO);
614624
contentsProfile.add(FetchProfile.Item.FLAGS);
625+
Assert.state(this.folder != null, "'folder' should not be null");
615626
this.folder.fetch(messages, contentsProfile);
616627
}
617628

@@ -658,10 +669,12 @@ protected void onInit() {
658669

659670
@Override
660671
public String toString() {
661-
return this.url.toString();
672+
String urlName = this.store != null && this.store.getURLName() != null
673+
? this.store.getURLName().toString() : "";
674+
return this.url != null ? this.url.toString() : urlName;
662675
}
663676

664-
Store getStore() {
677+
@Nullable Store getStore() {
665678
return this.store;
666679
}
667680

@@ -678,7 +691,7 @@ private final class IntegrationMimeMessage extends MimeMessage {
678691

679692
private final MimeMessage source;
680693

681-
private final Object content;
694+
private final @Nullable Object content;
682695

683696
IntegrationMimeMessage(MimeMessage source) throws MessagingException {
684697
super(source);
@@ -700,7 +713,7 @@ private final class IntegrationMimeMessage extends MimeMessage {
700713
}
701714

702715
@Override
703-
public Folder getFolder() {
716+
public @Nullable Folder getFolder() {
704717
if (!AbstractMailReceiver.this.autoCloseFolder) {
705718
return AbstractMailReceiver.this.folder;
706719
}
@@ -731,7 +744,7 @@ public int getLineCount() throws MessagingException {
731744
}
732745

733746
@Override
734-
public Object getContent() throws IOException, MessagingException {
747+
public @Nullable Object getContent() throws IOException, MessagingException {
735748
if (AbstractMailReceiver.this.simpleContent) {
736749
return super.getContent();
737750
}

spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapIdleChannelAdapter.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424
import jakarta.mail.Folder;
2525
import jakarta.mail.Message;
2626
import org.aopalliance.aop.Advice;
27+
import org.jspecify.annotations.Nullable;
2728

2829
import org.springframework.aop.framework.ProxyFactory;
2930
import org.springframework.beans.factory.BeanClassLoaderAware;
@@ -35,7 +36,6 @@
3536
import org.springframework.integration.transaction.IntegrationResourceHolder;
3637
import org.springframework.integration.transaction.IntegrationResourceHolderSynchronization;
3738
import org.springframework.integration.transaction.TransactionSynchronizationFactory;
38-
import org.springframework.lang.Nullable;
3939
import org.springframework.messaging.MessagingException;
4040
import org.springframework.transaction.support.TransactionSynchronization;
4141
import org.springframework.transaction.support.TransactionSynchronizationManager;
@@ -62,18 +62,22 @@ public class ImapIdleChannelAdapter extends MessageProducerSupport implements Be
6262

6363
private final ImapMailReceiver mailReceiver;
6464

65+
@SuppressWarnings("NullAway.Init")
6566
private Executor taskExecutor;
6667

67-
private TransactionSynchronizationFactory transactionSynchronizationFactory;
68+
private @Nullable TransactionSynchronizationFactory transactionSynchronizationFactory;
6869

70+
@SuppressWarnings("NullAway.Init")
6971
private ClassLoader classLoader;
7072

73+
@SuppressWarnings("NullAway.Init")
7174
private ApplicationEventPublisher applicationEventPublisher;
7275

7376
private boolean shouldReconnectAutomatically = true;
7477

75-
private List<Advice> adviceChain;
78+
private @Nullable List<Advice> adviceChain;
7679

80+
@SuppressWarnings("NullAway.Init")
7781
private Consumer<Object> messageSender;
7882

7983
private long reconnectDelay = DEFAULT_RECONNECT_DELAY; // milliseconds
@@ -252,8 +256,8 @@ private void delayNextIdleCall() {
252256
}
253257
}
254258

255-
@Nullable
256-
private static jakarta.mail.MessagingException getJakartaMailMessagingExceptionFromCause(Throwable cause) {
259+
private static jakarta.mail.@Nullable MessagingException getJakartaMailMessagingExceptionFromCause(
260+
@Nullable Throwable cause) {
257261
if (cause == null) {
258262
return null;
259263
}

spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapMailReceiver.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import jakarta.mail.search.NotTerm;
3434
import jakarta.mail.search.SearchTerm;
3535
import org.eclipse.angus.mail.imap.IMAPFolder;
36+
import org.jspecify.annotations.Nullable;
3637

3738
import org.springframework.scheduling.TaskScheduler;
3839
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
@@ -67,19 +68,20 @@ public class ImapMailReceiver extends AbstractMailReceiver {
6768

6869
private long cancelIdleInterval = DEFAULT_CANCEL_IDLE_INTERVAL;
6970

71+
@SuppressWarnings("NullAway.Init")
7072
private TaskScheduler scheduler;
7173

7274
private boolean isInternalScheduler;
7375

74-
private volatile ScheduledFuture<?> pingTask;
76+
private volatile @Nullable ScheduledFuture<?> pingTask;
7577

7678
@SuppressWarnings("this-escape")
7779
public ImapMailReceiver() {
7880
setProtocol(PROTOCOL);
7981
}
8082

8183
@SuppressWarnings("this-escape")
82-
public ImapMailReceiver(String url) {
84+
public ImapMailReceiver(@Nullable String url) {
8385
super(url);
8486
if (url != null) {
8587
Assert.isTrue(url.toLowerCase(Locale.ROOT).startsWith(PROTOCOL),
@@ -212,6 +214,7 @@ else if (!folder.getPermanentFlags().contains(Flags.Flag.RECENT) && searchForNew
212214
@Override
213215
protected Message[] searchForNewMessages() throws MessagingException {
214216
Folder folderToUse = getFolder();
217+
Assert.state(folderToUse != null, "'folderToUse' should not be null");
215218
Flags supportedFlags = folderToUse.getPermanentFlags();
216219
SearchTerm searchTerm = compileSearchTerms(supportedFlags);
217220
if (folderToUse.isOpen()) {
@@ -238,8 +241,10 @@ private Message[] nullSafeMessages(Message[] messageArray) {
238241
}
239242
}
240243

241-
private SearchTerm compileSearchTerms(Flags supportedFlags) {
242-
return this.searchTermStrategy.generateSearchTerm(supportedFlags, this.getFolder());
244+
private @Nullable SearchTerm compileSearchTerms(Flags supportedFlags) {
245+
Folder folderToUse = this.getFolder();
246+
Assert.state(folderToUse != null, "'folderToUse' should not be null");
247+
return this.searchTermStrategy.generateSearchTerm(supportedFlags, folderToUse);
243248
}
244249

245250
@Override
@@ -277,7 +282,7 @@ private class DefaultSearchTermStrategy implements SearchTermStrategy {
277282
}
278283

279284
@Override
280-
public SearchTerm generateSearchTerm(Flags supportedFlags, Folder folder) {
285+
public @Nullable SearchTerm generateSearchTerm(Flags supportedFlags, Folder folder) {
281286
SearchTerm searchTerm = null;
282287
boolean recentFlagSupported = false;
283288
if (supportedFlags != null) {
@@ -320,7 +325,7 @@ public SearchTerm generateSearchTerm(Flags supportedFlags, Folder folder) {
320325
return searchTerm;
321326
}
322327

323-
private SearchTerm applyTermsWhenNoRecentFlag(Folder folder, SearchTerm searchTerm) {
328+
private SearchTerm applyTermsWhenNoRecentFlag(Folder folder, @Nullable SearchTerm searchTerm) {
324329
NotTerm notFlagged;
325330
if (folder.getPermanentFlags().contains(Flag.USER)) {
326331
logger.debug(() -> "This email server does not support RECENT flag, but it does support " +

spring-integration-mail/src/main/java/org/springframework/integration/mail/MailReceiver.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.integration.mail;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
/**
2022
* Strategy interface for receiving mail {@link jakarta.mail.Message Messages}.
2123
*
@@ -25,6 +27,6 @@
2527
*/
2628
public interface MailReceiver {
2729

28-
Object[] receive() throws jakarta.mail.MessagingException;
30+
Object @Nullable [] receive() throws jakarta.mail.MessagingException;
2931

3032
}

spring-integration-mail/src/main/java/org/springframework/integration/mail/MailReceivingMessageSource.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@
2020
import java.util.Queue;
2121
import java.util.concurrent.ConcurrentLinkedQueue;
2222

23+
import org.jspecify.annotations.Nullable;
24+
2325
import org.springframework.core.log.LogMessage;
2426
import org.springframework.integration.endpoint.AbstractMessageSource;
2527
import org.springframework.messaging.MessagingException;
@@ -54,7 +56,7 @@ public String getComponentType() {
5456
}
5557

5658
@Override
57-
protected Object doReceive() {
59+
protected @Nullable Object doReceive() {
5860
try {
5961
Object mailMessage = this.mailQueue.poll();
6062
if (mailMessage == null) {

0 commit comments

Comments
 (0)