Das Spring Boot der EE-Welt?

Anwendungsbau mit Apache Meecrowave

Sven Ruppert

© Shutterstock / Tetiana Yurchenk & VectorsMarket (modifiziert)

Apache Meecrowave ist ein Mikroprofil-Server und ein Apache-Projekt, das man als eine Art „Spring Boot der EE-Welt“ bezeichnen kann, nur dass es sich hierbei ausschließlich um Apache-Projekte handelt, die in der Realisierung Verwendung finden. Ich bin mir bewusst das es an der einen oder anderen Stelle nun zu einem Aufschrei kommen wird. Aber schauen wir es uns doch einmal kurz an….

Vorbereitung: Das Projekt Apache Meecrowave befindet sich auf der Projektseite von OpenWebBeans, die nötigen Quellcodes können auf GitHub gefunden werden.

Apache Meecrowave: Grundlagen

Das Projekt Apache Meecrowave selbst ist sehr überschaubar. Handelt es sich im wesentlichen doch lediglich um eine Schale, in der bereits existierende Technologien, die alle aus dem Hause des Apachen kommen, vereint werden. Kern der Sache ist der Apache Tomcat, der in diesem Fall als Servlet-Container die Laufzeitumgebung bereitstellt. Zusätzlich sind die folgenden Komponenten enthalten:

Fügt man alles zusammen in ein JAR, ist das Archiv in etwa 9 MB groß, eine für die heutige Zeit doch sehr kleine Portion. Hinzu kommen wird natürlich noch die eigene Anwendung und die damit verbundenen Abhängigkeiten. Um nun mit dem Meecrowave-Paket zu arbeiten, fügen wir unserem Maven-Projekt (Gradle geht natürlich ebenfalls) die folgende Abhängigkeit hinzu. Hiermit haben wir schließlich eine Grundlage, mit der man beginnen kann.

    <dependency>
      <groupId>org.apache.meecrowave</groupId>
      <artifactId>meecrowave-core</artifactId>
      <version>${meecrowave.version}</version>
    </dependency>

Um nun eine Instanz zu starten, wird eine Klasse mit dem Namen (der kann natürlich frei gewählt werden) Main angelegt und darin eine klassische main-Methode definiert.

  public static void main(String[] args) {
    new Meecrowave(new Meecrowave.Builder() {
      {
        randomHttpPort();
        setTomcatScanning(true);
        setTomcatAutoSetup(false);
        setHttp2(true);
        setTempDir("target/meecrowave/" + System.nanoTime());
        setUseShutdownHook(true);
      }
    })
        .bake()
        .await();
  }

Das Meecrowave-Projekt bietet dem Nutzer einen Builder, mittels dem eine Instanz programmatisch konfiguriert werden kann. Die Instanz des Builders ist dann das Übergabe-Argument in dem Konstruktor. Ein anschließendes Starten (Bake) und darauffolgendes Warten bzw. Halten der Instanz (Await) runden den Startvorgang ab. Die in dem gerade gezeigten Beispiel-Quelltext verwendeten Konfigurationsoptionen sind übrigens nicht das notwendige Minimum, sondern sind einfach zu Demonstrationszwecken verwendet worden.

Da wir nun eine laufende Instanz haben, können wir mit dem ersten HelloWorld beginnen.

Hello World mit REST

Kommen wir zu dem obligatorischen Hello World, das eigentlich nie fehlen darf. In unserem Fall beginnen wir mit einem Hello World, um einen REST Endpoint
zur Verfügung zu stellen. Hierzu wird einfach im Klassenpfad eine Klasse mit dem Namen HelloEndpoint und dem folgenden Inhalt erzeugt:

@Path("hello")
@ApplicationScoped
public class HelloEndpoint {

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String sayHi() {
        return "Hello World";
    }
}

Beim Start des Containers wird die Klasse gefunden, der Endpunkt registriert und die Verwendung kann beginnen. Um dies zu prüfen, kann man auch gleich den ersten Test schreiben. Das Projekt Meecrowave bringt eine Erweiterung für JUnit4 und JUnit5 mit. Um diese zu nutzen, muss die nachfolgend aufgeführte Abhängigkeit in die Datei pom.xml eingetragen werden. Nicht zu vergessen, dass die JUnit5-Abhängigkeiten ebenfalls vorhanden sein müssen.

    <!--TDD Extension for Meecrowave-->
    <dependency>
      <groupId>org.apache.meecrowave</groupId>
      <artifactId>meecrowave-junit</artifactId>
      <scope>test</scope>
      <version>${meecrowave.version}</version>
    </dependency>

