Micro ohne Server

Von Dirk Weil, GEDOPLAN IT Training.

Der Trend – und vielleicht auch das Unwort – der jüngeren Vergangenheit lautet „Microservices“. Unabhängig von weiteren Beweggründen taucht hier häufig der Wunsch nach einem anderen Betriebskonzept als dem klassischen Application Server auf.
Von Dirk Weil
Am Anfang des Jahrtausends war das Vorgehen für Entwicklung und Betrieb von Enterprise-Anwendungen durch den Standard J2EE geprägt. Die technische Plattform bildete ein J2EE-Server wie BEA WebLogic oder JBoss auf einer für die Zielumgebung passenden Java VM. Die Application Server waren damals recht schwergewichtig, d. h. sie luden alle Subsysteme wie Persistenz, Messaging etc. in den Speicher und initialisierten diese auch, unabhängig davon, ob sie von den Anwendungen benötigt wurden. Technisch waren (und sind) die Server in der Lage, mehrere Anwendungen gleichzeitig zu betreiben, selbst wenn diese keinerlei Abhängigkeiten oder Bezüge untereinander hatten.

Die Server konnten also eine Art Betriebssytem für Enterprise-Anwendungen gesehen werden, allerdings mit geringerer Isolation der Anwendungen untereinander: Eine Fehlfunktion der einen Anwendug könnte im ungünstigen Fall auch die restlichen Anwendungen beeinträchtigen. Da dies in den wenigsten Fällen akzeptabel war, wurde die Mehr-Anwendungs-Fähigkeit der Server selten ausgenutzt und stattdessen pro Anwendung ein Server betrieben.

Die Anwendungen wiederum waren monolithisch aufgebaut, d. h. sie enthielten verschiedene fachliche Bereiche in einer großen Anwendung. Dieses Modell ist einfach, hat aber den Nachteil, dass Änderungen einer fachlichen Komponente immer eine Änderung der Gesamtanwendung bedeuten. Zudem kann selbst eine für den Gesamtbetrieb eher unwichtige Komponente nicht vorübergehend bspw. zu Wartungszwecken ausser Betrieb genommen werden, ohne die Gesamtanwendung stoppen zu müssen.
Um in diesem Punkt mehr Flexibilität zu erreichen, kann man die Idee der Microservices einbeziehen: Statt großer, monolithischer Anwendungen werden abgegrenzte, kleine Anwendungen betrieben, jeweils für einen bestimmten, zusammenhängenden Bereich und einzeln änderbar, deploybar etc. Der Preis für die höhere Flexibiliät und geringere Kopplung der Anwendungsteile ist eine höhere Komplexität der Gesamtanwendung und ein erhöhter Kommunikations- und Synchronisationsbedarf. Das soll aber im Rahmen dieses Artikels nicht weiter betrachtet werden.
Es ist wichtig zu erkennen, dass zur Einführung von Microservices kein neues Programmiermodell eingeführt werden muss: Die Plattform Java EE enthält neben den bewährten Teilstandards für Persistenz (JPA) oder Komponentenverwaltung (CDI) auch gute Kommunikationsmöglichkeiten für Anwendungen wie Messaging (JMS) oder Webservices (JAX-RS), um nur einen kleinen Ausschnitt zu nennen. Microservices können also weiterhin Java-EE-Anwendungen sein, die aus den genannten Gründen voraussichtlich auf jeweils einem Java-EE-Server betrieben werden.

Pro Microservice einen Application Server vorzuhalten ist aber natürlich eine relativ aufwändige Angelegenheit, zumal jeder Server mehr oder weniger speziell für seinen Service konfiguriert werden muss (Datasources, Messaging, …). Zudem befinden sich Server und Service sogar in zyklischer Abhängigkeit voneinander, so dass sie im Architektursinn eine einzelne Komponente bilden. Diese beiden Überlegungen führen zu der Idee, Server und Service zu einer Anwendung zusammen zu binden. Statt also einen Server zu installieren und zu konfigurieren, um den Service dann darauf zu deployen, wird eine einfache SE-Anwendung betrieben, die den Server in eingebetteter Form enthält.

