Teil 1: Wie alles beginnt

Backend meets Frontend: Web-UIs mit Java erstellen

Sven Ruppert

© Shutterstock / HelenField

 

Früher war alles besser; oder einfacher. Das gilt zumindest für Webdesign. Auch wenn es damals nicht unbedingt schöner war. JAXenter-Autor Sven Ruppert (Vaadin) wünscht sich die Einfachheit vergangener Zeiten zurück und zeigt in seiner Artikelserie, wie man als altgedienter Java-Backend-Entwickler einfach User Interfaces fürs Web bauen kann.

Wer kennt es nicht, die gute alte Zeit als man noch mit einer einfachen HTML-Seite und einem Apache-Webserver beginnen konnte? OK, zugegeben, das ist wirklich schon sehr lange her. Aber eines habe ich vermisst: Die Einfachheit, mit der Ergebnisse erzielt werden konnten, war schon bemerkenswert. Ist es damals noch ausreichend gewesen mit grell gelben Hintergrund und schwarzer Schrift eine einfache Seite bestehend aus einem gefühlt unendlich langem Text zu bauen, so ist der durchschnittliche Endbenutzer doch mittlerweile ein wenig mehr gewöhnt.

Außerdem muss ich gestehen, dass ich mich doch sehr gerne in der Sprache Java bewege. Nun hatte ich mit einem Dilemma zu kämpfen. Java auf der einen Seite als meine bevorzugte Sprache und auf der anderen Seite den Wunsch möglichst einfach – ich gestehe, das ist sehr subjektiv – eine Weboberfläche zu erstellen. So bin ich ca. 2009 auf das Framework Vaadin gekommen. Auch das ist schon sehr, sehr lange her. Was also hat mich bis heute daran fasziniert? Die Antwort ist recht einfach und kurz: Ich kann mit einem Java API auf einfache Art und Weise eine Webseite erstellen. Ich werde mich in der nächsten Zeit diesem Thema zuwenden und in dieser Artikelserie ausführlich beschreiben wie und was mit Vaadin möglich ist.

Backend meets Frontend

In der Artikelserie Backend meets Frontend stellt Sven Ruppert (Vaadin) Konzepte und Technologien rund um das UI-Framework Vaadin vor. Sein Fokus liegt dabei auf modernem Web-Design für Java-Backend-Entwickler.

Zum ersten Teil und damit dem Start der Tutorien rund um die UI-Entwicklung mit Java geht es hier entlang. Alle Teile der Serie Backend meets Frontend finden sich hier.

Und am Anfang stand das Servlet

Kommen wir nun zu den technischen Dingen. Um mit der Erstellung einer Webanwendung mit Vaadin zu beginnen, benötigen wir ein Servlet. Als Servlet-Container habe ich Undertow ausgesucht, andere sind aber genauso gut verwendbar. Nun gibt es verschiedene Wege eine Webanwendung in einem Servlet-Container laufen zu lassen, oder besser gesagt dort zu deployen. Der offizielle Vaadin-Weg ist übrigens hier beschrieben. In diesem Beispiel werden wir jedoch den Servlet-Container eingebettet laufen lassen und alles zusammen als ein Fat JAR starten. Undertow ist aus dem Hause Red Hat und kommt im Java Application Server Wildfly zum Einsatz. Wir werden Wildfly Swarm einsetzen. Das macht den Umgang mit Undertow wesentlich vereinfacht.

Wenn wir nun mit unserem „Hello World“ beginnen möchten, erstellen wir zuerst ein Projekt.
Ich verwende Maven in diesem Beispiel und füge die Definition zur Verwendung von Java 8 hinzu.

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

Um nun Wildfly Swarm verwenden zu können, fehlen noch die Abhängigkeiten und Versionsdefinitionen. Als erstes die Deklarationen der Versionen der eingesetzten Produkte. In diesem Fall Vaadin und Wildfly Swarm.

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.wildfly.swarm</groupId>
        <artifactId>bom-all</artifactId>
        <version>${wildfly.version}</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
      <dependency>
        <groupId>com.vaadin</groupId>
        <artifactId>vaadin-bom</artifactId>
        <version>${vaadin.version}</version>
        <scope>import</scope>
        <type>pom</type>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>

Danach die Definition der zusätzlich verwendeten Plug-ins.

<plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>3.1.0</version>
        <configuration>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.wildfly.swarm</groupId>
        <artifactId>wildfly-swarm-plugin</artifactId>
        <version>${wildfly.version}</version>
        <executions>
          <execution>
            <goals>
              <goal>package</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

Soweit präpariert, können wir nun unser erstes Servlet schreiben.

@WebServlet("/helloworld/*")
public class HelloWorldServlet extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.getWriter().write("Hello World");
  }
}

Was passiert hier? Hier wird lediglich auf der URL 127.0.0.1:8080/helloworld gelauscht und ein String mit dem Inhalt „Hello World“ zurück gegeben. Nun können wir die Instanz starten und testen.

