Anonymous
Проблема с почтой Джакарты: MessageCountAdapter, похоже, не работает
Сообщение
Anonymous » 06 ноя 2025, 09:54
Я столкнулся с проблемой при использовании Jakarta Mail для получения электронных писем и сбора определенных ключевых слов. Подробности:
Исходный код выглядит следующим образом:
Код: Выделить всё
package org.example.receiver.kit.component;
import jakarta.mail.Folder;
import jakarta.mail.Message;
import jakarta.mail.MessagingException;
import jakarta.mail.Session;
import jakarta.mail.Store;
import jakarta.mail.event.MessageCountAdapter;
import jakarta.mail.event.MessageCountEvent;
import lombok.NonNull;
import org.eclipse.angus.mail.imap.IdleManager;
import org.springframework.stereotype.Component;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.logging.Logger;
@Component
public class MailReceiver implements Receiver {
public static final Logger logger = Logger.getLogger(MailReceiver.class.getName());
public static final String INBOX = "INBOX";
public static class CustomMessageCountAdapter extends MessageCountAdapter {
private final IdleManager idleManager;
private final Folder inbox;
private final Consumer messageConsumer;
public CustomMessageCountAdapter(IdleManager idleManager, Folder inbox, Consumer messageConsumer) {
this.idleManager = idleManager;
this.inbox = inbox;
this.messageConsumer = messageConsumer;
}
@Override
public void messagesAdded(MessageCountEvent e) {
logger.info("Receive" + e.getMessages().length + "mail(s)");
messageConsumer.accept(e.getMessages());
try {
idleManager.watch(inbox);
} catch (MessagingException ex) {
throw new RuntimeException(ex);
}
}
@Override
public void messagesRemoved(MessageCountEvent e) {
logger.info("Delete" + e.getMessages().length + "mail(s)");
messageConsumer.accept(e.getMessages());
try {
idleManager.watch(inbox);
} catch (MessagingException ex) {
throw new RuntimeException(ex);
}
}
}
public record MailProcessCleaner(Store mailStore, Folder inbox, ExecutorService es) implements Closeable {
public static final Logger logger = Logger.getLogger(MailReceiver.class.getName());
@Override
public void close() throws IOException {
try {
if (inbox.isOpen()) {
inbox.close();
logger.info("Mail server was closed");
}
if (mailStore.isConnected()) {
mailStore.close();
logger.info("Disconnection");
}
es.shutdown();
logger.info("Close thread pool");
} catch (MessagingException e) {
throw new RuntimeException(e);
}
}
}
@Override
public MailProcessCleaner receive(Consumer messageConsumer, @NonNull MailSessionProvider sessionProvider) throws MessagingException {
Session session = sessionProvider.provide();
ExecutorService es = Executors.newCachedThreadPool();
try {
IdleManager idleManager = new IdleManager(session, es);
Store mailStore = session.getStore();
mailStore.connect();
Folder inbox = mailStore.getFolder(INBOX);
inbox.open(Folder.READ_ONLY);
inbox.addMessageCountListener(getMessageCountAdapter(idleManager, inbox, messageConsumer));
idleManager.watch(inbox);
// messageConsumer.accept(inbox.getMessages());
return new MailProcessCleaner(mailStore, inbox, es);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
protected MessageCountAdapter getMessageCountAdapter(IdleManager idleManager, Folder inbox, Consumer messageConsumer) {
return new CustomMessageCountAdapter(idleManager, inbox, messageConsumer);
}
}
Моя конфигурация:
Код: Выделить всё
package org.example.receiver.kit.component;
import jakarta.mail.Authenticator;
import jakarta.mail.PasswordAuthentication;
import jakarta.mail.Session;
import lombok.Getter;
import java.util.Properties;
import java.util.logging.Logger;
@Getter
public abstract class MailSessionProvider implements Provider {
public static final Logger logger = Logger.getLogger(MailSessionProvider.class.getName());
private final String username;
private final String protocol;
private final String host;
private final Integer port;
private final boolean sslEnabled;
public static final String PASSWORD_ENV_KEY = "__MAIL_PASSWORD";
protected Properties defaultProperties = new Properties();
public MailSessionProvider(String username, String protocol, String host, Integer port, boolean sslEnabled) {
this.username = username;
this.protocol = protocol;
this.host = host;
this.port = port;
this.sslEnabled = sslEnabled;
createDefaultProperties();
}
private void createDefaultProperties() {
// https://jakarta.ee/specifications/mail/2.1/jakarta-mail-spec-2.1#a823
defaultProperties.setProperty("mail.store.protocol", this.protocol);
defaultProperties.setProperty("mail.imap.host", this.host);
defaultProperties.setProperty("mail.imap.port", String.valueOf(this.port));
defaultProperties.setProperty("mail.imap.ssl.enable", String.valueOf(this.sslEnabled));
defaultProperties.setProperty("mail.imap.starttls.enable", "false");
// defaultProperties.setProperty("mail.imap.nio.enable", "true");
defaultProperties.setProperty("mail.imap.usesocketchannels", "true");
}
@Override
public Session provide() {
Properties properties = getConnectionProperties();
if (properties == null) {
properties = defaultProperties;
}
return Session.getInstance(
properties,
new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, getPassword());
}
});
}
public final String getPassword() {
return System.getenv(PASSWORD_ENV_KEY);
}
@Override
public Properties getConnectionProperties() {
return defaultProperties;
}
protected void setDefaultProperty(String key, String value) {
defaultProperties.setProperty(key, value);
}
}
Ввод:
Код: Выделить всё
@Autowired
FeishuMailSessionProvider feishuMailSessionProvider;
@Autowired
FeishuMailReceiver feishuMailReceiver;
@Test
public void test() throws MessagingException {
MailReceiver.MailProcessCleaner cleaner = feishuMailReceiver.receive((messages) -> {
try {
for (Message message : messages) {
System.out.println("Got" + Arrays.toString(message.getFrom()));
System.out.println(message.getSubject());
System.out.println(message.getContent());
}
} catch (IOException | MessagingException e) {
throw new RuntimeException(e);
}
System.out.println(messages.length);
}, feishuMailSessionProvider);
for(;;);
}
**Среда:**
JDK 17
spring-boot-starter-mail
Тестовое письмо: Feishu Mail (китайский поставщик услуг электронной почты)
Описание проблемы:
Программа работает нормально некоторое время после запуска. Когда я отправляю тестовые электронные письма на зарегистрированный адрес электронной почты, он успешно их получает и запускает уведомления — я получаю сообщение «Получить 1 письмо».
Однако есть два странных поведения:
Он больше не отправляет уведомления, когда я удаляю электронные письма. Хотя это не влияет на мою текущую функциональность, мне интересно узнать причину.
После того, как программа проработает около 30 минут, MessageCountAdapter, кажется, перестает получать новые электронные письма и не вызывает мой переопределенный метод messagesAdded. Однако, если я отправлю электронное письмо в течение 1–5 минут после запуска программы, она будет работать нормально — 6 минут, 10 минут или даже дольше (я еще не проверял верхний предел).
Может кто-нибудь объяснить такое поведение?
Подробнее здесь:
https://stackoverflow.com/questions/798 ... em-to-work
1762412093
Anonymous
Я столкнулся с проблемой при использовании Jakarta Mail для получения электронных писем и сбора определенных ключевых слов. Подробности: Исходный код выглядит следующим образом: [code]package org.example.receiver.kit.component; import jakarta.mail.Folder; import jakarta.mail.Message; import jakarta.mail.MessagingException; import jakarta.mail.Session; import jakarta.mail.Store; import jakarta.mail.event.MessageCountAdapter; import jakarta.mail.event.MessageCountEvent; import lombok.NonNull; import org.eclipse.angus.mail.imap.IdleManager; import org.springframework.stereotype.Component; import java.io.Closeable; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Consumer; import java.util.logging.Logger; @Component public class MailReceiver implements Receiver { public static final Logger logger = Logger.getLogger(MailReceiver.class.getName()); public static final String INBOX = "INBOX"; public static class CustomMessageCountAdapter extends MessageCountAdapter { private final IdleManager idleManager; private final Folder inbox; private final Consumer messageConsumer; public CustomMessageCountAdapter(IdleManager idleManager, Folder inbox, Consumer messageConsumer) { this.idleManager = idleManager; this.inbox = inbox; this.messageConsumer = messageConsumer; } @Override public void messagesAdded(MessageCountEvent e) { logger.info("Receive" + e.getMessages().length + "mail(s)"); messageConsumer.accept(e.getMessages()); try { idleManager.watch(inbox); } catch (MessagingException ex) { throw new RuntimeException(ex); } } @Override public void messagesRemoved(MessageCountEvent e) { logger.info("Delete" + e.getMessages().length + "mail(s)"); messageConsumer.accept(e.getMessages()); try { idleManager.watch(inbox); } catch (MessagingException ex) { throw new RuntimeException(ex); } } } public record MailProcessCleaner(Store mailStore, Folder inbox, ExecutorService es) implements Closeable { public static final Logger logger = Logger.getLogger(MailReceiver.class.getName()); @Override public void close() throws IOException { try { if (inbox.isOpen()) { inbox.close(); logger.info("Mail server was closed"); } if (mailStore.isConnected()) { mailStore.close(); logger.info("Disconnection"); } es.shutdown(); logger.info("Close thread pool"); } catch (MessagingException e) { throw new RuntimeException(e); } } } @Override public MailProcessCleaner receive(Consumer messageConsumer, @NonNull MailSessionProvider sessionProvider) throws MessagingException { Session session = sessionProvider.provide(); ExecutorService es = Executors.newCachedThreadPool(); try { IdleManager idleManager = new IdleManager(session, es); Store mailStore = session.getStore(); mailStore.connect(); Folder inbox = mailStore.getFolder(INBOX); inbox.open(Folder.READ_ONLY); inbox.addMessageCountListener(getMessageCountAdapter(idleManager, inbox, messageConsumer)); idleManager.watch(inbox); // messageConsumer.accept(inbox.getMessages()); return new MailProcessCleaner(mailStore, inbox, es); } catch (IOException e) { throw new RuntimeException(e); } } protected MessageCountAdapter getMessageCountAdapter(IdleManager idleManager, Folder inbox, Consumer messageConsumer) { return new CustomMessageCountAdapter(idleManager, inbox, messageConsumer); } } [/code] [b]Моя конфигурация:[/b] [code]package org.example.receiver.kit.component; import jakarta.mail.Authenticator; import jakarta.mail.PasswordAuthentication; import jakarta.mail.Session; import lombok.Getter; import java.util.Properties; import java.util.logging.Logger; @Getter public abstract class MailSessionProvider implements Provider { public static final Logger logger = Logger.getLogger(MailSessionProvider.class.getName()); private final String username; private final String protocol; private final String host; private final Integer port; private final boolean sslEnabled; public static final String PASSWORD_ENV_KEY = "__MAIL_PASSWORD"; protected Properties defaultProperties = new Properties(); public MailSessionProvider(String username, String protocol, String host, Integer port, boolean sslEnabled) { this.username = username; this.protocol = protocol; this.host = host; this.port = port; this.sslEnabled = sslEnabled; createDefaultProperties(); } private void createDefaultProperties() { // https://jakarta.ee/specifications/mail/2.1/jakarta-mail-spec-2.1#a823 defaultProperties.setProperty("mail.store.protocol", this.protocol); defaultProperties.setProperty("mail.imap.host", this.host); defaultProperties.setProperty("mail.imap.port", String.valueOf(this.port)); defaultProperties.setProperty("mail.imap.ssl.enable", String.valueOf(this.sslEnabled)); defaultProperties.setProperty("mail.imap.starttls.enable", "false"); // defaultProperties.setProperty("mail.imap.nio.enable", "true"); defaultProperties.setProperty("mail.imap.usesocketchannels", "true"); } @Override public Session provide() { Properties properties = getConnectionProperties(); if (properties == null) { properties = defaultProperties; } return Session.getInstance( properties, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(username, getPassword()); } }); } public final String getPassword() { return System.getenv(PASSWORD_ENV_KEY); } @Override public Properties getConnectionProperties() { return defaultProperties; } protected void setDefaultProperty(String key, String value) { defaultProperties.setProperty(key, value); } } [/code] [b]Ввод:[/b] [code]@Autowired FeishuMailSessionProvider feishuMailSessionProvider; @Autowired FeishuMailReceiver feishuMailReceiver; @Test public void test() throws MessagingException { MailReceiver.MailProcessCleaner cleaner = feishuMailReceiver.receive((messages) -> { try { for (Message message : messages) { System.out.println("Got" + Arrays.toString(message.getFrom())); System.out.println(message.getSubject()); System.out.println(message.getContent()); } } catch (IOException | MessagingException e) { throw new RuntimeException(e); } System.out.println(messages.length); }, feishuMailSessionProvider); for(;;); } [/code] **Среда:** [list] [*]JDK 17 [*]spring-boot-starter-mail [*]Тестовое письмо: Feishu Mail (китайский поставщик услуг электронной почты) [/list] [b]Описание проблемы:[/b] Программа работает нормально некоторое время после запуска. Когда я отправляю тестовые электронные письма на зарегистрированный адрес электронной почты, он успешно их получает и запускает уведомления — я получаю сообщение «Получить 1 письмо». Однако есть два странных поведения: [list] [*]Он больше не отправляет уведомления, когда я удаляю электронные письма. Хотя это не влияет на мою текущую функциональность, мне интересно узнать причину. [*]После того, как программа проработает около 30 минут, MessageCountAdapter, кажется, перестает получать новые электронные письма и не вызывает мой переопределенный метод messagesAdded. Однако, если я отправлю электронное письмо в течение 1–5 минут после запуска программы, она будет работать нормально — 6 минут, 10 минут или даже дольше (я еще не проверял верхний предел). [/list] Может кто-нибудь объяснить такое поведение? Подробнее здесь: [url]https://stackoverflow.com/questions/79810911/issue-with-jakarta-mail-messagecountadapter-doesnt-seem-to-work[/url]