Search code examples
javaspringspring-integrationimapmime

ImapIdleChannelAdapter doesn't get message content


I'm using this code to read mail from an IMAP server:

@EnableIntegration
public class MailIntegration implements HasLogger {

    @Bean
    public ImapIdleChannelAdapter messageChannel(ImapMailReceiver receiver) {
        var receiver = new ImapMailReceiver("imaps://...");
        var adapter = new ImapIdleChannelAdapter(receiver);
        adapter.setOutputChannelName("imapChannel");
        return adapter;
    }

    @ServiceActivator(inputChannel = "imapChannel")
    public void handleMessage(MimeMessage message) {
        getLogger().info("Got message!");

        var subject = message.getSubject();
        getLogger().info("Subject: {}", subject);

        var contentType = message.getContentType();
        getLogger().info("ContentType: {}", contentType);

        var content = message.getContent();
        if (content instanceof String) {
            var text = (String) content;
            getLogger().info("Content: {}", text);
            getLogger().info("Length: {}", text.length());
        } else {
            getLogger().info("Other content: {}", content);
        }
    }
}

If I send plain text e-mail, the handler kicks in and it logs:

INFO : Got message!
INFO : Subject: Lorem ipsum dolor sit amet
INFO : ContentType: text/plain; charset="utf-8"
INFO : Content:
INFO : Length: 0

If I send an HTML e-mail, the handler kicks in and it logs:

INFO : Got message!
INFO : Subject: Lorem ipsum dolor sit amet
INFO : ContentType: text/html; charset="utf-8"
INFO : Content:
INFO : Length: 0

The subject is correct (and so are headers) but the content is always empty for plain and HTML e-mails both.

Also, I would expect to receive a multipart message for HTML, not just the text/html part. In fact, if I check the raw message in my e-mail client I see:

From: Giovanni Lovato <giovanni.lovato@...>
To: Test <test@...>
Subject: Lorem ipsum dolor sit amet
... lots of other header lines ...
Content-type: multipart/alternative; boundary="B_3642854791_1171496246"

> This message is in MIME format. Since your mail reader does not understand
this format, some or all of this message may not be legible.

--B_3642854791_1171496246
Content-type: text/plain; charset="UTF-8"
Content-transfer-encoding: quoted-printable

Lorem ipsum dolor sit amet.

--B_3642854791_1171496246
Content-type: text/html; charset="UTF-8"
Content-transfer-encoding: quoted-printable

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
  <p>Lorem ipsum dolor sit amet.</p>
</body>
</html>


--B_3642854791_1171496246--

So it seems like the ImapIdleChannelAdapter is already extracting the HTML part and passing that to the handler, with all the original headers; still without content, though.

Am I doing something wrong?


Solution

  • Try to set simpleContent to true on the ImapMailReceiver: https://docs.spring.io/spring-integration/docs/current/reference/html/#mail-inbound

    When this is true, the content of the mail body is fetched on demand:

    @Override
        public Object getContent() throws IOException, MessagingException {
            if (AbstractMailReceiver.this.simpleContent) {
                return super.getContent();
            }
            else {
                return this.content;
            }
        }
    

    instead of eager fetch otherwise:

    IntegrationMimeMessage(MimeMessage source) throws MessagingException {
            super(source);
            this.source = source;
            if (AbstractMailReceiver.this.simpleContent) {
                this.content = null;
            }
            else {
                Object complexContent;
                try {
                    complexContent = source.getContent();
                }
                catch (IOException e) {
                    complexContent = "Unable to extract content; see logs: " + e.getMessage();
                    AbstractMailReceiver.this.logger.error("Failed to extract content from " + source, e);
                }
                this.content = complexContent;
            }
        }
    

    In the version 5.2 we have introduced:

    /**
     * Configure a {@code boolean} flag to close the folder automatically after a fetch (default) or
     * populate an additional {@link IntegrationMessageHeaderAccessor#CLOSEABLE_RESOURCE} message header instead.
     * It is the downstream flow's responsibility to obtain this header and call its {@code close()} whenever
     * it is necessary.
     * <p> Keeping the folder open is useful in cases where communication with the server is needed
     * when parsing multipart content of the email with attachments.
     * <p> The {@link #setSimpleContent(boolean)} and {@link #setHeaderMapper(HeaderMapper)} options are not
     * affected by this flag.
     * @param autoCloseFolder {@code false} do not close the folder automatically after a fetch.
     * @since 5.2
     */
    public void setAutoCloseFolder(boolean autoCloseFolder) {
    

    i believe this one should help you as well.