Der erste Test
Wenn wir nun die Klasse mit der main-Methode starten, können wir das Ergebnis „Hello World“ mittels Browser überprüfen. Hierzu rufen wir die Adresse http://localhost:8080/helloworld auf und – was für eine Überraschung – es erscheint als Ergebnis ein „Hello World“.

Nun stellen sich einige weitere Fragen. Die erste könnte sein: Wie teste ich nun mit jUnit mein Hello World? In diesem Artikel verwende ich jUnit4, da es wohl derzeitig noch weiter verbreitet ist als jUnit5. Ich möchte, dass der Servlet-Container für einen Test jedesmal neu gestartet wird. Das Vorgehen ist recht einfach, da wir ja schon über einen vollständigen Initialisierungsmechanismus verfügen.

In der Methode setUp wird der Servlet-Container gestartet und in der Methode tearDown wieder gestoppt. Um das zu erreichen, muss nicht mehr passieren als das folgende Listing zeigt.

  private Swarm swarm;

  @Before
  public void setUp() throws Exception {
    swarm = new Swarm();
    swarm.start().deploy();
  }

  @After
  public void tearDown() throws Exception {
    swarm.stop();
  }

Und fertig sind wir. Es fehlt nun noch der Test an sich. Hierfür fügen wir als erstes die Abhängigkeit zu dem hier verwendeten Projekt OkHttP her.

    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>3.8.0</version>
      <scope>test</scope>
    </dependency>

Da ich persönlich kein Freund von sehr langen Namen bei Testmethoden bin und stattdessen lieber kurze eindeutige Tests schreiben, nenne ich den ersten Test test001.

    Request request = new Request.Builder()
        .url("http://127.0.0.1:8080/helloworld")
        .build();

    OkHttpClient client = new OkHttpClient();
    Response response = client.newCall(request).execute();

    Assert.assertNotNull(response);
    ResponseBody body = response.body();
    Assert.assertNotNull(body);
    Assert.assertEquals("Hello World", body.string());

Gehen wir im einzeln durch was hier abläuft. Als erstes erzeugen wir einen Request, der lediglich den URL http://127.0.0.1:8080/ aufrufen wird. Der Request selbst wird an der Stelle noch nicht ausgeführt. Das Ausführen der Requests erfolgt durch eine Instanz der Klasse OkHttpClient. Die Methode newCall(..) bekommt das Ziel und der Aufruf execute() führt den Request dann aus. Die Methode gibt eine Instanz der Klasse Response zurück, in der die jeweils vorhandenen Daten enthalten sind, die der Server zurückgesendet hat. In unserem Fall sind wir lediglich an dem Inhalt selbst interessiert. Diesen holen wir aus dem Body der Antwort heraus.

Alles zusammen läuft auf meinem MacBookPro in rund sechs Sekunden durch. In dieser Zeit enthalten ist der Start der JVM genauso wir der Start von dem hier verwendeten Servlet-Container. Wie wir das in unter einer Sekunde hinbekommen, beschreibe ich in dem nächsten Teil dieser Serie.

Lesen Sie auch: Technologie-Trends 2017: Das sind die Top-Frameworks

Es werde bunt

Da wir nun ein Servlet inclusive eines Servlet-Containers zusammen haben, können wir uns nun um das UI kümmern. Um Vaadin zu verwenden, benötigen wir die nachfolgend aufgelisteten Abhängigkeiten.

<!--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>

Damit können wir nun beginnen unsere Vaadin-Web-App zu schreiben. Vaadin selbst bietet ein Servlet an, das die Grundlage der Vaadin-basierten Anwendung bildet. In unserem Fall reicht es aus, wenn wir von VaadinServlet ableiten und die Verbindung zu unserem UI herstellen.

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

In der Annotation @VaadinServletConfiguration(..) gibt es das Attribut ui, das eine Klasse als Parameter erwartet. Hier findet die Verknüpfung zu unserem UI statt. Das UI selbst ist eine Klasse, die von UI erbt. Dort erhalten wir die Möglichkeit, das gesamte UI zu initialisieren. Wir werden das in unserem Fall als erstes wieder mit einem Hello World machen.

 public class MyUI extends UI {
    @Override
    protected void init(VaadinRequest request) {
      setContent(new Label("Hello World"));   // Attach to the UI
    }
  }

Diesmal starten wir die Web-App mit der IDE, indem wir die Klasse org.wildfly.swarm.Swarm als die Klasse mit der main-Methode angeben. Genauso gut kann man auch das Maven-Target mvn wildfly-swarm:run verwenden.

Der Entwicklungszyklus

