Grundrechenarten

Nun, die Grundrechenarten kennt (hoffentlich) doch jeder: Addieren, Subtrahieren, Multiplizieren und Dividieren. Dazu braucht es zwei Zahlen, die mit einem der vier Operatoren verknüpft werden und der Rechner spuckt das Ergebnis aus. Eine triviale Aufgabe, die Sie wahrscheinlich auch im Kopf oder schriftlich lösen können, aber warum die Mühe? Lassen wir dies unseren Computer erledigen und zwar so, dass wir dies im Browser nutzen können.

In diesem Teil des Tutorials lernen Sie die unter anderem serverseitige Verarbeitung mittels einer sogeannten managed Bean kennen.

Legen Sie dazu wahlweise ein neues Web-Projekt, wie unter “Die erste Webapplikation” beschrieben an, mit dem Unterschied, dass Sie das Projekt “TinyCalulator” nennnen oder nennen Sie das vorhandene Projekt um. Für Letzteres klicken Sie in der Projektansicht mit der sekundären (meist rechten) Maustaste auf das Projekt “JSFTutorial” und wählen im Popupmenü “Rename…”. Es erscheint ein kleiner Dialog zum Umbennen des Projekts:

   

Wählen Sie als neuen Namen “TinyCalculator” und markieren “Also Rename Project Folder”, um den Ordner gleichlautend umzubenennen.

Nun, was benötigen Sie für dieses Projekt? Da sind zwei Eingabefelder für die beiden Parameter, vier Buttons für die Operatoren und ein Feld für das Ergebnis. Editieren Sie dazu den Sourcecode der Seite index.xhtml

       

Die Abbildung zeigt dies im Kontext von NetBeans. Für die weitere Beschreibung verzichte ich auf solche Abbildungen. Stattdessen finden Sie jeweils den Sourcecode gelistet. So auch hier, um diesen einfach mittels copy and paste zu übernehmen (der Lerneffekt ist aber größer, wenn Sie selber editieren).

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Tiny calculator</title>
    </h:head>
    <h:body>
        <h1>Tiny calculator</h1>
        <h:form>
            <h:outputLabel value="Param 1:"/>
            <h:inputText id="param1"/>
            <br/>
            <h:outputLabel value="Param 2:"/>
            <h:inputText id="param2"/>
            <br/>
            <h:commandButton value="add"/>
            <h:commandButton value="substract"/>
            <h:commandButton value="multiply"/>
            <h:commandButton value="divide"/>
            <br/>
            <h:outputLabel value="Result:"/>
            <h:outputText id="result"/>
        </h:form>
    </h:body>
</html>

Wie Sie erkennen können, gibt es neben ein paar beschreibenden Labeln die erforderlichen Komponenten (inputText, commandButton und outputText). Gerechnet wird an dieser Stelle noch nicht. Starten Sie die Applikation.

               

In den beiden Eingabefeldern können Sie Werte eingeben. Klicken Sie auf einer der Schaltflächen, so scheint nicht zu passieren. Wenn Sie jedoch genau darauf achten, werden Sie bemerken, dass die Seite neu geladen wird.

Nun schauen Sie sich einmal den Sourcecode der Seite an (hier ein bisschen “schöner” formatiert).

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Tiny calculator</title>
  </head>
  <body>
    <h1>Tiny calculator</h1>
    <form id="j_idt8" name="j_idt8" method="post" 
            action="/TinyCalculator/index.xhtml" 
            enctype="application/x-www-form-urlencoded">
      <input type="hidden" name="j_idt8" value="j_idt8" />
      <label>Param 1:</label>
      <input id="j_idt8:param1" type="text" name="j_idt8:param1" />
      <br />
      <label>Param 2:</label>
      <input id="j_idt8:param2" type="text" name="j_idt8:param2" />
      <br />
      <input type="submit" name="j_idt8:j_idt13" value="add" />
      <input type="submit" name="j_idt8:j_idt14" value="substract" />
      <input type="submit" name="j_idt8:j_idt15" value="multiply" />
      <input type="submit" name="j_idt8:j_idt16" value="divide" />
      <br />
      <label>Result:</label>
      <span id="j_idt8:result"></span>
      <input type="hidden" name="javax.faces.ViewState" 
            id="javax.faces.ViewState" 
            value="3061588258500397437:-7913731195929753954" 
            autocomplete="off" />
    </form>
  </body>
</html>

Hier ist bereits deutlich mehr passiert als in der ersten Applikation. Und es wird Zeit, zumindest ein paar der Ersetzungen genauer unter die Lupe zu nehmen.

