Исходный код выглядит следующим образом:
Код: Выделить всё
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