Man könnte nun in Bastellaune geraten und die Einbettung des Serveranteils in den Service selbst machen: Man nehme einen Webcontainer (z. B. Jetty), einen Injektionscontainer (z. B. Weld), eine Transaktionssteuerung (z. B. Arjuna), einen Persistenzprovider (z. B. Hibernate) inkl. Second Level Cache (z. B. Infinispan) etc. etc. etc. und klebe das geschickt untereinander und mit den restlichen Anwendungsklassen zusammen. Auch wenn’s vielleicht Spaß macht: Das ist ebenso aufwändig wie fehlerträchtig und somit als professionelle Vorgehensweise kaum zu empfehlen.


WildFly Swarm bietet hier eine wesentlich günstigere Möglichkeit für die Komposition von Service-Anwendungen: Man greift auf bewährte Bestandteile des Application Servers WildFly zurück, was die Integration untereinander einschließt. Es steht eine große Menge sog. Fractions zur Verfügung, aus denen man einfach die benötigten auswählt und sie als normale Libraries mit der restlichen Anwendung zusammen packt.  Will man bspw. einen Webservice auf Basis von JAX-RS, CDI und JPA erstellen, nutzt man die folgenden Maven Dependencies:


<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.wildfly.swarm</groupId>
      <artifactId>bom-all</artifactId>
      <version>2017.5.0</version>
      <type>pom</type>
      <scope>import</scope>
     </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <dependency>
    <groupId>org.wildfly.swarm</groupId>
    <artifactId>cdi</artifactId>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>org.wildfly.swarm</groupId>
    <artifactId>jaxrs</artifactId>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>org.wildfly.swarm</groupId>
    <artifactId>jpa</artifactId>
    <scope>provided</scope>
  </dependency>
  <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>1.3.173</version>
    <scope>runtime</scope>
  </dependency>

Anders als im klassischen WildFly-Server ist in WildFly Swarm kein Treiber für die H2-Datenbank enthalten, sodass die entsprechende Dependency explizit angegeben werden muss.
Die Anwendung aus herkömmlichen Java-EE-Bestandteilen (JAX-RS-Ressourcen, CDI Beans, EntityManager etc.) kann dann z. B. mit der folgenden main-Methode gestartet werden:


