SeamFramework.orgCommunity Documentation

Kapitel 3. Die Web Beans Referenzimplementierung

3.1. Das numberguess-Beispiel
3.2. Das translator-Beispiel

Die Web Beans Referenzimplementierung wird unter das Seam Projekt entwickelt. Sie können die aktuellste Entwickler-Release von Web Beans von der Downloads Seite herunterladen.

Die Web Beans RI kommt mit zwei deploybaren Beispielanwendungen: webbeans-numberguess, ein war-Beispiel, das nur einfache Beans enthält und webbeans-translator, ein ear-Beispiel, dasEnterprise Beans enthält. Um Beispiele auszuführen benötigen Sie folgendes:

Currently, the Web Beans RI only runs on JBoss Application Server 5. You'll need to download JBoss AS 5.0.1.GA from jboss.org, and unzip it. For example:

$ cd /Applications
$ unzip ~/jboss-5.0.1.GA.zip

Anschließend laden Sie die Web Beans RI unter seamframework.org herunter und entzippen diese. Zum Beispiel

$ cd ~/
$ unzip ~/webbeans-$VERSION.zip

Als nächstes müssen wir Web Beans mitteilen, wo JBoss sich befindet. Editieren Sie jboss-as/build.properties und setzen Sie die jboss.home-Property. Zum Beispiel:

jboss.home=/Applications/jboss-5.0.1.GA

Anmerkung

A new deployer, webbeans.deployer is added to JBoss AS. This adds supports for Web Bean archives to JBoss AS, and allows the Web Beans RI to query the EJB3 container and discover which EJBs are installed in your application.

Web Beans is bundled with JBoss AS 5.1 and above.

To install Web Beans, you'll need Ant 1.7.0 installed, and the ANT_HOME environment variable set. For example:

$ unzip apache-ant-1.7.0.zip
$ export ANT_HOME=~/apache-ant-1.7.0

Then, you can install the update. The update script will use Maven to download Web Beans automatically.

$ cd webbeans-$VERSION/jboss-as
$ ant update

Jetzt können Sie Ihr erstes Beispiel deployen!

Tipp

Die Build-Skripte für die Beispiele bieten zahlreiche Ziele, diese sind:

  • ant restart - Deployment des Beispiels in ausgeklapptem Format

  • ant explode - Aktualisierung eines ausgeklappten Beispiels ohne Neustart des Deployments

  • ant deploy - Deployment des Beispiels in komprimiertem jar-Format

  • ant undeploy - das Beispiel vom Server entfernen

  • ant clean - Das Beispiel bereinigen

Um das numberguess Beispiel zu deployen:

$ cd examples/numberguess
ant deploy

JBoss AS starten:

$ /Application/jboss-5.0.0.GA/bin/run.sh

Tipp

Falls Sie Windows verwenden, verwenden Sie das run.bat-Skript.

Deployen Sie die Anwendung und genießen Sie stundenlangen Spaß unter http://localhost:8080/webbeans-numberguess!

Die Web Beans RI enthält ein zweites einfaches Beispiel, das Ihren Text ins Lateinische übersetzt. Beim numberguess Beispiel handelt es sich um ein war-Beispiel und es verwendet nur einfache Beans; das translator-Beispiel ist ein ear-Beispiel, das in einem EJB-Modul verpackte Enterprise Beans enthält . Um dies auszuprobieren:

$ cd examples/translator
ant deploy

Warten Sie, bis die Anwendung deployt ist und besuchen Sie http://localhost:8080/webbeans-translator!

In der numberguess-Anwendung haben Sie 10 Versuche, eine Zahl zwischen 1 und 100 zu erraten. Nach jedem Versuch wird Ihnen mitgeteilt, ob Sie zu hoch oder zu niedrig liegen.

Das numberguess-Beispiel besteht aus einer Reihe von Web Beans, Konfigurationsdateien und Facelet JSF-Seiten, die als eine war verpackt sind. Fangen wir mit den Konfigurationsdateien an.

Alle Konfigurationsdateien für dieses Beispiel befinden sich in WEB-INF/, das in WebContent im Quell-Baum gespeichert ist. Zunächst haben wir faces-config.xml, in dem wir JSF anweisen, Facelets zu verwenden:


<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="1.2"
              xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd">
    
    <application>
        <view-handler
>com.sun.facelets.FaceletViewHandler</view-handler>
    </application>

</faces-config
>

Es existiert eine leere web-beans.xml-Datei, die diese Anwendung als Web Beans Applikation kennzeichnet.

Und schließlich gibt es noch web.xml:

Let's take a look at the Facelet view:

<!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:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:s="http://jboss.com/products/seam/taglib">

  <ui:composit(1)ion template="template.xhtml">
    <ui:define name="content">
       <h1
