JavaMail API Gmail IMAP Folder getNewMessageCount() und getUnreadMessageCount()

Hallo,
ich bin etwas am verzweifeln, Suche im Internet (google^^) hat mich auch nicht weiter gebracht.
Es geht darum, z. B. alle 10 Minuten (ich denke, das ist nicht unfair) auf neue Mails zu prüfen,
und dabei ganz wenig / am wenigsten Traffic zu verbrauchen.

Leider ist getNewMessageCount() und getUnreadMessageCount() nicht ganz so toll dokumentiert,
und leider ist das wohl implementationsabhängig von JavaMail API sowie Gmail (API):
https://javamail.java.net/nonav/docs/api/javax/mail/Folder.html

getNewMessageCount

public int getNewMessageCount()
                       throws MessagingException

Get the number of new messages in this Folder.

This method can be invoked on a closed folder. However, note that for some folder implementations, getting the new message count can be an expensive operation involving actually opening the folder. In such cases, a provider can choose not to support this functionality in the closed state, in which case this method must return -1.

Clients invoking this method on a closed folder must be aware that this is a potentially expensive operation. Clients must also be prepared to handle a return value of -1 in this case.

This implementation returns -1 if this folder is closed. Else this implementation gets each Message in the folder using getMessage(int) and checks whether its RECENT flag is set. The total number of messages that have this flag set is returned.

Returns:
    number of new messages. -1 may be returned by certain implementations if this method is invoked on a closed folder.
Throws:
    FolderNotFoundException - if this folder does not exist.
    MessagingException - for other failures

— getUnreadMessageCount() quasi analog.

Ok, ihr möchtet (Quelltext) sehen: Folgender Versuch funktioniert NICHT:

        Properties props = new Properties();
        props.setProperty("mail.store.protocol", "imaps");
        props.put("mail.imap.ssl.trust", "imap.gmail.com");
        Session session = Session.getDefaultInstance(props);
        store = session.getStore();
        store.connect("imap.gmail.com", 993, "user name here@gmail.com", "strong password");
		folder = store.getFolder("inbox");
        folder.open(Folder.READ_WRITE);
    }```

```    private static int empfangen() throws IOException, MessagingException {
        int sum = 0;
        int unreadMessageCount = folder.getUnreadMessageCount();
        System.out.println("unreadMessageCount = " + unreadMessageCount);
        if (unreadMessageCount > 0) {
            Message[] messages = folder.getMessages();
            for (Message message : messages) {
                boolean seen = message.getFlags().contains(Flags.Flag.SEEN);
                if (!seen) {
                    String from = message.getFrom()[0].toString();
                    if (from.contains("hallo")) {
                        // do something magic...
                        sum++;
                    }
                    message.setFlag(Flags.Flag.SEEN, true);
                }
            }
        }
        return sum;
    }```

(```    private static void delete() throws MessagingException {
		folder.close(true);
        store.close();
    }```)

Folgender Versuch FUNKTIONIERT (aber suboptimal, dazu später mehr):

```    private static void init() throws MessagingException {
        Properties props = new Properties();
        props.setProperty("mail.store.protocol", "imaps");
        props.put("mail.imap.ssl.trust", "imap.gmail.com");
        Session session = Session.getDefaultInstance(props);
        store = session.getStore();
        store.connect("imap.gmail.com", 993, "user name here@gmail.com", "strong password");

    }```

```    private static int empfangen() throws IOException, MessagingException {
        int sum = 0;
        Folder folder = store.getFolder("inbox");
        folder.open(Folder.READ_WRITE);
        int unreadMessageCount = folder.getUnreadMessageCount();
        System.out.println("unreadMessageCount = " + unreadMessageCount);
        if (unreadMessageCount > 0) {
            Message[] messages = folder.getMessages();
            for (Message message : messages) {
                boolean seen = message.getFlags().contains(Flags.Flag.SEEN);
                if (!seen) {
                    String from = message.getFrom()[0].toString();
                    if (from.contains("hallo")) {
                        // do something magic...
                        sum++;
                    }
                    message.setFlag(Flags.Flag.SEEN, true);
                }
            }
        }
        folder.close(true);
        return sum;
    }```

(```    private static void delete() throws MessagingException {
        store.close();
    }```)

Was ist jetzt der Unterschied?:
(- session bleibt bestehen,)
- folder muss jedes Mal neu geöffnet werden, das könnte Traffic verbrauchen!!!!

Könnt ihr mir sagen, ob man das allgemein so machen würd, und ob folder jedes Mal neu geöffnet werden muss?


Dann noch einen Teil der Imports, falls WICHTIG:
[spoiler]```import java.util.Properties;
import javax.mail.Authenticator;
import javax.mail.Flags;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;```[/spoiler]

Edit: Ich hoffe, ich hab NICHT irgendwo eine Mailadresse oder ein Password mitkopiert... (das wäre Schrecklich...)

Update:
Nach ein paar Stunden, kommt ein BYE vom Server, das ist für session aber nicht weiter schlimm.
Und ich hab noch vergessen zu erwähnen, dass, wer das ausprobieren möchte,
muss seinen Virenscanner deaktivieren, sonst gibt’s kein Handshake. :wink:

Die Frage bleibt, wie überprüft man am trafficsparendsten auf neue Mails?

Ich hab in die Implementierung von getUnreadMessageCount geschaut,
anscheinend wird ein Search mit bestimmtem Flag vorgenommen.

Edit: Nein, ich surfe nicht ohne Virenscanner. :rolleyes:

IMAP unterstützt Server-Push.
Lies mal hier weiter: email - Does JavaMail support server-push? - Stack Overflow

:slight_smile: Nicht zu glauben, kaum zu glauben, es funktioniert: :slight_smile:

        Properties props = new Properties();
        props.setProperty("mail.store.protocol", "imaps");
        props.put("mail.imap.ssl.trust", "imap.gmail.com");
        Session session = Session.getDefaultInstance(props);
        store = session.getStore();
        store.connect("imap.gmail.com", 993, "user name here@gmail.com", "strong password");
        folder = (IMAPFolder) store.getFolder("inbox");
        folder.open(Folder.READ_WRITE);
        folder.addMessageCountListener(new MessageCountAdapter() {
            @Override
            public void messagesAdded(MessageCountEvent e) {
                proceed(e.getMessages());
            }
        });
        for (;;) {
            System.out.println("Laeuft...");
            folder.idle();
            System.out.println("messagesAdded...");
        }
    }

    private static void proceed(Message[] messages) {
        System.out.println("messages.length = " + messages.length);
        boolean proceed = false;
        try {
            for (Message message : messages) {
                boolean seen = message.getFlags().contains(Flags.Flag.SEEN);
                if (!seen) {
                    String from = message.getFrom()[0].toString();
                    // magic here
                    message.setFlag(Flags.Flag.SEEN, true);
                }
            }
        } catch (MessagingException | IOException ex) {
            System.out.println("ex = " + ex);
        }
        if (proceed) {
            try {
                Process proc = Runtime.getRuntime().exec("java -jar MagicJar.jar");
                proc.waitFor();
                //...and here
            } catch (IOException | InterruptedException | MessagingException ex) {
                System.out.println("ex = " + ex);
            }
        }
    }```