Unser Quelltext

<h:head>
   <title>Tiny calculator</title>
</h:head>

erreicht den Browser als

<head>
    <title>Tiny calculator</title>
</head>

Das haben Sie bereits bei der ersten Applikation gesehen. Was passiert eigentlich, wenn Sie das Präfix “h:” einfach auslassen? Nun, hier erst einmal nichts: Der Browser erhält zur Darstellung den gleichen HTML-Code. Später werden Sie jedoch sehen, dass es Komponenten gibt, die in den Header gehören. Und ohne den Namensraum zur Kennzeichnung als Tag, weiß JSF dann damit nichts anzufangen. Geben Sie den Präfix also einfach immer an. Er schadet nicht, und wenn Sie ihn brauchen,ist er da. Entsprechendes gilt auch für den Body.

Deutlich mehr passiert bereits bei “h:form”

<h:form> 
    [...] 
</h:form>

Im Browserquelltext wird dies regelrecht aufgebläht.

<form id="j_idt8" name="j_idt8" method="post" 
        action="/TinyCalculator/index.xhtml" 
        enctype="application/x-www-form-urlencoded">
  <input type="hidden" name="j_idt8" value="j_idt8" />
[...]
  <input type="hidden" name="javax.faces.ViewState" 
        id="javax.faces.ViewState" 
        value="3061588258500397437:-7913731195929753954" 
        autocomplete="off" />
</form>

Da ist zum einen eine id. Diese können Sie auch selber festlegen. Sie muss für die Seite eindeutig sein und dient dem serverseitigen Aufbau des Komponentenbaums. Interessant sind auch zwei versteckte Eingabefelder, die JSF nutzt, um Informationen mitzuführen. So müssen die Status der einzelnen Komponenten zwischengespeichert werden. Dies geschieht je nach Konfiguration auf dem Server oder auf dem Client. Der Wert des versteckten Eingabefeldes mit der ID “javax.faces.ViewState” stellt einen Identifier dar, unter dem die Status serverseitig gespeichert werden. Er ändert sich bei jedem Aufruf. Bei clientseitiger Speicherung kann der Wert je nach Größe des Formulars deutlich größer ausfallen. Weitergehende Informationen finden Sie in späteren Teilen dieses Tutorials beim JSF-Lebenszyklus sowie der Konfiguration

Der Rest im Schnelldurchgang: Ein outputLabel wird in ein label übersetzt, ein inputText in input type “text”, ein commandButton in input type “submit” sowie der outputText in ein span. In dieser Applikation. Denn je nach Kontext können einzelne Komponenten auch schon einmal anders übersetzt werden. Insofern stellen diese Tags ein Abstraktion von HTML-Elementen dar. Und jede Komponente erhält eine eindeutige ID bzw. Namen, in dem sich die Schachtelung der Komponenten widerspiegelt.

Eine Menge Stoff bisher, und die Anwendung kann noch nicht einmal rechnen. Zum Glück fällt der Quelltext bisher recht übersichtlich aus. Und vielmehr wird es auch nicht. Denn die Berechnung hat nichts in der xhtml-Seite zu suchen, schließlich erfolgt sie serverseitig. Aber wo?

