2010-11-11 65 views
13

我正在製作一個程序,需要監視Gmail帳戶中的新郵件,爲了儘快獲得它們,我正在使用JavaMail的閒置功能。這裏是從線程的代碼片段,我使用調用folder.idle():JavaMail:保持IMAPFolder.idle()存活

//Run method that waits for idle input. If an exception occurs, end the thread's life. 
public void run() { 

    IMAPFolder folder = null; 

      try { 
       folder = getFolder(); 
       while(true) 
       { 
        //If connection has been lost, attempt to restore it 
        if (!folder.isOpen()) 
         folder = getFolder(); 
        //Wait until something happens in inbox 
        folder.idle(true); 
        //Notify controller of event 
        cont.inboxEventOccured(); 
       } 
      } 
      catch (Exception ex) { 
       ex.printStackTrace(); 
      } 
      System.out.println("MailIdleWaiter thread ending."); 
} 

的的GetFolder()方法基本上打開到IMAP服務器的連接,並打開收件箱。

這工作了一段時間,但10分鐘左右後,它停止獲取更新(不會引發異常)。

我在尋找建議,以保持連接活着。我是否需要第二個線程,它的唯一作用是每10分鐘休眠一次並更新idle()線程,還是有更簡單/更好的方法?

在此先感謝。

+0

我打算也這樣做。你終於能夠解決這個問題了嗎?目前,我每15秒通過'folder.open/folder.close'輪詢文件夾,但IDLE當然會更好。我打算在應用服務器環境中使用它。 – Theo 2011-07-20 13:48:01

+0

對不起,沒有更快發現你的評論。我最終放棄了這個項目,所以我從未接近過解決方案。但現在,這個線程有一個答案,也許這將工作......雖然它是基於投票,而不是閒置。 – Anders 2011-08-13 15:39:50

+0

您必須同時進行投票和閒置才能正確執行此操作。根據規範,IDLE必須每半小時終止並更新一次,如果一箇中斷的NATbox正在運行,則更經常地更新。正確的間隔是......好吧,也許沒有一個正確的價值。 – arnt 2014-06-10 07:09:31

回答

-6

檢查郵件數每5分鐘對我的作品:

new Thread() 
{ 
    @Override 
    public void run() 
    { 
     startTimer(); 
    } 
    private void startTimer() 
    { 
     int seconds = 0; 
     while (true) 
     { 
      try 
      { 
       Thread.sleep(300000); 
       int c = folder.getMessageCount();  
      } 
      catch (InterruptedException ex) 
      { 
      } 
      catch (MessagingException me) 
      { 
      } 
     } 
    } 
}.start(); 
+4

這是一個輪詢循環而不是閒置。它對於服務器和帶寬使用都是低效的,並且不能獲得即時通知。 – WhyNotHugo 2012-03-22 05:13:48

+0

另外,請不要使用空的catch塊。即使你確定它不會發生(不是這種情況),最好記錄它。 – sargue 2015-07-20 16:08:06

3

該建議由@ user888307是一個骯髒的黑客,最終慘遭失敗。真的只有一個適當的方法來做到這一點。

在當前所選文件夾上調用空閒(false)方法。理想情況下收件箱,因爲它會收到所有消息。

調用idle(false)基本上會掛起線程的運行時間,所以最好將idle(false)放在新線程上。然後,一旦您使用messageCountChange收到新的電子郵件/通知,您必須重新運行此線程。

這是實現這一目標的唯一方法。在寫一個名爲JavaPushMail的程序時,我爲您的顯式問題編寫了一個包裝。你可以找到我的網站上(http://www.mofirouz.com/wordpress)更多信息,或者您可以在GitHub上https://github.com/mofirouz/JavaPushMail

+0

有兩個問題:1)messageCountChange在相同的閒置線程上還是在新的線程上調用? 2)你如何更新閒置以防止超時?謝謝。 – Patricio 2014-03-01 20:14:02

+0