Um nun diesen Endpunkt zu testen, wird zu Beginn des Tests der Container hochgefahren. Da zum Beispiel der Port dynamisch vergeben wird, kann man diese und auch weitere Informationen aus der Builder-Konfiguration auslesen. Die aktuell gültige Instanz der Konfiguration wird von der JUnit5-Erweiterung (MonoMeecrowaveConfig) injiziert.

@MonoMeecrowaveConfig
public class HelloEndpointTest {

    @ConfigurationInject
    private Meecrowave.Builder configuration;

    @Test
    public void hello() {
        final Client client = ClientBuilder.newClient();
        try {
            assertEquals("Hello World", client.target("http://localhost:" + configuration.getHttpPort())
                    .path("/hello")
                    .request(APPLICATION_JSON_TYPE)
                    .get(String.class));
        } finally {
            client.close();
        }
    }
}

Hello World mit HttpServlet

Kommen wir nun zum ersten Beispiel auf der Basis eines HttpServlets. Auch hier wieder zum Testen ein einfaches HelloWorld. In diesem Fall gehen wir einen Schritt weiter, indem eine minimale Funktion mittels CDI der Servlet-Instanz zur Verfügung gestellt wird.

@WebServlet("/*")
public class HelloWorldServlet extends HttpServlet {

  @Inject private UpperCaseService service;

  public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
      throws IOException {
    response.setContentType("text/plain; charset=utf-8");

    String value = request.getParameter("value");

    response.getWriter().println(service.upperCase(value));
  }
}
@Dependent
public class UpperCaseService{
  public String upperCase(String txt) {
    return txt.toUpperCase();
  }
}

Auch hier reicht es wieder, die Klassen im aktuellen Klassenpfad zur Verfügung zu stellen. Ein @Inject funktioniert also auch bestens. Wenn man in seinem Projekt JUnit4 verwendet, kann man den Test zum Beispiel wie im Folgenden dargestellt schreiben. Das Vorgehen ist dasselbe wie bisher, nur dass diesmal die Information mittels Rule bereitgestellt wird.

public class HelloWorldServletTest {

  @Rule
  public final MeecrowaveRule rule = new MeecrowaveRule();

  @Test
  public void test001() {
    final Client client = ClientBuilder.newClient();
    try {
      assertEquals("HALLONASE", client.target("http://127.0.0.1:" + rule.getConfiguration().getHttpPort())
                                      .queryParam("value", "HalloNase")
                                      .request(APPLICATION_JSON_TYPE)
                                      .get(String.class)
                                      .trim());
    } catch (Exception e) {
      Assert.fail(e.getMessage());
    } finally {
      client.close();
    }
  }
}

Die erste Anwendung mit Vaadin V8

Da wir nun die ersten Erfahrungen mit Meecrowave und der Verwendung von Servlets gesammelt haben, starten wir mit der ersten RIA-Anwendung auf der Basis von Vaadin 8. Warum Vaadin 8, wo doch gerade Vaadin 10 herausgekommen ist? Nun, das hat mehrere Gründe. Der erste Grund ist, dass bei Vaadin 8 noch das Servlet selbst angegeben werden muss. Hiermit knüpfen wir an das vorherige Beispiel an und können sehen, wie einfach und schnell mit der Entwicklung einer Single-Page-Webapplikation begonnen werden kann.

