I have a Spring Boot project that is leveraging Spring Integration. My goal is to to poll a POP3 mail server regularly, and download any attachments associated with those messages. My relevant Spring Config looks like this:
@Configuration
public class MailIntegrationConfig {
@Value("${path.output.temp}")
private String outPath;
@Bean
public MessageChannel mailChannel() {
return new DirectChannel();
}
@Bean
@InboundChannelAdapter(value = "mailChannel", poller = @Poller(fixedDelay = "16000"))
public MessageSource<Object> fileReadingMessageSource() {
var receiver = new Pop3MailReceiver("pop3s://user:passwordexample.com/INBOX");
var mailProperties = new Properties();
mailProperties.setProperty("mail.pop3.port", "995");
mailProperties.put("mail.pop3.ssl.enable", true);
receiver.setShouldDeleteMessages(false);
receiver.setMaxFetchSize(10);
receiver.setJavaMailProperties(mailProperties);
// receiver.setHeaderMapper(new DefaultMailHeaderMapper());
var source = new MailReceivingMessageSource(receiver);
return source;
}
@Bean
@ServiceActivator(inputChannel = "mailChannel")
public MessageHandler popMessageHandler() {
return new MailReceivingMessageHandler(outPath);
}
}
My MailReceivingMessageHandler class (partial)
public class MailReceivingMessageHandler extends AbstractMessageHandler {
private String outDir;
public MailReceivingMessageHandler(String outDir) {
var outPath = new File(outDir);
if (!outPath.exists()) {
throw new IllegalArgumentException(String.format("%s does not exist.", outDir));
}
this.outDir = outDir;
}
@Override
protected void handleMessageInternal(org.springframework.messaging.Message<?> message) {
Object payload = message.getPayload();
if (!(payload instanceof Message)) {
throw new IllegalArgumentException(
"Unable to create MailMessage from payload type [" + message.getPayload().getClass().getName()
+ "], " + "expected MimeMessage, MailMessage, byte array or String.");
}
try {
var msg = (Message) payload;
System.out.println(String.format("Headers [%s] Subject [%s]. Content-Type [%s].", msg.getAllHeaders(),
msg.getSubject(), msg.getContentType()));
this.handleMessage(msg);
} catch (IOException | MessagingException e) {
e.printStackTrace();
}
}
private void handleMessage(Message msg) throws MessagingException, IOException {
var cType = msg.getContentType();
if (cType.contains(MediaType.TEXT_PLAIN_VALUE)) {
handleText((String) msg.getContent());
} else if (cType.contains(MediaType.MULTIPART_MIXED_VALUE)) {
handleMultipart((Multipart) msg.getContent());
}
}
// See
// https://stackoverflow.com/questions/1748183/download-attachments-using-java-mail
private void handleMultipart(Multipart msgContent) throws MessagingException, IOException {
var mCount = msgContent.getCount();
for (var i = 0; i < mCount; i++) {
this.processAttachments(msgContent.getBodyPart(i));
}
}
private void processAttachments(BodyPart part) throws IOException, MessagingException {
var content = part.getContent();
if (content instanceof InputStream || content instanceof String) {
if (Part.ATTACHMENT.equalsIgnoreCase(part.getDisposition()) || !part.getFileName().isBlank()) {
var fName = String.format("%s.%s", UUID.randomUUID().toString(),
FilenameUtils.getExtension(part.getFileName()));
FileUtils.copyInputStreamToFile(part.getInputStream(), new File(outDir + File.separator + fName));
}
if (content instanceof Multipart) {
Multipart multipart = (Multipart) content;
for (int i = 0; i < multipart.getCount(); i++) {
var bodyPart = multipart.getBodyPart(i);
processAttachments(bodyPart);
}
}
}
}
}
Whenever I run my code using the config above, I receive the following error:
javax.mail.MessagingException: No inputstream from datasource;
nested exception is:
java.lang.IllegalStateException: Folder is not Open
at javax.mail.internet.MimeMultipart.parse(MimeMultipart.java:576)
at javax.mail.internet.MimeMultipart.getCount(MimeMultipart.java:312)
at com.midamcorp.data.mail.MailReceivingMessageHandler.handleMultipart(MailReceivingMessageHandler.java:70)
at com.midamcorp.data.mail.MailReceivingMessageHandler.handleMessage(MailReceivingMessageHandler.java:58)
at com.midamcorp.data.mail.MailReceivingMessageHandler.handleMessageInternal(MailReceivingMessageHandler.java:44)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:62)
at org.springframework.integration.handler.ReplyProducingMessageHandlerWrapper.handleRequestMessage(ReplyProducingMessageHandlerWrapper.java:58)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:134)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:62)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:115)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:133)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:106)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:72)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:570)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:520)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:187)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:166)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:109)
at org.springframework.integration.endpoint.SourcePollingChannelAdapter.handleMessage(SourcePollingChannelAdapter.java:196)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.messageReceived(AbstractPollingEndpoint.java:444)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.doPoll(AbstractPollingEndpoint.java:428)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.pollForMessage(AbstractPollingEndpoint.java:376)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.lambda$null$3(AbstractPollingEndpoint.java:323)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.lambda$execute$0(ErrorHandlingTaskExecutor.java:57)
at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
at org.springframework.integration.util.ErrorHandlingTaskExecutor.execute(ErrorHandlingTaskExecutor.java:55)
at org.springframework.integration.endpoint.AbstractPollingEndpoint.lambda$createPoller$4(AbstractPollingEndpoint.java:320)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.IllegalStateException: Folder is not Open
at com.sun.mail.pop3.POP3Folder.checkOpen(POP3Folder.java:562)
at com.sun.mail.pop3.POP3Folder.getProtocol(POP3Folder.java:592)
at com.sun.mail.pop3.POP3Message.getRawStream(POP3Message.java:154)
at com.sun.mail.pop3.POP3Message.getContentStream(POP3Message.java:251)
at javax.mail.internet.MimePartDataSource.getInputStream(MimePartDataSource.java:78)
at javax.mail.internet.MimeMultipart.parse(MimeMultipart.java:570)
... 35 more
Obviously, the root cause is clear - the POP3 folder is closed. I have seen solutions that would likely be able to handle when just the Java mail classes are used, but none with Spring Integration. My question is how does one properly control when a folder is open or closed using Spring Integration Mail? I realize the Pop3MailReceiver class has a .setAutoCloseFolder()
method. Based on the Spring Docs, I assume I need to set that, along something like the following to my handler:
Closeable closeableResource = StaticMessageHeaderAccessor.getCloseableResource(message);
if (closeableResource != null) {
closeableResource.close();
}
However, if I set autoCloseFolder to false, it does not appear as if the message even ever "hits" my handler, so unfortunately being able to close the resource does not even matter at this point. That is, when autoClose is set to false, the 'handleMessageInternal()' method in my handler class is never reached even though there are indeed message on the POP3 server. Instead I just get a bunch of logs like this:
2020-06-26 15:26:54.523 INFO 15348 --- [ scheduling-1] o.s.integration.mail.Pop3MailReceiver : attempting to receive mail from folder [INBOX]
What am I missing? Thanks.