[Javadoc中(https://javamail.java.net/nonav/docs/api/com/sun/mail/imap/IMAPFolder.html#idle()) @Mo你真的不提供答案這個問題。你給一個github回購和wordpress博客的鏈接。調用閒置()和閒置(假)都將掛在運行時,需要對自己的線程上運行。你也說「上面的建議」,但由於對S.O.被投票不能保證上述答案會保持在上面。 – gdoubleod 2014-04-21 21:22:43

+0

我使用@Mo工作,這太神奇了。幹得好:) – jechaviz 2014-10-28 17:28:14

21

一個常見的錯誤是假設一個IDLE命令將無限期地發佈更新抓取應用程序(目前正在開發)。然而,RFC 2177,定義空閒分機的狀態:

服務器可以考慮客戶端不活動的,如果它有一個IDLE命令 運行,如果這樣的服務器有一個閒置超時就可以登錄 客戶端關閉隱含在超時期結束時。因爲 ,因此建議使用IDLE的客戶端終止IDLE,並至少每隔29分鐘重新發出一次,以避免被註銷。 儘管它只需要在半小時間隔內進行「輪詢」,但它仍允許客戶端即時收到即時郵箱更新 。

特別是,GMail的超時時間比你說的大約低10分鐘左右。

我們只需要每隔9分鐘左右重新發出一次空閒命令以使其工作。 javax.mail API無法爲IDLE命令設置超時,因此您需要第二個線程來移動此設置。

第一種方法是讓第二個線程中斷第一個線程,處理異常並忽略它。然而,這將允許沒有乾淨的方式來關閉線程,所以我不會推薦它。更簡潔的方法是讓第二個線程向服務器發出NOOP命令。這完全沒有作用,但足以讓IDLE中止並重新發布。

我在這裏提供一些代碼來做到這一點:

public void startListening(IMAPFolder imapFolder) { 
    // We need to create a new thread to keep alive the connection 
    Thread t = new Thread(
     new KeepAliveRunnable(imapFolder), "IdleConnectionKeepAlive" 
    ); 

    t.start(); 

    while (!Thread.interrupted()) { 
     LOGGER.debug("Starting IDLE"); 
     try { 
      imapFolder.idle(); 
     } catch (MessagingException e) { 
      LOGGER.warn("Messaging exception during IDLE", e); 
      throw new RuntimeException(e); 
     } 
    } 

    // Shutdown keep alive thread 
    if (t.isAlive()) { 
     t.interrupt(); 
    } 
} 

/** 
* Runnable used to keep alive the connection to the IMAP server 
* 
* @author Juan Martín Sotuyo Dodero <[email protected]> 
*/ 
private static class KeepAliveRunnable implements Runnable { 

    private static final long KEEP_ALIVE_FREQ = 300000; // 5 minutes 

    private IMAPFolder folder; 

    public KeepAliveRunnable(IMAPFolder folder) { 
     this.folder = folder; 
    } 

    @Override 
    public void run() { 
     while (!Thread.interrupted()) { 
      try { 
       Thread.sleep(KEEP_ALIVE_FREQ); 

       // Perform a NOOP just to keep alive the connection 
       LOGGER.debug("Performing a NOOP to keep alvie the connection"); 
       folder.doCommand(new IMAPFolder.ProtocolCommand() { 
        public Object doCommand(IMAPProtocol p) 
          throws ProtocolException { 
         p.simpleCommand("NOOP", null); 
         return null; 
        } 
       }); 
      } catch (InterruptedException e) { 
       // Ignore, just aborting the thread... 
      } catch (MessagingException e) { 
       // Shouldn't really happen... 
       LOGGER.warn("Unexpected exception while keeping alive the IDLE connection", e); 
      } 
     } 
    } 
} 
+1

在您使用的ExecutorService的(特別是在JEE)情況下,您可能需要使用的Object.wait(),而不是Thread.sleep()方法,所以你也可以中斷它,而不在線程的掌控之中。 – 2015-09-23 07:17:34

3

其實Java Mail samples包括IMAP IDLE爲例,這是如下。 除此之外,IdleManager class可能會很有趣。

/* 
* Copyright (c) 1996-2010 Oracle and/or its affiliates. All rights reserved. 
* 
* Redistribution and use in source and binary forms, with or without 
* modification, are permitted provided that the following conditions 
* are met: 
* 
* - Redistributions of source code must retain the above copyright 
*  notice, this list of conditions and the following disclaimer. 
* 
* - Redistributions in binary form must reproduce the above copyright 
*  notice, this list of conditions and the following disclaimer in the 
*  documentation and/or other materials provided with the distribution. 
* 
* - Neither the name of Oracle nor the names of its 
*  contributors may be used to endorse or promote products derived 
*  from this software without specific prior written permission. 
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/ 

import java.util.*; 
import java.io.*; 
import javax.mail.*; 
import javax.mail.event.*; 
import javax.activation.*; 

import com.sun.mail.imap.*; 

/* Monitors given mailbox for new mail */ 

public class monitor { 

    public static void main(String argv[]) { 
    if (argv.length != 5) { 
     System.out.println(
     "Usage: monitor <host> <user> <password> <mbox> <freq>"); 
     System.exit(1); 
    } 
    System.out.println("\nTesting monitor\n"); 

    try { 
     Properties props = System.getProperties(); 

     // Get a Session object 
     Session session = Session.getInstance(props, null); 
     // session.setDebug(true); 

     // Get a Store object 
     Store store = session.getStore("imap"); 

     // Connect 
     store.connect(argv[0], argv[1], argv[2]); 

     // Open a Folder 
     Folder folder = store.getFolder(argv[3]); 
     if (folder == null || !folder.exists()) { 
     System.out.println("Invalid folder"); 
     System.exit(1); 
     } 

     folder.open(Folder.READ_WRITE); 

     // Add messageCountListener to listen for new messages 
     folder.addMessageCountListener(new MessageCountAdapter() { 
     public void messagesAdded(MessageCountEvent ev) { 
      Message[] msgs = ev.getMessages(); 
      System.out.println("Got " + msgs.length + " new messages"); 

      // Just dump out the new messages 
      for (int i = 0; i < msgs.length; i++) { 
      try { 
       System.out.println("-----"); 
       System.out.println("Message " + 
       msgs[i].getMessageNumber() + ":"); 
       msgs[i].writeTo(System.out); 
      } catch (IOException ioex) { 
       ioex.printStackTrace(); 
      } catch (MessagingException mex) { 
       mex.printStackTrace(); 
      } 
      } 
     } 
     }); 

     // Check mail once in "freq" MILLIseconds 
     int freq = Integer.parseInt(argv[4]); 
     boolean supportsIdle = false; 
     try { 
     if (folder instanceof IMAPFolder) { 
      IMAPFolder f = (IMAPFolder)folder; 
      f.idle(); 
      supportsIdle = true; 
     } 
     } catch (FolderClosedException fex) { 
     throw fex; 
     } catch (MessagingException mex) { 
     supportsIdle = false; 
     } 
     for (;;) { 
     if (supportsIdle && folder instanceof IMAPFolder) { 
      IMAPFolder f = (IMAPFolder)folder; 
      f.idle(); 
      System.out.println("IDLE done"); 
     } else { 
      Thread.sleep(freq); // sleep for freq milliseconds 

      // This is to force the IMAP server to send us 
      // EXISTS notifications. 
      folder.getMessageCount(); 
     } 
     } 

    } catch (Exception ex) { 
     ex.printStackTrace(); 
    } 
    } 
}