Ab die Post (Teil 3)

Wer kennt diese Situation als Software Entwickler nicht. Man hat eine nette kleine Idee und statt sie direkt zu implementieren, bespricht man sie mit einem Projektverantwortlichen. Plötzlich wächst die Idee zu irgendetwas heran, das ohne wirklich weiteren Wert zu bieten, mehrere Tage bis Wochen Vorbereitung und Implementierung benötigt. Als junger Entwickler hörte ich, im Zuge einer eigenen kleinen Idee, zum aller ersten Mal von den U-Booten.

Die U-Boote sind nützliche Tools, die von Entwicklern implementiert werden, um Defizite der verwendeten Frameworks zu umgehen, Automatisierung in die antike Entwicklungslandschaft zu bringen oder einfach das tägliche Arbeiten zu beschleunigen. Der Name beruht auf der Tatsache, dass all diese Tools für Außenstehende nicht zu entdecken sind, wie U-Boote schwimmen sie unter der Oberfläche der Projektpläne.

Natürlich sollte es aus diversen Gründen keine U-Boote geben, es enstehen für die Projektleitung unbekannte Aufwände, die Tools können ein unbewertetes Risiko für den Projektverlauf darstellen, Qualität und Wartung hängen von den Launen eines einzelnen Entwicklers ab. U-Boote entstehen, wenn die Notwendigkeit für ein Tool von der Projektleitung nicht verstanden wird und die Entwickler in den Spagat gezwungen werden, Projektplan und Realität eigenständig abzugleichen.

Bei dem Experimenten mit dem GreenMail Framework, war die Idee recht schnell geboren, den SMTP Server mit einer REST Schnittstelle zu verbinden. Auf diese Art wäre es möglich, über einen einfachen REST Aufruf, den Erfolg des Mail Versands zu überprüfen. In der Integrationsumgebung des Stammbaum Dienstes werden keine ausgehenden E-Mails getestet. Um diese Lücke zu schließen, könnte eine passende Spring Anwendung schnell erstellt werden.

@SpringBootApplication
@EnableConfigurationProperties
public class GreenmailServiceApplication {
  @Bean
  public GreenMail greenMail(GreenMailProperties properties) {
    Smtp smtp = properties.getSmtp();
    ServerSetup serverSetup = new ServerSetup(smtp.getPort(), null, "smtp");
    GreenMail greenMail = new GreenMail(new ServerSetup[] { serverSetup});
    greenMail.start();
    return greenMail;
  }

  public static void main(String[] args) {
    SpringApplication.run(GreenmailServiceApplication.class, args);
  }
}

Die Klassen GreenMailProperties und Smtp sind eigene POJOs und bei der Verwendung des SMTP Standard-Port 25 unnötig.

Bei dieser Anwendung fehlt nur noch die REST Schnittstelle zu Prüfung der E-Mails, die an den SMTP Server übermittelt wurden. Die einfachste Variante im Spring Umfeld ist ein eigener Actuator.

@Component
@Endpoint(id = "greenmail")
public class GreenMailEndpoint {

  private final GreenMail greenMail;

  public GreenMailEndpoint(GreenMail greenMail) {
    this.greenMail = greenMail;
  }

  @ReadOperation
  Map<String, Set<Mail>> all() {
    return getMessages().map(Mail::new).collect(groupingBy(Mail::getFrom, toSet()));
  }

  @DeleteOperation
  void delete() throws FolderException {
    greenMail.purgeEmailFromAllMailboxes();
  }

  @ReadOperation
  Map<String, List<Mail>> byFrom(@Selector String from) {
    return getMessages().map(Mail::new).filter(m -> from.equals(m.getFrom()))
        .collect(groupingBy(Mail::getMessageId, toList()));
  }

  private Stream<MimeMessage> getMessages() {
    return ofNullable(greenMail.getReceivedMessages()).flatMap(Arrays::stream);
  }
}

Der Actuator kann über den Endpoint /actuator/greenmail angesprochen werden und liefert dann mit GET eine Map aller E-Mails. Dazu wird die mit @ReadOperation annotierte Methode all() aufgerufen. Der Schlüssel in der Map ist der Versender der E-Mail.

{  
  "import@schegge.de": [
    {
      "from": "import@schegge.de",
      "subject": "Stammbaum-Import ",
      "messageId": "<message-id-1>",
      "cc": [
 "quality@schegge.de"
 ],
      "to": [
 "export@ahnen.de"
 ]
    },
    {
      "from": "import@schegge.de",
      "subject": "Stammbaum-Prüfung",
      "messageId": "<message-id-2>",
      "cc": [
 "quality@schegge.de" ],
      "to": [
 "export@ahnen.de", "quality@ahnen.de"
 ]
    }
  ]
}

Diese Gruppierung macht vielleicht nicht in jedem Szenario Sinn, aber zum Testen der ausgehenden E-Mails für den Import von Stammbaum-Dateien ist sie hinreichend.

Über den gleichen Endpoint können mit DELETE auch alle E-Mails gelöscht werden, dazu wird die delete() Methode aufgerufen, die mit @DeleteOperation annotiert ist.

Zusätzlich existiert noch der Endpoint /actuator/greenmail/{from}. Hier wird die Methode byFrom() aufgerufen, die mit @ReadOperation annotiert ist. Sie liefert für einen Versender alle E-Mails in einer Map, mit der MessageId als Schlüssel. Dies scheint erst einmal wenig Sinn zu machen, da es nur eine E-Mail zu einer MessageId geben sollte.

Die Erklärung ist recht einfach, es sind die Kopien der E-Mail, die an die verschiedenen Empfänger TO, CC und BCC gehen. Wieviele BCC Empfänger die E-Mail erhalten haben, kann auch nur anhand der Zahl der E-Mails festgestellt werden. Ihre E-Mail Adresse kommt, im Gegensatz zu TO und CC, nicht in der gespeicherten E-Mail vor.

Der hier vorgestellte Spring Boot Service ist in kurzer Zeit entstanden und wäre ein klassisches Beispiel eines U-Bootes. Aber in der agilen Software Entwicklung ist die Existenz von U-Booten obsolete geworden, weil solche Tools nicht mehr im Geheimen entwickelt werden müssen. Entdecken die Entwickler Probleme, dann werden im Team Lösungen erarbeitet und diese allen Projektbeteiligten vorgestellt.