>Guess a number...</h1>
       <h:form(2) id="NumberGuessMain">
          <div style="color: red">
             <h:messages id="messages" globalOnly="false"/>
             <h:outputText id="Higher" value="Higher!" rendered="#{game.number gt game.guess and game.guess ne 0}"/>
             <h:outputText id="Lower" value="Lower!" rendered="#{game.number lt game.guess and game.guess ne 0}"/>
          </div>
   
          <div(3)>
             I'm thinking of a number between #{game.smallest} and #{game.biggest}.
             You have #{game.remainingGuesses} guesses.
          </div>
     
          <div>
             Y(4)our guess: 
             <h:inputText id="inputGuess" 
                          value="#{game.guess}" 
                          required="true" 
                          size="3" 
              (5)            disabled="#{game.number eq game.guess}">
                <f:validateLongRange maximum="#{game.biggest}" 
                                     minimum="#{game.smallest}"/>
             <(6)/h:inputText>
            <h:commandButton id="GuessButton"  
                             value="Guess" 
                             action="#{game.check}" 
                             disabled="#{game.number eq game.guess}"/>
          </div>
          <div>
            <h:commandButton id="RestartButton" value="Reset" action="#{game.reset}" immediate="true" />
          </div>
       </h:form>
    </ui:define>
  </ui:composition>
</html
>
1

Facelets is a templating language for JSF, here we are wrapping our page in a template which defines the header.

2

There are a number of messages which can be sent to the user, "Higher!", "Lower!" and "Correct!"

3

As the user guesses, the range of numbers they can guess gets smaller - this sentance changes to make sure they know what range to guess in.

4

This input field is bound to a Web Bean, using the value expression.

5

A range validator is used to make sure the user doesn't accidentally input a number outside of the range in which they can guess - if the validator wasn't here, the user might use up a guess on an out of range number.

6

And, of course, there must be a way for the user to send their guess to the server. Here we bind to an action method on the Web Bean.

Das Beispiel besteht aus 4 Klassen, wobei die ersten beiden Binding-Typen sind. Zunächst gibt es den @Random Binding-Typ, der zur Einspeisung einer zufälligen Zahl dient:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface Random {}

Es gibt außerdem den @MaxNumber Binding-Typ, der zur Einspeisung der maximalen Zahl, die eingespeist werden kann, verwendet wird:

@Target( { TYPE, METHOD, PARAMETER, FIELD })

@Retention(RUNTIME)
@Documented
@BindingType
public @interface MaxNumber {}

Die Generator-Klasse ist verantwortlich für die Erstellung der zufälligen Zahl via einer Producer-Methode. Sie legt auch die mögliche Maximalzahl via einer maximum Producer-Methode offen:

@ApplicationScoped

public class Generator {
   
   private java.util.Random random = new java.util.Random( System.currentTimeMillis() );
   
   private int maxNumber = 100;
   
   java.util.Random getRandom()
   {
      return random;
   }
   
   @Produces @Random int next() { 
      return getRandom().nextInt(maxNumber); 
   }
   
   @Produces @MaxNumber int getMaxNumber()
   {
      return maxNumber;
   }
}

Sie werden feststellen, dass der Generator anwendungsbegrenzt ist; daher erhalten wir nicht jedes Mal ein anderes Zufallsergebnis.

Das letzte Web Bean in der Anwendung ist das sessionbegrenzte Game.

Sie werden bemerken, dass wir die @Named-Annotation verwendet haben, damit wir das Bean durch EL in der JSF-Seite verwenden können. Zu guter Letzt haben wir Konstruktor-Einspeisung zur Initialisierung des Spiels mit Zufallszahl verwendet. Und natürlich müssen wir dem Spieler mitteilen, wenn er gewonnen hat, daher bieten wir Feedback mittels einer FacesMessage.

package org.jboss.webbeans.examples.numberguess;



import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.webbeans.AnnotationLiteral;
import javax.webbeans.Current;
import javax.webbeans.Initializer;
import javax.webbeans.Named;
import javax.webbeans.SessionScoped;
import javax.webbeans.manager.Manager;
@Named
@SessionScoped
public class Game
{
   private int number;
   
   private int guess;
   private int smallest;
   private int biggest;
   private int remainingGuesses;
   
   @Current Manager manager;
   
   public Game()
   {
   }
   
   @Initializer
   Game(@MaxNumber int maxNumber)
   {      
      this.biggest = maxNumber;
   }
   public int getNumber()
   {
      return number;
   }
   
   public int getGuess()
   {
      return guess;
   }
   
   public void setGuess(int guess)
   {
      this.guess = guess;
   }
   
   public int getSmallest()
   {
      return smallest;
   }
   
   public int getBiggest()
   {
      return biggest;
   }
   
   public int getRemainingGuesses()
   {
      return remainingGuesses;
   }
   
   public String check()
   {
      if (guess
>number)
      {
         biggest = guess - 1;
      }
      if (guess<number)
      {
         smallest = guess + 1;
      }
      if (guess == number)
      {
         FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Correct!"));
      }
      remainingGuesses--;
      return null;
   }
   
   @PostConstruct
   public void reset()
   {
      this.smallest = 0;
      this.guess = 0;
      this.remainingGuesses = 10;
      this.number = manager.getInstanceByType(Integer.class, new AnnotationLiteral<Random
>(){});
   }
   
}

Beim translator-Beispiel werden die von Ihnen eingegebenen Sätze ins Lateinische übersetzt.

