具有runnable while(true)循环的Tomcat执行程序只运行一次。为什么?

时间:2014-10-28 23:29:09

标签: javamail imap runnable gmail-imap servletcontextlistener

我正在尝试在Tomcat中实现javax.mail.event.MessageCountListener。当我启动应用程序时,似乎运行contextInitialized方法并读取邮箱。但是,我看到了日志消息"空闲"只有一次。我希望它会不断闲置,并在收到或删除电子邮件时调用AnalyzerService()。

更新:发现idle()方法没有返回。它运行直到com.sun.mail.iap.ResponseInputStream.readResponse(ByteArray ba)方法,它运行到一个永远不会出来的while循环。

我是否滥用idle()方法做一些我不应该做的事情?这是com.sun.mail.iap包中的错误吗?

AnalyzerContextListener.java:

import com.sun.mail.imap.IMAPStore;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.event.MessageCountListener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class AnalyzerContextListener implements ServletContextListener {

    private ExecutorService executorService;
    private final String username = "myemail@gmail.com";
    private final String password = "mypassword";
    private final String mailhost = "imap.gmail.com";
    private final String foldername = "INBOX";

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        final ServletContext servletContext = sce.getServletContext();
        executorService = Executors.newFixedThreadPool(3);
        Session session = Session.getInstance(new Properties());
        try {
            final IMAPStore store = (IMAPStore) session.getStore("imaps");
            store.connect(mailhost, username, password);
            final Folder folder = store.getFolder(foldername);
            if (folder == null) {
                servletContext.log("Folder in mailbox bestaat niet.");
                return;
            }
            folder.open(Folder.READ_ONLY);
            MessageCountListener countListener = new AnalyzerService();
            folder.addMessageCountListener(countListener);

            Runnable runnable = new Runnable() {

                @Override
                public void run() {
                    while (true) {
                        try {
                            servletContext.log("Aantal berichten in folder: " + folder.getMessageCount());
                            servletContext.log("Idling");
                            store.idle();
                        } catch (MessagingException ex) {
                            servletContext.log(ex.getMessage());
                            return;
                        }
                    }
                }
            };
            executorService.execute(runnable);
            servletContext.log("Executorservice gestart");
        } catch (MessagingException ex) {
            servletContext.log(ex.getMessage());
        }
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        sce.getServletContext().log("Context wordt vernietigd");
        executorService.shutdown();
        sce.getServletContext().log("Executorservice gestopt");
    }
}

AnalyzerService.java:

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.event.MessageCountEvent;
import javax.mail.event.MessageCountListener;

class AnalyzerService implements MessageCountListener {

    public AnalyzerService() {
    }

    @Override
    public void messagesAdded(MessageCountEvent event) {

        Message[] addedMessages = event.getMessages();
        for (Message message : addedMessages) {
            try {
                System.out.println(message.getSubject());
            } catch (MessagingException ex) {
                System.out.println(ex.getMessage());
            }
        }
    }

    @Override
    public void messagesRemoved(MessageCountEvent event) {

        Message[] removedMessages = event.getMessages();
        for (Message message : removedMessages) {
            try {
                System.out.println(message.getSubject());
            } catch (MessagingException ex) {
                System.out.println(ex.getMessage());
            }
        }

    }

}

1 个答案:

答案 0 :(得分:2)

while (true) {
    try {
        servletContext.log("Aantal berichten in folder: " + folder.getMessageCount());
        servletContext.log("Idling");
        store.idle();
    } catch (MessagingException ex) {
        servletContext.log(ex.getMessage());
        return;
    }
}

2 3种可能性早于>只运行一次

  • 循环实际上结束了:

    1. 如果是return,请通过明确MessagingException。看看你的日志,有一条消息或类似的东西,比如" null"。考虑使用正确的堆栈跟踪日志(.log(String message, Throwable throwable)),因为Exception#getMessage()通常是空的或者没有告诉您太多。

    2. 通过任何未经检查的异常。您应该注意到在某些日志中,因为未被捕获的异常通过executorService.execute应该调用最近的未捕获的exeption处理程序,这通常是坏的。请参阅Choose between ExecutorService's submit and ExecutorService's execute

  • 循环在记录"Idling"

    后停止执行
    1. store.idle()永远不会回来。 (每隔一行代码也可以在理论上做到这一点,例如第二次迭代中的folder.getMessageCount()调用,但这种情况不太可能发生)

关于3号 - documentation

  

如果服务器支持,请使用IMAP IDLE命令(请参阅RFC 2177)进入空闲模式,以便服务器可以发送未经请求的通知,而无需客户端不断轮询服务器。使用 ConnectionListener 通知事件。 当另一个线程(例如,侦听器线程)需要为此Store发出IMAP命令时,将终止空闲模式,此方法将返回。通常,调用者将循环调用此方法。

     

如果设置了mail.imap.enableimapevents属性,则在IDLE命令处于活动状态时收到的通知将作为具有IMAPStore.RESPONSE类型的事件传递给 ConnectionListeners 。该事件的消息将是原始IMAP响应字符串。请注意,在未选择邮箱的连接(即此方法)上使用IDLE命令时,大多数IMAP服务器不会传递任何事件。在大多数情况下,您将要在IMAPFolder上使用idle方法。

听起来这个方法并不是为了很快就能回归。在您的情况下,永远不要因为您在进入空闲状态后不向服务器发出任何命令。除此之外

  • folder.idle()可能就是你应该做的事情
  • 我认为文档错误,但ConnectionListenerMessageCountListener是两回事。