Интеграция Spring - получатель почты imap

Я использую Spring boot 2.2.4-RELEASE и Spring Integration 5.2.3, и я использую IntegrationFlow и DSL, потому что мне нужно настроить несколько серверов IMAP.

Итак, я написал этот код:

String flowId = MAIL_IN_FLOW_ID_PREFIX+cpd.getIndirizzoMail();
if( flowContext.getRegistrationById(flowId) != null ) {
    flowContext.remove(flowId);
}
ImapIdleChannelAdapterSpec imapIdleChannelAdapterSpec = Mail.imapIdleAdapter(connectionUrl.toString())
    .javaMailProperties(javaMailProperties)
    .shouldDeleteMessages(deleteMessages)
    .shouldMarkMessagesAsRead(markMessagesRead)
    .autoStartup(true)
    .autoCloseFolder(false)
    .id(confMailIn.getHost()+"_adapter")
    .selector(selectFunction);
IntegrationFlow flowIdle = IntegrationFlows.from(imapIdleChannelAdapterSpec)
    .handle(msgHandler)
    .get();
flowContext.registration(flowIdle).id(flowId).register();

где msgHandler

@Component
public class MailMessageHandler implements MessageHandler {
    private static final Logger logger = LoggerFactory.getLogger(MailMessageHandler.class.getName());
    @Autowired
    private IConfigCasPostaleSvc ccps;
    @Autowired
    private IGestioneMailSvc gestioneMailSvc;
    @Override
    public void handleMessage(Message<?> message) throws MessagingException {
        MimeMessage mimeMessage = (MimeMessage) message.getPayload();
        if( logger.isDebugEnabled() ) {

            try {
                mimeMessage.getAllHeaders().asIterator().forEachRemaining(header->{
                    logger.debug("Header name {} header value {}", header.getName(), header.getValue());
                });
            } catch (javax.mail.MessagingException e) {
                logger.error("Errore nella lettura degli header", e);
            }
        }
        try {
            //Recupero i dati del messaggio
            MimeMessageParser parser = new MimeMessageParser(mimeMessage);
            parser = parser.parse();
            String mailId = mimeMessage.getMessageID();
            String oggettoMail = parser.getSubject();
            Date receivedDate = mimeMessage.getReceivedDate();
            List<DataSource> allegatiMail = parser.getAttachmentList();

            String corpoMail = parser.getHtmlContent();
            if( !StringUtils.hasText(corpoMail) ) {
                if( logger.isDebugEnabled() ) {
                    logger.debug("Nessun contenuto HTML nella mail; recupero il contenuto plain/text");
                }
                corpoMail = parser.getPlainContent();
            }
            MailInDto datiMail = new MailInDto();
            datiMail.setAllegatiMail(allegatiMail);
            datiMail.setIdMail(mailId);
            datiMail.setOggettoMail(oggettoMail);
            datiMail.setDataRicezioneMail(receivedDate);
            datiMail.setAllegatiMail(allegatiMail);
            datiMail.setCorpoMail(corpoMail);
            List<Address> destinatari = parser.getTo();
            for (Address to:destinatari) {

                String destinatario = to.toString();
                if( to instanceof InternetAddress ){
                    destinatario = ((InternetAddress)to).getAddress();
                }
                //Considero solo i destinatari che sono censiti nelle nostre tabelle
                Optional<ConfigurazioneCasellaPostaleDto> configurazioneCasellaPostale = ccps.getConfigurazioneCasellaPostale(destinatario);
                if( configurazioneCasellaPostale.isPresent() ) {
                    ConfigurazioneCasellaPostaleDto ccpd = configurazioneCasellaPostale.get();
                    //Indico l'UUID del documentale che contiene tutti i messaggi mail della casella postale
                    datiMail.setParentIdFolder(ccpd.getCasellaPostale().getIdFolderDocumentale());
                    //Posso salvare
                    datiMail.setIdCasellaPostale(ccpd.getCasellaPostale().getPk());
                    this.gestioneMailSvc.storeReceivedMail(datiMail, parser);
                }else {
                    if( logger.isDebugEnabled() ) {
                        logger.debug("Nessuna configurazione casella postale trovata per il destinatario {}", destinatario);
                    }
                }
            }
        }catch (Exception e) {

            throw new MessagingException("Errore nella gestione del messaggio "+message, e); 
        }
    }
}