Das translator-Beispiel ist eine ear und enthält EJBs. Als Folge ist seine Struktur komplexer als die desnumberguess-Beispiels.

Werfen wir zunächst einen Blick auf den ear-Aggregator, das sich im webbeans-translator-ear-Modul befindet. Maven generiert automatisch die application.xml für uns:


<plugin>
   <groupId
>org.apache.maven.plugins</groupId>
   <artifactId
>maven-ear-plugin</artifactId>
   <configuration>
      <modules>
         <webModule>
            <groupId
>org.jboss.webbeans.examples.translator</groupId>
            <artifactId
>webbeans-translator-war</artifactId>
            <contextRoot
>/webbeans-translator</contextRoot>
         </webModule>
      </modules>
   </configuration>
</plugin
>

Hier setzen wir den Kontextpfad, der uns eine schöne url liefert (http://localhost:8080/webbeans-translator).

Tipp

Falls Sie zur Generierung dieser Dateien nicht Maven verwendet haben, benötigen Sie META-INF/application.xml:


<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://java.sun.com/xml/ns/javaee" 
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd"
             version="5">
  <display-name
>webbeans-translator-ear</display-name>
  <description
>Ear Example for the reference implementation of JSR 299: Web Beans</description>
  
  <module>
    <web>
      <web-uri
>webbeans-translator.war</web-uri>
      <context-root
>/webbeans-translator</context-root>
    </web>
  </module>
  <module>
    <ejb
>webbeans-translator.jar</ejb>
  </module>
</application
>

Next, lets look at the war. Just as in the numberguess example, we have a faces-config.xml (to enable Facelets) and a web.xml (to enable JSF) in WebContent/WEB-INF.

Interessanter ist das zur Übersetzung des Texts verwendete Facelet. Ganz wie im numberguess-Beispiel besitzen wir eine Vorlage, die das Formular umgibt (hier der Kürze wegen weggelassen):


<h:form id="NumberGuessMain">
            
   <table>
      <tr align="center" style="font-weight: bold" >
         <td>
            Your text
         </td>
         <td>
            Translation
         </td>
      </tr>
      <tr>
         <td>
            <h:inputTextarea id="text" value="#{translator.text}" required="true" rows="5" cols="80" />
         </td>
         <td>
            <h:outputText value="#{translator.translatedText}" />
         </td>
      </tr>
   </table>
   <div>
      <h:commandButton id="button" value="Translate" action="#{translator.translate}"/>
   </div>
   
</h:form
>

Der Benutzer kann Text im Textbereich links eingeben und dann die translate-Schaltfläche drücken (zur Übersetzung), um auf der rechten Seite das Ergebnis zu sehen.

Sehen wir uns schließlich noch das ejb-Modul webbeans-translator-ejb an. In src/main/resources/META-INF existiert nur eine leere web-beans.xml, die dazu dient das Archiv als Web Beans enthaltend zu markieren.

Wir haben uns das Interessanteste bis zuletzt aufgehoben, nämlich den Code! Das Projekt besitzt zwei einfache Beans, SentenceParser und TextTranslator und zwei Enterprise Beans, TranslatorControllerBean und SentenceTranslator. Sie sind wahrscheinlich schon weitehend vertraut damit, wie Web Bean aussehen, daher gehen wir hier nur auf die interessantesten Aspekte ein.

Sowohl bei SentenceParser als auch bei TextTranslator handelt es sich um abhängige Beans und TextTranslator verwendet Konstruktor-Initialisierung :

public class TextTranslator { 

   private SentenceParser sentenceParser; 
   private Translator sentenceTranslator; 
   
   @Initializer
   TextTranslator(SentenceParser sentenceParser, Translator sentenceTranslator) 
   { 
      this.sentenceParser = sentenceParser; 
      this.sentenceTranslator = sentenceTranslator;

TextTranslator ist ein stateless Bean (mit einem lokalen Business-Interface), wo alles passiert - natürlich konnten wir keinen kompletten Übersetzer entwickeln,, aber wir haben uns Mühe gegeben!

Schließlich gibt es noch den UI-orientierten Kontroller, der den Text vom Benutzer nimmt und ihn an den translator (Übersetzer) weitergibt. Hierbei handelt es sich um ein anfragenbegrenztes, benanntes, stateful Session Bean, das den translator einspeist.

@Stateful

@RequestScoped
@Named("translator")
public class TranslatorControllerBean implements TranslatorController
{
   
   @Current TextTranslator translator;

Das Bean besitzt auch Getter und Setter für alle Felder auf der Seite.

Da es sich um ein stateful Session Bean handelt, müssen wir eine remove-Methode besitzen:

   @Remove

   public void remove()
   {
      
   }

Der Web Beans Manager ruft die remove-Methode für Sie auf, wenn das Bean gelöscht wird, in diesem Fall am Ende der Anfrage.

That concludes our short tour of the Web Beans examples. For more on Web Beans , or to help out, please visit http://www.seamframework.org/WebBeans/Development.

Wir brauche Unterstützung auf allen Gebieten - Fehlerbehebung, Schreiben neuer Features, Schreiben neuer Beispiele und bei der Übersetzung dieses Referenzhandbuchs.