5

How can i create direct channel, imap channel adapter and pass user account information so that program will start looking for new mails.

i have already implemented mail receiver using xml config.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/mail http://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
   xmlns:int="http://www.springframework.org/schema/integration"
   xmlns:int-mail="http://www.springframework.org/schema/integration/mail"
   xmlns:util="http://www.springframework.org/schema/util">

<int:channel id="emails"/>

<util:properties id="javaMailProperties">
    <prop key="mail.imap.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
    <prop key="mail.imap.socketFactory.fallback">false</prop>
    <prop key="mail.store.protocol">imaps</prop>
    <prop key="mail.debug">true</prop>
</util:properties>

<int-mail:imap-idle-channel-adapter id="mailAdapter"
                              store-uri="imaps://login:pass@imap-server:993/INBOX"
                              java-mail-properties="javaMailProperties"
                              channel="emails"
                              should-delete-messages="false"
                              should-mark-messages-as-read="true">
</int-mail:imap-idle-channel-adapter>

Below is Java file which uses xml file.

public class EmailIntegrationTesting {

private static Logger logger = LoggerFactory.getLogger(EmailIntegrationTesting.class);

public static void main(String[] args) throws Exception {
    ApplicationContext ac = new ClassPathXmlApplicationContext("gmail-imap.xml");

    DirectChannel inputChannel = ac.getBean("receiveChannel", DirectChannel.class);
    inputChannel.subscribe(new MessageHandler() {
        @Override
        public void handleMessage(Message<?> message) throws MessagingException {


            MailToStringTransformer m2s = new MailToStringTransformer();
            m2s.setCharset("utf-8");
            System.out.println("Message: " + m2s.transform(message));

            System.out.println("Message: " + message.getPayload());
            Object payload = message.getPayload();

            if (payload instanceof MimeMessage) {
                try {

                    javax.mail.Message mailMessage = (javax.mail.Message) payload;
                    System.out.println(mailMessage.getSubject());
                    System.out.println(getTextFromMessage(mailMessage));

                    Address[] receipts = mailMessage.getAllRecipients();
                    System.out.println("RECEIPIENTS MAIL ID");
                    if (receipts != null && receipts.length > 0) {
                        for (int i = 0; i < receipts.length; i++) {
                            System.out.println(((InternetAddress) receipts[i]).getAddress());
                        }
                    }

                    System.out.println("FROM MAIL ID");
                    Address[] froms = mailMessage.getFrom();
                    String email = froms == null ? null
                            : ((InternetAddress) froms[0]).getAddress();
                    System.out.println(email);

                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }
    });
}

private static String getTextFromMessage(javax.mail.Message message) throws Exception {
    String result = "";
    if (message.isMimeType("text/plain")) {
        result = message.getContent().toString();
    } else if (message.isMimeType("multipart/*")) {
        MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
        result = getTextFromMimeMultipart(mimeMultipart);
    }
    return result;
}

private static String getTextFromMimeMultipart(MimeMultipart mimeMultipart) throws Exception {
    String result = "";
    int count = mimeMultipart.getCount();
    for (int i = 0; i < count; i++) {
        BodyPart bodyPart = mimeMultipart.getBodyPart(i);
        if (bodyPart.isMimeType("text/plain")) {
            result = result + "\n" + bodyPart.getContent();
            break; // without break same text appears twice in my tests
        } else if (bodyPart.isMimeType("text/html")) {
            String html = (String) bodyPart.getContent();
            // result = result + "\n" + org.jsoup.Jsoup.parse(html).text();
        } else if (bodyPart.getContent() instanceof MimeMultipart) {
            result = result + getTextFromMimeMultipart((MimeMultipart) bodyPart.getContent());
        }
    }
    return result;
}

}

i can successfully receive mail using above code.

i can also convert xml to java config. below is the code.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.mail.ImapIdleChannelAdapter;
import org.springframework.integration.mail.ImapMailReceiver;
import java.util.Properties;

@Configuration
class ImapConfiguration {

private Properties javaMailProperties() {
    Properties javaMailProperties = new Properties();

    javaMailProperties.setProperty("mail.imap.socketFactory.class","javax.net.ssl.SSLSocketFactory");
    javaMailProperties.setProperty("mail.imap.socketFactory.fallback","false");
    javaMailProperties.setProperty("mail.store.protocol","imaps");
    javaMailProperties.setProperty("mail.debug","true");

    return javaMailProperties;
}

@Bean
ImapIdleChannelAdapter mailAdapter() {
    ImapMailReceiver mailReceiver = new ImapMailReceiver("imaps://login:pass@imap-server:993/INBOX");

    mailReceiver.setJavaMailProperties(javaMailProperties());
    mailReceiver.setShouldDeleteMessages(false);
    mailReceiver.setShouldMarkMessagesAsRead(true);

    return new ImapIdleChannelAdapter(mailReceiver);
}

@Bean
public MessageChannel emails() {
 return new DirectChannel();
}

}

now, my point is i want to configure above code dynamically.

USECASE when user fill imap server details it should start looking for incoming emails. means i dont want to create bean at the time server start.

Vishal Patel
  • 554
  • 6
  • 15

1 Answers1

9

See my answer to this question and its follow-up.

You can also use the Java DSL to register flows dynamically...

@Autowired
private IntegrationFlowContext flowContext;

...

    IntegrationFlow flow = IntegrationFlows.from(Mail.imapIdleAdapter(...)
                            .handle(...)
                            ...
                            .get();
    IntegrationFlowRegistration flowRegistration =
                this.flowContext.registration(flow)
                        .register();

EDIT

Added a sample Boot application

@SpringBootApplication
public class So42297006Application {

    public static void main(String[] args) throws Exception {
        ConfigurableApplicationContext context = SpringApplication.run(So42297006Application.class, args);
        context.getBean(So42297006Application.class).runDemo();
        context.close();
        System.exit(0);
    }

    public void runDemo() throws Exception {
        Scanner scanner = new Scanner(System.in);
        System.out.println("Enter username");
        String user = scanner.next();
        System.out.println("Enter pw");
        String pw = scanner.next();
        scanner.close();
        startMail(user, pw);
        Thread.sleep(10_000);
    }

    @Autowired
    private IntegrationFlowContext flowContext;

    public void startMail(String user, String pw) {
        IntegrationFlow flow = IntegrationFlows
                .from(Mail.imapIdleAdapter(imapUrl(user, pw))
                        .javaMailProperties(p -> p.put("mail.debug", "false"))
                        .userFlag("testSIUserFlag") // needed by the SI test server - not needed if server supports /SEEN
                        .headerMapper(new DefaultMailHeaderMapper()))
                .handle(System.out::println)
                .get();
        this.flowContext.registration(flow).register();
    }

    private String imapUrl(String user, String pw) {
        return "imap://"
                + user + ":" + pw
                + "@localhost:" + imapServer().getPort() + "/INBOX";
    }

    @Bean
    public TestMailServer.ImapServer imapServer() {
        return TestMailServer.imap(0);
    }

}

Maven deps:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-integration</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-mail</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-test</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-java-dsl</artifactId>
    </dependency>
Community
  • 1
  • 1
Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • Hi, I m very novice in spring and I don't know much about applicationContext. But is it possible to create channel that start receiving mails when some event occurs. In my case user need to fill imap configuration and when he fill up the configuration and click save button I need to start imap receiver on the fly. Is it possible? Can you please add working examples please? – Vishal Patel Feb 17 '17 at 20:05
  • The example in my answer will do exactly that - as soon as you register the flow, the mail adapter will start. – Gary Russell Feb 17 '17 at 20:59
  • I added a quick example. – Gary Russell Feb 17 '17 at 21:44
  • Thanks a lot for this solution, it works perfectly. I have a question though, the message implemented in the "handle" is a GenericMessage, and the payload just a String, where in the OP post, the payload in the message is an AbstractMailReceiver$IntegrationMimeMessage, which is much easier to use I think. Do you have an idea why? – Chaps May 20 '21 at 19:36
  • It's generally best to ask a new question, referencing this answer, rather than commenting, when the answer is old (4+ years); things might have changed. It's because I added a header mapper - see the javadocs for `setHeaderMapper()`. ` `* Set the header mapper; if a header mapper is not provided, the message payload is * a {@link MimeMessage}, when provided, the headers are mapped and the payload is * the {@link MimeMessage} content.` – Gary Russell May 20 '21 at 19:47