public class SwarmBootstrap {
  public static void main(String[] args) throws Exception {
    System.setProperty("swarm.context.path", "micro-ohne-server");
    Swarm swarm = new Swarm();
    URL standaloneFullXml = SwarmBootstrap.class
      .getClassLoader()
      .getResource("configuration/standalone-full.xml");
    swarm
      .withXmlConfig(standaloneFullXml)
      .start()
      .deploy();


Das Beispiel nutzt zwei Konfigurationsmöglichkeiten: Zum einen lassen sich die WildFly Fractions teilweise mit System Properties einstellen (hier: Context Path des Webcontainers), zum anderen steht ein recht umfangreiches API zur Verfügung. Im Beispiel wird die vom WildFly Server bekannte Konfigurationsdatei standalone-full.xml verwendet.
Mit dem gezeigten Code kann die Anwendung bereits innerhalb einer IDE wie Eclipse gestartet und gestestet werden. Für die Paketierung als ausführbares Jar File steht ein Maven Plugin zur Verfügung, mit dem die Anwendungsklassen mit den WildFly-Swarm-Klassen zu einem sog. Uber Jar gepackt werden können:


<plugin>
  <groupId>org.wildfly.swarm</groupId>
  <artifactId>wildfly-swarm-plugin</artifactId>
  <version>2017.5.0</version>
  <configuration>
    <mainClass>de.gedoplan.….SwarmBootstrap</mainClass>
  </configuration>
  <executions>
    <execution>
      <goals>
        <goal>package</goal>
      </goals>
    </execution>
  </executions>
</plugin>


In der package-Phase des Maven-Builds wird nun zusätzlich zur herkömmlichen Webanwendung xyz.war ein Uber Jar namens xyz-swarm.jar erstellt, das mit java –jar xyz-swarm.jar gestartet werden kann.
Über das gezeigte hinaus bietet WildFly Swarm einige Besonderheiten und Zusätze, die hier nicht weiter ausgeführt werden können. So gibt es bspw. einen Project Configurator, mit dem interaktiv eine Startversion eines Swarm-Projektes erstellt werden kann. Das Angebot an verfügbaren Fractions geht weit über die Unterstützung des Java-EE-Standards hinaus und umfasst bspw. Camel, Flyway, EclipseLink, Hystrix, LogStash, Keycloak und Modcluster, um nur einige zu nennen. Statt eines Uber Jars, das die komplette Anwendung enthält, kann auch ein Hollow Jar erzeugt werden, das nur die anwendungsspezifischen Klassen enthält. Details zu diesen und weiteren Features finden Sie in der Dokumentation (Link s. u.).
Es ist sehr angenehm, dass man für die Benutzung von WildFly Swarm das normale Java-EE-Programmiermodell nicht verlassen muss. Einerseits kann man sein bisher aufgebautes Know-how weiter verwenden. Andererseits ist es bspw. möglich, die Anwendung in der Entwicklungsumgebung auf einem WildFly-Server zu betreiben und zu testen. Der Start der Beispielanwendung mit WildFly Swarm ist zwar mit etwa 10 Sekunden – natürlich abhängig von der verwendeten Hardware und dem Umfang der Anwendung – nicht wirklich langsam, aber ein Redeployment auf einen WildFly-Server benötigt nur etwa 3 Sekunden … Und schließlich bleibt so die Entscheidung für den Betrieb als Uber Jar revidierbar, wenn für einen anderen Kunden, eine andere Abteilung, ein anderes Team doch wieder ein klassischer Server zum Einsatz kommen soll.
WildFly Swarm ist nicht die einzige Software, die die Erstellung von sog. Self Contained Services erlaubt. Als Alternativen sind hier Payara Micro und KumuluzEE zu nennen, beide allerdings mit im Vergleich mehr oder weniger deutlich geringerem Umfang.


Das Beispielprojekt finden Sie in hier. Es nutzt verschiedene Maven-Profile für den Betrieb als WildFly-Swarm-Anwendung oder als klassische Server-Anwendung. Zudem werden auch Payara Micro und KumuluzEE berücksichtigt. Die entsprechenden Build- und Run-Skripte finden Sie im Startverzeichnis des Projektes.


Die Homepage von WildFly Swarm. ist

Zur Dokumentation sein zudem der User’s Guide empfohlen.

Bei Fragen wenden Sie sich gerne an mich!.

Unsere Kundenzeitschrift GEDOPLAN aktuell

In dieser Rubrik stellen wir Ihnen einen Artikel aus der aktuellen Ausgabe unserer Kundenzeitschrift zum Online-Lesen zur Verfügung. Alle weiteren Ausgabe zum Download finden Sie hier.

Aktuelle Kurse zum Thema:

Entwicklung und Betrieb von Anwendungen auf WildFly 10
* Nächster Termin: 22.11. - 24.11.2017 in Berlin.

Java EE 7 Intensivkurs
* Nächster Termin: 13.11. - 17.11.2017 in Berlin.

Java EE 6/7 Masterclass
* Nächster Termin: 20.11. - 24.11.2017 in Berlin.

Java Persistence API: Grundlagenkurs (JPA)
* Nächster Termin: 07.12. - 08.12.2017 in Berlin.

Java Persistence API: Komplettkurs (JPA)
* Nächster Termin: 05.12. - 08.12.2017 in Berlin.

Java Persistence API: Aufbaukurs (JPA)
* Nächster Termin: 23.10. -24.12.2017 in Berlin.