enhancement
major
minor
major
minor
major
#30030
Mail-Server-Daemon: neue Mails anhand der IMAP-UID statt des Gelesen-Flags erkennen, unbekannte Mails standardmäßig im Postfach belassen
Problem
Der Mail-Server-Daemon (AbstractMailServerDaemon) holt bei jedem Lauf Mails aus der INBOX eines IMAP-Postfachs und ordnet sie Objekten der Anwendung zu. Zwei Eigenschaften machen ihn unzuverlässig und gefährlich:
Unbekannte Mails werden ohne Konfiguration gelöscht. strategy-for-unknown-mails hat keinen Default. Die typisierte Konfiguration verwendet dann die erste Enum-Konstante, also UnknownMailStrategy.DELETE. Ein Daemon ohne explizite Strategie löscht damit stillschweigend jede Mail, die er keinem Objekt zuordnen kann – auch Mails, die andere Leser des Postfachs noch brauchen. In der Testkonfiguration mail.test.config.xml führt das dazu, dass parallel laufende Builds die Mails des jeweils anderen löschen (#30027).
Welche Mails neu sind, entscheidet das Gelesen-Flag. Mit process-all-mails="true" (Voreinstellung) verarbeitet der Daemon bei jedem Lauf alle Mails der INBOX erneut. Mit process-all-mails="false" überspringt er gelesene Mails – eine Mail, die ein Mensch oder ein anderer Client vor dem Lauf öffnet, wird nie verarbeitet. Eine Mail im Postfach zu belassen ist deshalb keine Option.
Lösung
- Verarbeitungsstand über IMAP-UIDs. Der Daemon merkt sich pro Task die UIDVALIDITY der INBOX und die UID, bis zu der alle Mails behandelt sind (UIDMark, gespeichert von UIDMarkStore als globale DBProperties-Eigenschaft mail.uid-mark.<Task-Name>). Jeder Lauf holt nur Mails mit größerer UID (UIDFolder.getMessagesByUID()), in aufsteigender UID-Reihenfolge. Das Gelesen-Flag spielt keine Rolle mehr. Ein Datum eignet sich als Grenze nicht, weil IMAP SEARCH SINCE nur tagesgenau vergleicht.
- Fortschreiben des Stands. Erst nach erfolgreichem Commit der Verarbeitung. Der Stand rückt über alle erfolgreich behandelten Mails vor und bleibt vor der ersten Mail stehen, deren Behandlung fehlschlug; diese und alle folgenden werden im nächsten Lauf erneut gelesen. Erfolgreich verarbeitete Mails haben die INBOX dann bereits verlassen (sie werden wie bisher in den Ordner ihres Objekts verschoben). Schlägt der Commit fehl oder ist die UID einer Mail nicht lesbar, bleibt der Stand unverändert. Eine Mail wird damit höchstens erneut verarbeitet, nie übersprungen.
- Erster Lauf. Ist kein Stand gespeichert oder hat sich die UIDVALIDITY geändert, entscheidet die neue Option initial-mails (InitialMails): SKIP (Voreinstellung) setzt den Stand auf UIDNEXT - 1 und verarbeitet nur danach eingehende Mails, PROCESS verarbeitet auch die bereits vorhandenen. Meldet der Server kein UIDNEXT, gilt die UID der letzten Mail im Ordner, bei leerem Ordner 0. Der initiale Stand wird sofort gespeichert.
- Neue Strategie KEEP für unbekannte Mails: die Mail bleibt unverändert in der INBOX. KEEP ist die neue Voreinstellung von strategy-for-unknown-mails; DELETE und MOVE bleiben verfügbar.
- Die Option process-all-mails entfällt (auch in der Demo- und der Testkonfiguration).
- Die Mail-Server-Tests (TestMailMultipart, TestMailAttachment) lassen den Daemon einmal laufen, bevor sie eine Mail senden (MailTestUtils.initMailServerDaemon()), damit der Stand vor ihrer Mail gesetzt ist.
Migration
- Die Option process-all-mails des Mail-Server-Daemons entfällt und muss aus der Konfiguration entfernt werden; ein verbliebenes Attribut führt zu einem Konfigurationsfehler beim Start.
- Ohne Angabe von strategy-for-unknown-mails bleiben unbekannte Mails jetzt in der INBOX (KEEP). Anwendungen, die sich auf das bisherige Löschen verlassen, setzen strategy-for-unknown-mails="DELETE" explizit.
- Beim ersten Lauf nach dem Update verarbeitet der Daemon nur Mails, die danach eingehen (initial-mails="SKIP"). Sollen bereits vorhandene Mails einmalig verarbeitet werden, ist initial-mails="PROCESS" zu setzen.
Test
- TestUIDMark: Startstand (ohne Stand, mit/ohne UIDNEXT, leerer Ordner, geänderte UIDVALIDITY, SKIP/`PROCESS`), Fortschreiben (alle erfolgreich, Fehler in der Mitte, Fehler bei der ersten Mail, keine Mails), externe Form.
- TestUIDMarkStore: Speichern und Lesen in DBProperties, getrennte Stände pro Task, ungültiger Wert.
- TestMailServerDaemonConfig: Voreinstellungen KEEP und SKIP, process-all-mails wird abgelehnt.
- Die Tests gegen den Mail-Server (TestMailMultipart, TestMailAttachment) laufen nur auf CI.