Vaadin 8 ist in fast der Hälfte aller Fortune-100-Firmen im Einsatz und wird noch bis mindestens Februar 2022 weiterentwickelt. Ein Blick auf Vaadin 8 lohnt sich also auch heute noch. Um Vaadin 8 zu nutzen, müssen noch die folgenden Abhängigkeiten in der pom.xml definiert werden.

  <dependencyManagement>
    <dependencies>
      <!--Vaadin -->
      <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-bom</artifactId>
        <version>${vaadin.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <!--Vaadin -->
    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin-server</artifactId>
    </dependency>
    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin-themes</artifactId>
    </dependency>
    <dependency>
      <groupId>com.vaadin</groupId>
      <artifactId>vaadin-client-compiled</artifactId>
    </dependency>

  </dependencies>

Beginnen wir mit dem Servlet, dem Dreh- und Angelpunkt der Webanwendung. Hier wird lediglich mittels Annotation die zu verwendende UI-Klasse (in unserem Fall die Klasse MyUI) definiert.

    @WebServlet("/*")
    @VaadinServletConfiguration(productionMode = false, ui = MyUI.class)
    public static class MyProjectServlet extends VaadinServlet { }

Die Klasse MyUI ist sehr einfach und enthält lediglich einen Button, der, wenn er gedrückt wird, ein neues Label erzeugt und der Ansicht hinzufügt.

  public class MyUI extends UI {
    @Override
    protected void init(VaadinRequest request) {
      final Layout layout = new VerticalLayout();
      layout
          .addComponent(new Button("click me",
                                   event -> layout.addComponents(new Label("clicked again"))
          ));

      //set the main Component
      setContent(layout);
    }
  }

Die erste Anwendung mit Vaadin V10

Zu guter Letzt sehen wir uns noch ein HelloWorld mittels Vaadin 10 an. Der Unterschied ist, dass ab Vaadin 10 das Servlet nicht mehr zwingend definiert werden muss. Es reicht also, wenn man die Route, zu der die UI-Komponente zugehörig ist, mittels Annotation definiert. In unserem Fall ist die Komponente direkt unter / zu erreichen. Das dazugehörige Servlet stellt in diesem Fall das Framework bereit, der grundsätzliche Ablauf ist aber recht ähnlich.

@Route("")
public class HelloVaadinV10 extends Composite<Div> {

  public HelloVaadinV10() {
    final VerticalLayout layout = new VerticalLayout();
    layout
        .add(new Button("click me",
                        event -> layout.add(new Label("clicked again"))
        ));
    //set the main Component
    getContent().add(layout);

  }
}

Es gibt es noch einige andere Unterschiede zwischen Vaadin 8 und der Vaadin-10-Platform, auf die ich an dieser Stelle nicht weiter eingehen werde. Ich möchte nur den wichtigsten hervorheben: Vaadin 8 basierte noch auf GWT, in der Vaadin-10-Plattform basiert alles auf WebComponents. Hier verweise ich auf die entsprechende Dokumentation bzw. werde hier auf JAXenter demnächst mehr dazu schreiben.

Fazit

Alles zusammengenommen kann man sagen, dass das Team „Meecrowave & Vaadin“ sehr effektiv ist, es lohnt sich auf jeden Fall einen Blick darauf zu werfen. Nachdem die Abhängigkeiten definiert sind, kann man sofort damit beginnen, das UI zu erstellen. Die Turn-Around-Zeiten sind sehr gering, ein Neustart von Meecrowave und Vaadin liegt zusammen bei unter 500ms auf meinem Laptop. So kann man selbst mit einer einfachen IDE, Meecrowave und Vaadin einen UI-Prototypen erstellen, während man mit dem Fachbereich zusammensitzt und die grundsätzlichen Anforderungen aufnimmt.

Wer sich die Quellcodes zu diesem Artikel ansehen möchte der sollte sich auf GitHub die Demos nicht entgehen lassen.

Happy Coding!

Geschrieben von
Sven Ruppert
Sven Ruppert
Sven Ruppert arbeitet seit 1996 mit Java und ist Developer Advocate bei Vaadin. In seiner Freizeit spricht er auf internationalen und nationalen Konferenzen, schreibt für IT-Magazine und für Tech-Portale. Twitter: @SvenRuppert
Kommentare

Hinterlasse einen Kommentar

3 Kommentare auf "Anwendungsbau mit Apache Meecrowave"

avatar
400
  Subscribe  
Benachrichtige mich zu:
Carsten Petschel
Gast

Funktioniert der Meecrowave Server auch mit der NetBeans IDE?

Sven Ruppert
Gast

klar, Meecrowave ist ja nicht abhängig von der IDE. Intern ist es ein Tomcat.

Basti
Gast

Sieht auf den ersten Blick sehr interessant aus. Wie sieht es aber mit der Unterstützung für eine Datenbankabstraktion ala JDBI oder gar JPA aus? Ohne DB ist ein Rest-Service recht mau in der Praxis.