Skip to content

Commit 7da77f7

Browse files
artembilangaryrussell
authored andcommitted
INT-1887: Optimize AbstractMailReceiver.receive()
JIRA: https://jira.spring.io/browse/INT-1887 * Optimize access to the `AbstractMailReceiver.folder` via `ReadWriteLock`
1 parent 4ea3b1c commit 7da77f7

File tree

1 file changed

+72
-63
lines changed

1 file changed

+72
-63
lines changed

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

Lines changed: 72 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2018 the original author or authors.
2+
* Copyright 2002-2019 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,9 @@
2424
import java.util.List;
2525
import java.util.Map;
2626
import java.util.Properties;
27+
import java.util.concurrent.locks.Lock;
28+
import java.util.concurrent.locks.ReadWriteLock;
29+
import java.util.concurrent.locks.ReentrantReadWriteLock;
2730

2831
import javax.mail.Authenticator;
2932
import javax.mail.FetchProfile;
@@ -69,7 +72,11 @@ public abstract class AbstractMailReceiver extends IntegrationObjectSupport impl
6972

7073
private final URLName url;
7174

72-
private final Object folderMonitor = new Object();
75+
private final ReadWriteLock folderLock = new ReentrantReadWriteLock();
76+
77+
private final Lock folderReadLock = this.folderLock.readLock();
78+
79+
private final Lock folderWriteLock = this.folderLock.writeLock();
7380

7481
private String protocol;
7582

