2010-11-11 36 views
13

Tôi đang tạo một chương trình cần giám sát tài khoản Gmail cho thư mới và để có được chúng càng sớm càng tốt Tôi đang sử dụng tính năng nhàn rỗi của JavaMail. Đây là đoạn mã từ chuỗi mà tôi đang sử dụng để gọi thư mục folder.idle():JavaMail: Giữ IMAPFolder.idle() còn sống

//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."); 
} 

Phương thức getFolder() về cơ bản sẽ mở kết nối đến máy chủ IMAP và mở hộp thư đến.

Điều này hoạt động trong một thời gian, nhưng sau 10 phút hoặc lâu hơn, nó sẽ không nhận được cập nhật (không có ngoại lệ được ném).

Tôi đang tìm các đề xuất để giữ kết nối luôn hoạt động. Tôi có cần một chủ đề thứ hai chỉ có vai trò là ngủ và làm mới nhàn rỗi() thread mỗi 10 phút hoặc là có một cách dễ dàng hơn/tốt hơn?

Xin cảm ơn trước.

+0

Tôi đang lên kế hoạch thực hiện tương tự. Bạn cuối cùng có thể giải quyết vấn đề này không? Hiện tại, tôi đang bỏ phiếu thư mục qua 'folder.open/folder.close' cứ 15 giây một lần, nhưng IDLE sẽ tốt hơn. Tôi đang lập kế hoạch để sử dụng điều này trong một môi trường máy chủ ứng dụng. – Theo

+0

Xin lỗi vì không nhận được bình luận của bạn sớm hơn. Tôi đã kết thúc việc bỏ dự án, vì vậy tôi chưa bao giờ đến gần một giải pháp. Nhưng bây giờ mà chủ đề này có một câu trả lời, có thể điều đó sẽ làm việc ... mặc dù nó dựa trên bỏ phiếu và không nhàn rỗi. – Anders

+0

Bạn có cả hai cuộc thăm dò ý kiến ​​và được nhàn rỗi để làm điều đó đúng. IDLE phải được chấm dứt và gia hạn mỗi nửa giờ theo thông số kỹ thuật và thường xuyên hơn nếu một NATbox bị hỏng đang cản trở. Khoảng thời gian phù hợp là ... tốt, có thể không có Giá trị chính xác. – arnt

Trả lời

-6

kiểm tra được thông báo đếm mỗi 5 phút làm việc cho tôi:

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

Đây là vòng lặp cuộc thăm dò thay vì nhàn rỗi. Nó không hiệu quả, cho cả máy chủ và sử dụng băng thông, cũng như không nhận được thông báo tức thì. – WhyNotHugo

+0

Ngoài ra, vui lòng KHÔNG BAO GIỜ sử dụng khối catch trống. Ngay cả khi bạn SURE nó sẽ không xảy ra (không phải trường hợp này) nó tốt hơn để đăng nhập nó. – sargue

3

Các gợi ý bởi @ user888307 là một bẩn hack và cuối cùng thất bại thảm hại. Có thực sự chỉ có một cách thích hợp để làm điều này.

Gọi phương thức nhàn rỗi (sai) trên thư mục hiện được chọn. Lý tưởng nhất là Inbox vì sẽ nhận được tất cả thư.

Gọi điện không hoạt động (sai) về cơ bản sẽ treo thời gian chạy của chuỗi, vì vậy tốt hơn nên đặt chế độ chờ (sai) trên một chuỗi mới. Sau đó, khi bạn nhận được một email/thông báo mới bằng cách sử dụng messageCountChange, bạn phải chạy lại luồng này.

Đây là cách thực sự duy nhất để đạt được điều này. Tôi đã viết một wrapper cho vấn đề rõ ràng của bạn khi tôi đang viết một chương trình gọi là JavaPushMail. Bạn có thể tìm thêm thông tin trên trang web của tôi (http://www.mofirouz.com/wordpress) hoặc bạn có thể lấy ứng dụng (hiện đang được phát triển) trên GitHub https://github.com/mofirouz/JavaPushMail

+0

Hai câu hỏi: 1) là messageCountChange được gọi trên cùng một chuỗi bị lỗi hoặc trên một chuỗi mới? 2) làm cách nào để bạn gia hạn chế độ chờ để ngăn thời gian chờ? Cảm ơn. – Patricio

+0

[The javadoc] (https://javamail.java.net/nonav/docs/api/com/sun/mail/imap/IMAPFolder.html#idle()) @Bạn không thực sự cung cấp câu trả lời cho câu hỏi. Bạn đưa ra một liên kết đến một repo github và một blog wordpress. Gọi nhàn rỗi() và nhàn rỗi (false) sẽ cả hai treo thời gian chạy và cần phải được chạy trên thread của riêng mình. Bạn cũng nói "gợi ý ở trên" nhưng vì có bầu chọn trên S.O. không có gì đảm bảo rằng câu trả lời ở trên sẽ vẫn ở trên. – gdoubleod

+0

Tôi sử dụng công việc @Mo, thật tuyệt vời. Tốt công việc :) – jechaviz

21

Lỗi thường gặp là giả sử lệnh IDLE sẽ tiếp tục đăng cập nhật vô thời hạn. Tuy nhiên, RFC 2177, xác định các trạng thái mở rộng IDLE:

máy chủ có thể xem xét một khách hàng không hoạt động nếu nó có một IDLE lệnh chạy, và nếu một máy chủ đó có một thời gian chờ không hoạt động nó CÓ THỂ đăng nhập client tắt hoàn toàn vào cuối khoảng thời gian chờ của nó. Bởi vì trong số đó, khách hàng sử dụng IDLE nên chấm dứt IDLE và cấp lại ít nhất 29 phút một lần để tránh bị đăng xuất. Điều này vẫn cho phép khách hàng nhận cập nhật hộp thư ngay lập tức ngay cả mặc dù nó chỉ cần "cuộc thăm dò ý kiến" trong khoảng thời gian nửa giờ.

GMail nói riêng, có thời gian chờ thấp hơn nhiều, như bạn nói, khoảng 10 phút.

Chúng tôi chỉ cần phát hành lại lệnh IDLE sau mỗi 9 phút để chương trình hoạt động. Các API javax.mail không có cách nào để đặt thời gian chờ cho lệnh IDLE, vì vậy bạn sẽ cần một chuỗi thứ hai để di chuyển xung quanh.

Cách tiếp cận đầu tiên sẽ là ngắt luồng thứ hai trong chuỗi thứ nhất, xử lý ngoại lệ và bỏ qua nó. Điều này tuy nhiên, sẽ cho phép không có cách nào để tắt thread, vì vậy tôi sẽ không khuyên bạn nên nó.Một cách gọn gàng hơn là để thread thứ hai đưa ra một lệnh NOOP tới máy chủ. Điều này không có gì cả, nhưng là đủ để có IDLE hủy bỏ và được phát hành lại.

Tôi đây cung cấp một số mã để làm điều này:

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

Trong một kịch bản mà bạn sử dụng một ExecutorService (đặc biệt trong JEE), bạn có thể muốn sử dụng Object.wait() chứ không phải Thread.sleep() để bạn có thể ngắt nó cũng không được kiểm soát của Thread . –

3

Trên thực tế các Java Mail samples bao gồm một ví dụ IDLE IMAP, mà là như sau. Bên cạnh đó, IdleManager class có thể thú vị.

/* 
* 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(); 
    } 
    } 
} 
Các vấn đề liên quan