Search code examples
spring-bootspring-integrationpop3spring-integration-dsl

Spring integration email pop3 inbound adapter not working/starting


With java spring integration written the below code to read the email from gmail. As per logs seems the connection with gmail is formed, but on new email its not reading or not going into handle() method. Please help.

o.s.i.m.AbstractMailReceiver - attempting to receive mail from folder [INBOX]

import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.mail.MailReceiver;
import org.springframework.integration.mail.dsl.Mail;
import org.springframework.integration.mail.support.DefaultMailHeaderMapper;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;

import javax.mail.internet.MimeMessage;

@Log4j2
@Configuration
@EnableIntegration
public class EmailReceiver {
    @Autowired
    private PollableChannel pop3Channel;
    @Bean
    public PollableChannel receivedChannel() {
        return new QueueChannel();
    }
    @Bean
    public IntegrationFlow pop3MailFlow() {
        return IntegrationFlows
                .from(Mail.pop3InboundAdapter("pop.gmail.com", 995, "userName", "password")
                                .javaMailProperties(p -> {
                                    p.put("mail.debug", "true");
                                    p.put("mail.pop3.socketFactory.fallback", "false");
                                    p.put("mail.pop3.port", 995);
                                    p.put("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
                                    p.put("mail.pop3.socketFactory.port", 995);
                                })
                                .headerMapper(mailHeaderMapper()),                       
                        e -> e.poller(Pollers.fixedRate(5000).maxMessagesPerPoll(1)))
                .handle((payload, header) -> logMail(payload))
                .get();
    }
    public Message logMail(Object payload) {
        Message message = (Message)payload;
        log.info("*******Email[TEST]********* ", payload);
        return message;
    }
    @Bean
    public HeaderMapper<MimeMessage> mailHeaderMapper() {
        return new DefaultMailHeaderMapper();
    }
}

Solution

  • The problem is here:

    .maxFetchSize(1)
    

    By default the AbstractMailReceiver tries to fetch all the messages from the mail box. And looks like it takes a lot of time.

    Another problem that with the .headerMapper(mailHeaderMapper() a Mail.pop3InboundAdapter does not produce a Message but rather byte[]. So, your .handle((payload, header) -> logMail(payload)) not only bad by the request-reply definition in the end of flow, but also fails with ClassCast like this, because you expect the type which is not produced for you:

    Caused by: java.lang.ClassCastException: class [B cannot be cast to class org.springframework.messaging.Message ([B is in module java.base of loader 'bootstrap'; org.springframework.messaging.Message is in unnamed module of loader 'app')
    at com.firm.demo.EmailReceiver.logMail(EmailReceiver.java:59)
    at com.firm.demo.EmailReceiver.lambda$pop3MailFlow$2(EmailReceiver.java:53)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    

    Well, no: better to say you are missing the fact that this signature .handle((payload, header) -> logMail(payload)) deals with the payload not the whole message. So, your expectations in the logMail() are wrong. It has to be byte[]. And consider to have a one-way handler over there in the end of flow anyway.

    UPDATE

    The working code is like this:

    @Log4j2
    @Configuration
    @EnableIntegration
    public class EmailReceiver {
    
        @Autowired
        private PollableChannel pop3Channel;
    
        private MailReceiver receiver;
    
    
        @Bean
        public PollableChannel receivedChannel() {
            return new QueueChannel();
        }
    
    
        @Bean
        public IntegrationFlow pop3MailFlow() {
            return IntegrationFlows
                    .from(Mail.pop3InboundAdapter("pop.gmail.com", 995, "userName", "password")
                                    .javaMailProperties(p -> {
                                        p.put("mail.debug", "false");
                                        p.put("mail.pop3.socketFactory.fallback", "false");
                                        p.put("mail.pop3.port", 995);
                                        p.put("mail.pop3.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
                                        p.put("mail.pop3.socketFactory.port", 995);
                                    })
                                    .maxFetchSize(1)
                                    .headerMapper(mailHeaderMapper()),
                            e -> e.poller(Pollers.fixedRate(5000).maxMessagesPerPoll(1)))
                    .handle(this, "logMail")
                    .get();
        }
    
    
        public void logMail(String payload) {
            log.info("*******Email[TEST]********* \n" + payload);
        }
    
        @Bean
        public HeaderMapper<MimeMessage> mailHeaderMapper() {
            return new DefaultMailHeaderMapper();
        }
    
    
    }