@@ -134,9 +141,7 @@ public void setProtocol(String protocol) {
134141
/**
135142
* Set the {@link Session}. Otherwise, the Session will be created by invocation of
136143
* {@link Session#getInstance(Properties)} or {@link Session#getInstance(Properties, Authenticator)}.
137-
*
138144
* @param session The session.
139-
*
140145
* @see #setJavaMailProperties(Properties)
141146
* @see #setJavaMailAuthenticator(Authenticator)
142147
*/
@@ -148,9 +153,7 @@ public void setSession(Session session) {
148153
/**
149154
* A new {@link Session} will be created with these properties (and the JavaMailAuthenticator if provided).
150155
* Use either this method or {@link #setSession}, but not both.
151-
*
152156
* @param javaMailProperties The javamail properties.
153-
*
154157
* @see #setJavaMailAuthenticator(Authenticator)
155158
* @see #setSession(Session)
156159
*/
@@ -165,9 +168,7 @@ protected Properties getJavaMailProperties() {
165168
/**
166169
* Optional, sets the Authenticator to be used to obtain a session. This will not be used if
167170
* {@link AbstractMailReceiver#setSession} has been used to configure the {@link Session} directly.
168-
*
169171
* @param javaMailAuthenticator The javamail authenticator.
170-
*
171172
* @see #setSession(Session)
172173
*/
173174
public void setJavaMailAuthenticator(Authenticator javaMailAuthenticator) {
@@ -176,7 +177,6 @@ public void setJavaMailAuthenticator(Authenticator javaMailAuthenticator) {
176177

177178
/**
178179
* Specify the maximum number of Messages to fetch per call to {@link #receive()}.
179-
*
180180
* @param maxFetchSize The max fetch size.
181181
*/
182182
public void setMaxFetchSize(int maxFetchSize) {
@@ -185,15 +185,14 @@ public void setMaxFetchSize(int maxFetchSize) {
185185

186186
/**
187187
* Specify whether mail messages should be deleted after retrieval.
188-
*
189188
* @param shouldDeleteMessages true to delete messages.
190189
*/
191190
public void setShouldDeleteMessages(boolean shouldDeleteMessages) {
192191
this.shouldDeleteMessages = shouldDeleteMessages;
193192
}
193+
194194
/**
195195
* Indicates whether the mail messages should be deleted after being received.
196-
*
197196
* @return true when messages will be deleted.
198197
*/
199198
protected boolean shouldDeleteMessages() {
@@ -243,21 +242,17 @@ public void setEmbeddedPartsAsBytes(boolean embeddedPartsAsBytes) {
243242

244243
/**
245244
* {@link MimeMessage#getContent()} returns just the email body.
246-
*
247245
* <pre class="code">
248246
* foo
249247
* </pre>
250-
*
251248
* Some subclasses, such as {@code IMAPMessage} return some headers with the body.
252-
*
253249
* <pre class="code">
254250
* To: foo@bar
255251
* From: bar@baz
256252
* Subject: Test Email
257253
*
258254
* foo
259255
* </pre>
260-
*
261256
* Starting with version 5.0, messages emitted by mail receivers will render the
262257
* content in the same way as the {@link MimeMessage} implementation returned by
263258
* javamail. In versions 2.2 through 4.3, the content was always just the body,
@@ -266,7 +261,6 @@ public void setEmbeddedPartsAsBytes(boolean embeddedPartsAsBytes) {
266261
* <p>To revert to the previous behavior, set this flag to true. In addition, even
267262
* if a header mapper is provided, the payload will just be the email body.
268263
* @param simpleContent true to render simple content.
269-
*
270264
* @since 5.0
271265
*/
272266
public void setSimpleContent(boolean simpleContent) {
@@ -283,7 +277,6 @@ protected int getFolderOpenMode() {
283277

284278
/**
285279
* Subclasses must implement this method to return new mail messages.
286-
*
287280
* @return An array of messages.
288281
* @throws MessagingException Any MessagingException.
289282
*/
@@ -347,53 +340,67 @@ private Folder obtainFolderInstance() throws MessagingException {
347340

348341
@Override
349342
public Object[] receive() throws javax.mail.MessagingException {
350-
synchronized (this.folderMonitor) {
343+
this.folderReadLock.lock();
344+
Folder folder = getFolder();
345+
if (folder == null || !folder.isOpen()) {
346+
this.folderReadLock.unlock();
347+
this.folderWriteLock.lock();
351348
try {
352-
this.openFolder();
353-
if (this.logger.isInfoEnabled()) {
354-
this.logger.info("attempting to receive mail from folder [" + getFolder().getFullName() + "]");
355-
}
356-
Message[] messages = searchForNewMessages();
357-
if (this.maxFetchSize > 0 && messages.length > this.maxFetchSize) {
358-
Message[] reducedMessages = new Message[this.maxFetchSize];
359-
System.arraycopy(messages, 0, reducedMessages, 0, this.maxFetchSize);
360-
messages = reducedMessages;
361-
}
362-
if (this.logger.isDebugEnabled()) {
363-
this.logger.debug("found " + messages.length + " new messages");
364-
}
365-
if (messages.length > 0) {
366-
fetchMessages(messages);
367-
}
368-
369-
if (this.logger.isDebugEnabled()) {
370-
this.logger.debug("Received " + messages.length + " messages");
371-
}
372-
373-
MimeMessage[] filteredMessages = filterMessagesThruSelector(messages);
349+
openFolder();
350+
folder = getFolder();
351+
this.folderReadLock.lock();
352+
}
353+
finally {
354+
this.folderWriteLock.unlock();
355+
}
356+
}
357+
try {
358+
if (this.logger.isInfoEnabled()) {
359+
this.logger.info("attempting to receive mail from folder [" + folder.getFullName() + "]");
360+
}
361+
Message[] messages = searchForNewMessages();
362+
if (this.maxFetchSize > 0 && messages.length > this.maxFetchSize) {
363+
Message[] reducedMessages = new Message[this.maxFetchSize];
364+
System.arraycopy(messages, 0, reducedMessages, 0, this.maxFetchSize);
365+
messages = reducedMessages;
366+
}
367+
if (this.logger.isDebugEnabled()) {
368+
this.logger.debug("found " + messages.length + " new messages");
369+
}
370+
if (messages.length > 0) {
371+
fetchMessages(messages);
372+
}
374373

375-
postProcessFilteredMessages(filteredMessages);
374+
if (this.logger.isDebugEnabled()) {
375+
this.logger.debug("Received " + messages.length + " messages");
376+
}
376377

377-
if (this.headerMapper != null) {
378-
org.springframework.messaging.Message<?>[] converted =
379-
new org.springframework.messaging.Message<?>[filteredMessages.length];
380-
int n = 0;
381-
for (MimeMessage message : filteredMessages) {
382-
Map<String, Object> headers = this.headerMapper.toHeaders(message);
383-
converted[n++] = getMessageBuilderFactory().withPayload(extractContent(message, headers))
384-
.copyHeaders(headers)
385-
.build();
386-
}
387-
return converted;
388-
}
389-
else {
390-
return filteredMessages;
378+
MimeMessage[] filteredMessages = filterMessagesThruSelector(messages);
379+
380+
postProcessFilteredMessages(filteredMessages);
381+
382+
if (this.headerMapper != null) {
383+
org.springframework.messaging.Message<?>[] converted =
384+
new org.springframework.messaging.Message<?>[filteredMessages.length];
385+
int n = 0;
386+
for (MimeMessage message : filteredMessages) {
387+
Map<String, Object> headers = this.headerMapper.toHeaders(message);
388+
converted[n++] =
389+
getMessageBuilderFactory()
390+
.withPayload(extractContent(message, headers))
391+
.copyHeaders(headers)
392+
.build();
391393
}
394+
return converted;
392395
}
393-
finally {
394-
MailTransportUtils.closeFolder(this.folder, this.shouldDeleteMessages);
396+
else {
397+
return filteredMessages;
395398
}
396399
}
400+
finally {
401+
MailTransportUtils.closeFolder(folder, this.shouldDeleteMessages);
402+
this.folderReadLock.unlock();
403+
}
397404
}
398405

399406
private Object extractContent(MimeMessage message, Map<String, Object> headers) {
@@ -471,17 +478,15 @@ private void setMessageFlags(Message[] filteredMessages) throws MessagingExcepti
471478
if (flags != null && flags.contains(Flags.Flag.USER)) {
472479
if (this.logger.isDebugEnabled()) {
473480
this.logger.debug("USER flags are supported by this mail server. Flagging message with '"
474-
+ this.userFlag + "' user flag");
481+
+ this.userFlag + "' user flag");
475482
}
476483
Flags siFlags = new Flags();
477484
siFlags.add(this.userFlag);
478485
message.setFlags(siFlags, true);
479486
}
480487
else {
481-
if (this.logger.isDebugEnabled()) {
482-
this.logger.debug("USER flags are not supported by this mail server. "
483-
+ "Flagging message with system flag");
484-
}
488+
this.logger.debug("USER flags are not supported by this mail server. " +
489+
"Flagging message with system flag");
485490
message.setFlag(Flags.Flag.FLAGGED, true);
486491
}
487492
}
@@ -556,12 +561,16 @@ protected void setAdditionalFlags(Message message) throws MessagingException {
556561

557562
@Override
558563
public void destroy() {
559-
synchronized (this.folderMonitor) {
564+
this.folderWriteLock.lock();
565+
try {
560566
MailTransportUtils.closeFolder(this.folder, this.shouldDeleteMessages);
561567
MailTransportUtils.closeService(this.store);
562568
this.folder = null;
563569
this.store = null;
564570
}
571+
finally {
572+
this.folderWriteLock.unlock();
573+
}
565574
}
566575

567576
@Override

0 commit comments

Comments
 (0)