Jetzt bitte nicht den Kopf schütteln, zu:
`for (;;) {` das ist offiziell so vorgegeben, weil es nur "blockt", bis etwas Neues,
Exception-Behandlung von init()... bei einem Fehler, ist's halt ein Fehler,
Exception-Behandlung von proceed()... ja, das stimmt...

Danke an cmrudolph.

Moin,

ich bekomme ne ganz komische Ausgabe, kann nicht mal jemand schauen, was schief läuft?:

Laeuft...
messages.length = 1
messagesAdded...
Laeuft...
messagesAdded...
Laeuft...
messagesAdded...
Laeuft...
messagesAdded...
Laeuft...
messagesAdded...
Laeuft...
Fertig
Exception in thread "main" javax.mail.FolderClosedException: * BYE JavaMail Exception: java.io.IOException: Connection dropped by server?
	at com.sun.mail.imap.IMAPFolder.handleIdle(IMAPFolder.java:3199)
	at com.sun.mail.imap.IMAPFolder.idle(IMAPFolder.java:3043)
	at com.sun.mail.imap.IMAPFolder.idle(IMAPFolder.java:2995)
	at sendenempfangen.SendenEmpfangen.init(SendenEmpfangen.java:100)
	at sendenempfangen.SendenEmpfangen.main(SendenEmpfangen.java:44)

Also wenn da ne neue Message ist, wird idle() mehrmals aufgerufen…

Ich komme mit der ganzen Fehlerbehandlung nicht klar, ich hab jetzt mal Folgendes:

        Properties props = new Properties();
        props.setProperty("mail.store.protocol", "imaps");
        props.put("mail.imap.ssl.trust", "imap.gmail.com");
        Session session = Session.getDefaultInstance(props);
        store = session.getStore();
        store.connect("imap.gmail.com", 993, " @gmail.com", " ");
        folder = (IMAPFolder) store.getFolder("inbox");
        folder.open(Folder.READ_WRITE);
        folder.addMessageCountListener(new MessageCountAdapter() {
            @Override
            public void messagesAdded(MessageCountEvent e) {
                System.out.println("messagesAdded...");
                try {
                    proceed(e.getMessages());
                } catch (MessagingException | IOException | InterruptedException ex) {
                    System.out.println("ex = " + ex);
                    delete();
                    System.exit(0);
                }
            }
        });
        for (;;) {
            System.out.println("Laeuft...");
            folder.idle();
        }
    }

    private static void proceed(Message[] messages) throws MessagingException, IOException, InterruptedException {
        System.out.println("messages.length = " + messages.length);
        boolean proceed = false;
        for (Message message : messages) {
            // ...
        }
        if (proceed) {
            // ...
        }
    }

    private static void delete() {
        try {
            if (folder != null) {
                folder.close(true);
            }
            if (store != null) {
                store.close();
            }
        } catch (MessagingException ex) {
            System.out.println("ex = " + ex);
        }
    }

psvm...```

Was mache ich Falsch?