Nun, dazu benötigt es eine Kaffeebohne (JavaBean). Eine JavaBean zeichnet sich dadurch aus, dass der Zugriff auf die Attribute (Objektvariablen) einem bestimmten Schema folgt, nämlich getund set, wobei Siedurch den gewünschten Methodennamen (in Anlehnung an das jeweilige Attribut ersetzen. Auch bekannt als Getter/Setter.

Legen Sie dazu eine neue Klasse vom Typ “JSF Managed Bean” an.

                                                       

Geben Sie als Klassennamen “CalculatorBean” an. Diesen Namen finden Sie weiter unten – mit kleinem Anfangsbuchstaben – als Name wieder.

               

Unter “Package” vergeben Sie einen beliebigen Paketnamen, im Beispiel “calculator”. Eine interessante Einstellung können Sie bei “Scope” vornehmen. Damit legen Sie fest, ob die Bean beispielsweise nur für eine Anfrage, eine Sitzung oder die gesamte Applikation gültig ist. Mehr dazu später. Belassen Sie dies hier auf “request” und beenden Sie den Assistenten mit [Finish]. Der Assistent legt Ihnen nun die Klasse an sowie diverse Einträge in den Konfigurationsdateien, die ich später erkläre. Sollten Sie einmal eine “normale” Java-Klasse anlegen, müssen Sie all diese Einstellungen manuell vornehmen, um sie als JSF Managed Bean nutzen zu können. Ein Lob auf den Assistenten 😉

Vervollständigen Sie den Quelltext wie folgt:

package calculator;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

/**
 *
 * @author mmueller
 */
@ManagedBean
@RequestScoped
public class CalculatorBean {
    private int _param1;
    private int _param2;
    private String _result;

    public CalculatorBean() {
    }

    // 
    public int getParam1() {
        return _param1;
    }

    public void setParam1(int param1) {
        _param1 = param1;
    }

    public int getParam2() {
        return _param2;
    }

    public void setParam2(int param2) {
        _param2 = param2;
    }

    public String getResult() {
        return _result;
    }
    // 

    public String add(){
        _result = Integer.toString(_param1 + _param2);
        return "";
    }

    public String substract(){
        _result = Integer.toString(_param1 - _param2);
        return "";
    }

    public String multiply(){
        _result = Integer.toString(_param1 * _param2);
        return "";
    }

    public String divide(){
        _result = _param2 == 0 ? "n/a" : Double.toString(_param1 / (double)_param2);
        return "";
    }

}

Zwei Attribute entsprechen unseren Parametern und das dritte ist das Ergebnis. Das Ergebnis wird in vier Methoden, die wie die Grundrechenarten heißen berechnet und im Attribut abgelegt. Das sieht ungewöhnlich aus, geben Sie doch normalerweise das Ergebnis als Rückgabewert der Methode (Funktion) aus. Hier liefern die Methoden stattdessen einen Leerstring (es darf auch null sein). Weiterhin fallen zwei Annotationen auf, die Ihre Eingaben im Assistenten wiederspiegeln. Wozu das Ganze gut ist, sehen Sie in Kürze. Wenn Sie möchten, schreiben Sie jetzt einen Unit-Test, um die Funktion der Bean zu überprüfen.

Wenden wir uns wieder der Webseite zu. Ergänzen Sie den Quelltext entsprechend dem folgenden Listing.

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html">
    <h:head >
        <title>Tiny calculator</title>
    </h:head>
    <h:body>
        <h1>Tiny calculator</h1>
        <h:form id="calculator">
          <h:outputLabel for="param1" value="Param 1:"/>
          <h:inputText id="param1" value="#{calculatorBean.param1}"/>
          <br/>
          <h:outputLabel for="param2" value="Param 2:"/>
          <h:inputText id="param2" value="#{calculatorBean.param2}"/>
          <br/>
          <h:commandButton action="#{calculatorBean.add}" value="add"/>
          <h:commandButton action="#{calculatorBean.substract}" value="substract"/>
          <h:commandButton action="#{calculatorBean.multiply}" value="multiply"/>
          <h:commandButton action="#{calculatorBean.divide}" value="divide"/>
          <br/>
          <h:outputLabel for="result" value="Result:"/>
          <h:outputText id="result" value="#{calculatorBean.result}"/>
        </h:form>
    </h:body>
</html>

Zum einen erhalten Formular und Texte eine ID und die Labels werden daran gebunden. Diese Ids ersetzen die automatisch generierten und sind für den Entwickler deutlich besser zu lesen.

Zum anderen werden die Texte an die Getter/Setter gebunden. Nun, die Wertebindung kann in beide Richtungen erfolgen, und so wird einfach nur der Methodenname ohne “get” oder “set” angegeben. Dies wird intern automatisch ergänzt (genauer gesagt kommt hier Reflection zum Einsatz). Vielleicht kennen Sie die Properties aus C#? Ohne get/set sieht dies den Properties doch sehr ähnlich. Und wenn Sie sich Microsofts CLR ansehen, so werden Sie feststellen, dass die Properties intern mittels Getter und Setter realisiert sind. Oh wundersame Welt der Sprachen…

Und die Buttons werden an die Rechenmethoden gebunden. Sie sind nur der Auslöser, um die entsprechenden Methoden aufzurufen. Das Ergebnis wiederum ist als Wert (getter!) an den outputText gebunden. Zur Bindung dienen die Ausdrücke in der Form “#{…}”. Diese sind in der sogenannten Expression Language verfasst. Davon später mehr. Hier das Resultat der bisherigen Arbeit (Starten Sie die App mit F6).    

               

Das sieht wie gehabt aus. Wenn Sie nun aber Werte eingeben und einen der Buttons betätigen, so wird tatsächlich gerechnet.

Und wie von Zauberhand werden unpassende Eingaben kommentiert und abgewiesen.

               

Doch das ist eine andere Geschichte und davon erzähle ich später…