Установив .autoCloseFolder(false), я могу обрабатывать MimeMessage в компоненте обработчика, но у меня есть несколько вопросов.

  1. По документации, когда я использую .autoCloseFolder(false), я читал:

Если задано значение false, папка не закрывается автоматически после выборки. Целевое приложение обязано закрыть его с помощью заголовка IntegrationMessageHeaderAccessor.CLOSEABLE_RESOURCE из сообщения, созданного этим адаптером канала.

Я не могу понять, нужно ли и что мне делать, чтобы фреймворк закрыл папку.

  1. Иногда я получаю исключение закрытия папки с ошибкой. Я не могу понять причину
  2. Я не уверен, что, используя одноэлементный компонент в качестве msgHandler, я поступаю правильно. Конечно, у меня есть все локальные переменные, и я всегда делаю запросы к БД, но мне интересно, правильный ли это подход

Кто-нибудь может дать мне какое-нибудь предложение?

Спасибо

Анджело

EDIT - STACKTRACE

Здесь находится закрытая папка stacktrace

2020-02-11 18: 40: 01,688 206505 [task-scheduler-2] WARN o.s.i.mail.ImapIdleChannelAdapter - Не удалось выполнить задачу IDLE. Будет предпринята попытка повторной отправки через 10000 миллисекунд. java.lang.IllegalStateException: сбой в задаче «простоя». Отправим повторно. в org.springframework.integration.mail.ImapIdleChannelAdapter $ IdleTask.run (ImapIdleChannelAdapter.java:295) в org.springframework.integration.mail.ImapIdleChannelAdapter $ ReceivingTask.rched.frame.subscribe.com .DelegatingErrorHandlingRunnable.run (DelegatingErrorHandlingRunnable.java:54) в org.springframework.scheduling.concurrent.ReschedulingRunnable.run (ReschedulingRunnable.java:93) в java.base / Execurter.java:93 : 515) в java.base / java.util.concurrent.FutureTask.run $$$ capture (FutureTask.java:264) в java.base / java.util.concurrent.FutureTask.run (FutureTask.java) в java. base / java.util.concurrent.ScheduledThreadPoolExecutor $ ScheduledFutureTask.run (ScheduledThreadPoolExecutor.java:304) в java.base / java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPooljava.java.buttil: java. concurrent.ThreadPoolExecutor $ Worke r.run (ThreadPoolExecutor.java:628) в java.base / java.lang.Thread.run (Thread.java:830) Вызвано: javax.mail.MessagingException: папка закрыта в org.springframework.integration.mail. ImapMailReceiver.searchForNewMessages (ImapMailReceiver.java:226) по адресу org.springframework.integration.mail.ImapMailReceiver.waitForNewMessages (ImapMailReceiver.java:189) по адресу org.springTapdleframework.integration. ... 10 общих кадров опущены


person Angelo Immediata    schedule 11.02.2020    source источник


Ответы (1)


Я думаю, вы получаете "исключение закрытия папки", когда вызываете это flowContext.remove(flowId);, но в вашем MailMessageHandler еще есть какой-то процесс, пытающийся прочитать MimeMessage.

Чтобы закрыть папку вручную, вам нужно получить доступ к StaticMessageHeaderAccessor.getCloseableResource(message) и вызвать его close().

person Artem Bilan    schedule 11.02.2020
comment
Спасибо, Артем. В своих тестах я никогда не вызывал flowContext.remove(flowId);, это вызывается только при изменении конфигурации imap, но я не пробовал этот сценарий. Я оставляю свое приложение с быстрой загрузкой запущенным; Я попытаюсь получить сообщение и опубликовать трассировку стека. Чтобы закрыть поток, я должен добавить метод finally и вызвать написанную вами инструкцию. Правильно ли я понял? - person Angelo Immediata; 11.02.2020
comment
OK. Таким образом, вы выходите из своего теста и закрываете контекст приложения, не дожидаясь, пока процесс почтового сообщения завершит свою работу. - person Artem Bilan; 11.02.2020
comment
привет Артем. Я немного запутался, когда мне следует закрыть папку. На самом деле я реализовал свой код, закрыв папку, когда закончил обрабатывать mimemessage; Мне интересно, правильный ли это подход. У вас есть какие-нибудь подсказки? - person Angelo Immediata; 03.03.2020