Nachdem wir nun mit dem Hello World fertig sind, wollen wir uns ansehen wie prinzipiell nun ein einfacher Entwicklungszyklus aussehen kann. Hiermit ist gemeint, wie man die Änderungen an der Web-App vornimmt und sich das Ergebnis ansieht. Das wird innerhalb der nächsten Artikel dieser Serie noch um einiges ausführlicher werden. Aber beginnen wir einfach. Ziel ist es, einen Button auf der Seite zu zeigen, der nach dem Drücken eine kleine Nachricht auf der Seite anzeigt.

Abb. 1: Mini-Anwendung: Nach dem Drücken des Buttons soll eine Nachricht erscheinen

Hierfür benötigen wir einen Button, ein Label und ein VerticalLayout, in dem wir alles platzieren.
Um die Elemente unterzubringen, werden wir die Klasse MyUI erweitern. Innerhalb der Methode init(VaadinRequest request) erzeugen wir als erstes den Holder, in unserem Fall ein VerticalLayout. Danach platzieren wir den Button imLayout und setzen alles erst einmal als Content der Seite. In diesem Zuge werden wir unser Hello World entfernen.

  public class MyUI extends UI {
    @Override
    protected void init(VaadinRequest request) {
      final VerticalLayout layout = new VerticalLayout();
      final Button button = new Button("click me");
      layout.addComponents(button);
      setContent(layout);
    }
  }

Abb. 2: Tada! Ein Button erscheint

Nachdem wir nun den Quelltext geschrieben haben, starten wir die Anwendung wie bisher auch und
rufen im Browser die URL http://127.0.0.1:8080/vaadin auf. Und wir sehen … einem Button mit der Beschriftung click me. An dieser Stelle möchte ich schon mal kurz darauf hinweisen, dass wir das Thema Mehrsprachigkeit zu einem späteren Zeitpunkt ausführlich betrachten werden.

Nun fehlt noch die Funktion, die der Button haben soll. Dazu fügen wir dem Button einen ClickListener hinzu.

      button.addClickListener(new Button.ClickListener() {
        @Override
        public void buttonClick(Button.ClickEvent event) {
          Label label = new Label("clicked again");
          layout.addComponents(label);
        }
      });

Um die Funktion auszuprobieren, starten wir den Servlet-Conatiner erneut.

Abb. 3: Beschriftung geändert von clicked again zu was clicked

Beim manuellen Test fällt uns auf, dass wir ja eigentlich was clicked ausgeben wollten und nicht klicked again. OK, in dem Fall ist es trivial. Aber wie würde man mit einem Debugger hier arbeiten? Die Antwort ist ganz einfach, wie in einem plain Java-Programm auch setzen wie einen Breakpoint an die Stelle, an der wir das Label erzeugen und schauen uns den Inhalt an.

Abb. 4: Der Blick in den Debugger

Nun ändern wir den Inhalt vom Label auf was clicked und starten den Servlet-Conatiner neu.

      button.addClickListener(new Button.ClickListener() {
        @Override
        public void buttonClick(Button.ClickEvent event) {
          Label label = new Label("was clicked");
          layout.addComponents(label);
        }
      });

Und was natürlich ein netter Nebeneffekt ist, dass der ClickListener ein FunctionalInterface ist.
Demnach können wir natürlich auch Lambdas verwenden.

      button.addClickListener((Button.ClickListener) event -> {
        Label label = new Label("was clicked");
        layout.addComponents(label);
      });

Hier wird der schnelle Round-Trip bei der Erstellung des UI mit Vaadin und einem
embedded Servlet-Conatiner deutlich. Wir werden uns in einem der nächsten Artikel noch wesentlich intensiver damit auseinandersetzen wie wir uns eine komfortable und schnelle Entwicklungsumgebung aufbauen werden. Unter anderem auch, wie wir automatische Tests für unser UI schreiben können.

Abb. 5: Fertig!

Fazit

Wir haben heute eine erste Anwendung geschrieben, basierend auf Undertow (Projekt Wildfly Swarm) als Servlet-Container und einer ersten Implementierung die uns ein Hello World auf einen Request zurückliefert. Dazu passend haben wir einen jUnit-Test geschrieben, der vor jedem Test den Servlet-Container startet, den Request durchführt und den Servlet-Container wieder stoppt. Damit hatten wir die Basisinfrastruktur, um mit einer Vaadin-Anwendung zu beginnen. Hierzu wurde von dem VaadinSerlvet abgeleitet und per Annotation ein UI verknüpft. Die Klasse MyUI haben wir nach und nach erweitert und kurz angesehen wie ein einfacher Entwicklungszyklus aussehen kann inklusive dem debuggen der Anwendung.

In den nächsten Teilen dieser Serie werden wir uns mit Themen beschäftigen wie Mehrsprachigkeit (i18n), Aufbau und Testen komplexer UIs und vielem mehr. Den Quelltext findet ihr auf GitHub. Bei Fragen oder Anregungen einfach melden unter sven@vaadin.com oder per Twitter @SvenRuppert.

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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.