Search code examples
gmailspring-integrationjakarta-mail

Spring Integration flow for consuming gmail messages with file attachments, by example


Spring Integration 5.x here. I am trying to write an IntegrationFlow that:

  1. Reads emails from a Gmail account. If the emails do not contain file attachments, they are ignored and simlpy marked as "Read". Otherwise...
  2. For each email with attachments, and for each one of those attachments, I need the attachment to be in the Message<?> body (as a byte[] or similar) and I need to send the message off to a bean handler called documentRegistrar. My best attempt thus far is this:
@Configuration
public class GmailIntegrationConfiguration {

    private String emailHost;
    private String emailUsername;
    private String emailPassword;
    private DocumentRegistrar documentRegistrar;

    @Autowired
    public GmailIntegrationConfiguration(
            @Value("${email.host}") String emailHost,
            @Value("${email.username}") String emailUsername,
            @Value("${email.password}") String emailPassword,
            DocumentRegistrar documentRegistrar) {
        this.emailHost = emailHost;
        this.emailUsername = emailUsername;
        this.emailPassword = emailPassword;
        this.awsAccessKey = awsAccessKey;
    }

    @Bean
    public IntegrationFlow gmailIntegrationFlow() {
        return IntegrationFlows.from(emailAdapterSpec ->
            Mail.imapInboundAdapter("imap://imapuser:pw@localhost:993/INBOX")
                    .javaMailProperties(p -> p.put("mail.imap.ssl.enable", "true"))
                    .autoCloseFolder(false))
                .channel(MessageChannels.queue())
                .handle(documentRegistrar)
            .get();
    }

}

The code for Mail.imapInboundAdapter and createImapAdapterSpec is just what I was able to Frankenstein together using the Spring Integration docs and a few Google searches, but they are both fraught with compiler errors.

Can anyone help nudge me over the finish line with a concrete working example here? Specifically the Mail.imapInboundAdapter does not have port, user password, etc. builder methods on it, so how would I configure all of those? And the file attachements filter is definitely wrong as Spring does not appear to have a MimeBodyPart type defined (at all). Thanks in advance for any and all help!


Solution

  • Not sure what drove you to that configuration style, since it is clear from the Mail.imapInboundAdapter() API that there is no those props. There is only this:

    public static ImapMailInboundChannelAdapterSpec imapInboundAdapter(String url) {
    

    So, you need to build an URL from those your parts.

    Something in style with this sample:

    Mail.imapInboundAdapter("imap://imapuser:pw@localhost:993/INBOX")

    And yes, folder need to be present as a part of the IMAP URL.

    See .autoCloseFolder(false) option and its Javadocs for mutlipart mail to be parsed downstream:

    /**
     * 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) {
    

    If you don't provide a HeaderMapper, then with .autoCloseFolder(false), the whole MimeMessage is provided in the payload. The channel adapter does not determine if there are some attachments in the mail message or not. More over it seems Gmail always produce mutilpart message even if there is just one simple part in the body.

    You probably need to push the parsing and filtering logic down to your documentRegistrar and don't forget to close the folder from time to time: https://docs.spring.io/spring-integration/docs/current/reference/html/mail.html#mail-inbound