SeamFramework.orgCommunity Documentation

Capitolo 3. Getting started with Web Beans, the Reference Implementation of JSR-299

3.1. Usare JBoss AS 5
3.2. Usare Apache Tomcat 6.0
3.3. Usare GlassFish
3.4. Esempio Indovina Numero
3.4.1. The numberguess example in Tomcat
3.4.2. The numberguess example for Apache Wicket
3.4.3. The numberguess example for Java SE with Swing
3.5. Esempio Traduttore

Web Beans viene sviluppato all'indirizzo the Seam project. Si può scaricare l'ultima release di Web Beans dalla pagina di download.

Web Beans viene distribuito con due applicazioni deployabili d'esempio, webbeans-numberguess, un esempio in formato war, che contiene solo bean semplici, e webbeans-translator, un esempio in formato ear, che contiene bean enterprise. Ci sono anche due varianti dell'esempio Indovina Numero, per Tomcat e con jsf2, che può essere usato con JSF2. Per eseguire gli esempi occorre fare le seguenti cose:

Occorre scaricare JBoss AS 5.0.1.GA da jboss.org, e scompattarlo. Per esempio:

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

Scaricare Web Beans da seamframework.org, e scompattarlo. Per esempio

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

Quindi, occorre indicare a Web Beans dove è stato installato JBoss. Modificate il file jboss-as/build.properties e valorizzate la proprietà jboss.home. Per esempio:

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

Per installare Web Beans, occorre avere installato Ant 1.7.0, e avere valorizzato la variabile d'ambiente ANT_HOME. Per esempio:

Nota

JBoss 5.1.0 comes with Web Beans built in, so there is no need to update the server.

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

Quindi, è possibile installare gli aggiornamenti. Lo script di aggiornamento userà Maven per scaricare automaticamente Web Beans.

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

Ora, siete pronti a fare il deploy del primo esempio!

Suggerimento

Gli script di build degli esempio offrono una quantità di target per JBoss AS, quali:

  • ant restart - fa il deploy dell'esempio in formato esploso

  • ant explode - aggiorna un esempio in formato esploso, senza riavviare il deploy

  • ant deploy - fa il deploy dell'esempio in formato jar compresso

  • ant undeploy - rimuove l'esempio dal server

  • ant clean - ripulisce l'esempio

Per fare il deploy dell'esempio Indovina Numero:

$ cd examples/numberguess
ant deploy

Avviare JBoss AS:

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

Suggerimento

Se si usa Windows, si usi lo script run.bat.

Attendete che l'applicazione sia installata, e godetevi ore di divertimento all'indirizzo http://localhost:8080/webbeans-numberguess!

Web Beans include un secondo semplice esempio che tradurrà i vostri testi in Latino. L'esempio Indovina Numero è in formato war, e usa soltanto bean semplici; l'esempio col traduttore è in formato ear, e include dei bean enterprise, assemblati in un modulo EJB. Per provarlo:

$ cd examples/translator
ant deploy

Attendete che l'applicazione sia installata, e visitate l'indirizzo http://localhost:8080/webbeans-translator!

Scaricare Tomcat 6.0.18 o successivo da tomcat.apache.org, e scompattarlo. Per esempio

$ cd /Applications
$ unzip ~/apache-tomcat-6.0.18.zip

Scaricare Web Beans da seamframework.org, e scompattarlo. Per esempio

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

Quindi, occorre indicare a Web Beans dove è stato installato Tomcat. Modificate il file jboss-as/build.properties e valorizzate la proprietà tomcat.home. Per esempio:

tomcat.home=/Applications/apache-tomcat-6.0.18

Suggerimento

Gli script di build degli esempi offrono una quantità di target per Tomcat, quali:

  • ant tomcat.restart - esegue il deploy dell'esempio in formato esploso

  • ant tomcat.explode - aggiorna un esempio in formato esploso, senza riavviare il deploy

  • ant tomcat.deploy - esegue il deploy dell'esempio in formato jar compresso

  • ant tomcat.undeploy - rimuove l'esempio dal server

  • ant tomcat.clean - ripulisce l'esempio

Per eseguire il deploy dell'esempio Indovina Numero per tomcat:

$ cd examples/tomcat
ant tomcat.deploy

Avviare Tomcat:

$ /Applications/apache-tomcat-6.0.18/bin/startup.sh

Suggerimento

Se si usa Windows, si usi lo script startup.bat.

Attendete che l'applicazione sia installata, e godetevi ore di divertimento all'indirizzo http://localhost:8080/webbeans-numberguess!

DA FARE

Nell'applicazione Indovina Numero avete a disposizione 10 tentativi per indovinare un numero tra 1 e 100. Dopo ciascun tentativo, siete informati se siete stati troppo alti o troppo bassi.

L'esempio Indovina Numero comprende un certo numero di Web Bean, file di configurazione e pagine JSF, assemblati in un war. Iniziamo dai file di configurazione.

Tutti i file di configurazione di questo esempio si trovano in WEB-INF/, che è situato in WebContent nell'albero dei sorgenti. Innanzitutto, c'è faces-config.xml, in cui JSF viene informata di usare Facelets:


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

Vi è un file vuoto web-beans.xml, che identifica l'applicazione come applicazione Web Beans.

Infine c'è 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 id="NumberGuessMain">
          <div(2) 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>
             I(3)'m thinking of a number between #{game.smallest} and #{game.biggest}.
             You have #{game.remainingGuesses} guesses.
          </div>
     
          <div>
             Your guess: 
             <(4)h:inputText id="inputGuess" 
                          value="#{game.guess}" 
                          required="true" 
                          size="3" 
                          disabled="#{game.number eq game.guess}">
              (5)  <f:validateLongRange maximum="#{game.biggest}" 
                                     minimum="#{game.smallest}"/>
             </h:inputText>
            <h(6):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.

L'esempio consiste di 4 classi, delle quali le prime due sono tipi di binding. Innanzitutto, c'è il tipo di binding @Random, usato per iniettare un numero casuale:

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

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

C'è anche il tipo di binding @MaxNumber, usato per iniettare il numero massimo iniettatabile:

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

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

Alla classe Generator è affidata la generazione del numero casuale, per mezzo di un metodo produttore. Inoltre essa espone il massimo numero possibile attraverso un metodo produttore:

@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;
   }
}

E' possibile notare che Generator ha scope applicazione; quindi non si ottiene un diverso numero casuale ogni volta.

Il Web Bean finale nell'applicazione è Game avente scope di sessione.

Si noti anche che è stata usata l'annotazione @Named, in modo che sia possibile usare il bean in espressioni EL presenti nelle pagine JSF. Infine, si è utilizzata l'iniezione del costruttore per inizializzare il gioco con un numero casuale. E naturalmente, è necessario dire al giocatore se ha vinto, informazione di feedback che viene fornita con un 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
>(){});
   }
   
}

Whilst JSR-299 specifies integration with Java ServerFaces, Web Beans allows you to inject into Wicket components, and also allows you to use a conversation context with Wicket. In this section, we'll walk you through the Wicket version of the numberguess example.

Like the previous example, the Wicket WebBeans examples make use of the webbeans-servlet module. The use of the Jetty servlet container is common in the Wicket community, and is chosen here as the runtime container in order to facilitate comparison between the standard Wicket examples and these examples, and also to show how the webbeans-servlet integration is not dependent upon Tomcat as the servlet container.

These examples make use of the Eclipse IDE; instructions are also given to deploy the application from the command line.

JSF uses Unified EL expressions to bind view layer components in JSP or Facelet views to beans, Wicket defines it's components in Java. The markup is plain html with a one-to-one mapping between html elements and the view components. All view logic, including binding of components to models and controlling the response of view actions, is handled in Java. The integration of Web Beans with Wicket takes advantage of the same binding annotations used in your business layer to provide injection into your WebPage subclass (or into other custom wicket component subclasses).

The code in the wicket numberguess example is very similar to the JSF-based numberguess example. The business layer is identical!

Differences are:

This example can be found in the examples/se/numberguess folder of the Web Beans distribution.

To run this example:

There is an empty beans.xml file in the root package (src/main/resources/beans.xml), which marks this application as a Web Beans application.

The game's main logic is located in Game.java. Here is the code for that class, highlighting the changes made from the web application version:

(1)(2)@ApplicationScoped

public class Game implements Serializable
{
    private int number;
    private int guess;
    private int smallest;
    @MaxNumber
    private int maxNumber;
    private int biggest;
    private int remainingGuesses;
    private boolean validNumberRange = true;
    @Current Generator rndGenerator;
    ...
    public boolean isValidNumberRange()
    {
        return validNumberRange;
    }
    public boolean isGameWon()
(3)    {
        return guess == number;
    }
    public boolean isGameLost()
    {
        return guess != number && remainingGuesses <= 0;
    }
    public boolean check()
    {
        boolean result = false;
(4)        if ( checkNewNumberRangeIsValid() )
        {
            if ( guess > number )
            {
                biggest = guess - 1;
            }
            if ( guess < number )
            {
                smallest = guess + 1;
            }
            if ( guess == number )
            {
                result = true;
            }
            remainingGuesses--;
        }
        return result;
    }
    private boolean checkNewNumberRangeIsValid()
    {
        return validNumberRange = ( ( guess >= smallest ) && ( guess <= biggest ) );
    }
    @PostConstruct
(5)    public void reset()
    {
        this.smallest = 0;
        ...
        this.number = rndGenerator.next();
    }
}
1

The bean is application scoped instead of session scoped, since an instance of the application represents a single 'session'.

2

The bean is not named, since it doesn't need to be accessed via EL

3

There is no JSF FacesContext to add messages to. Instead the Game class provides additional information about the state of the current game including:

  • If the game has been won or lost

  • If the most recent guess was invalid

This allows the Swing UI to query the state of the game, which it does indirectly via a class called MessageGenerator, in order to determine the appropriate messages to display to the user during the game.

4

Validation of user input is performed during the check() method, since there is no dedicated validation phase

5

The reset() method makes a call to the injected rndGenerator in order to get the random number at the start of each game. It cannot use manager.getInstanceByType(Integer.class, new AnnotationLiteral<Random>(){}) as the JSF example does because there will not be any active contexts like there is during a JSF request.

The MessageGenerator class depends on the current instance of Game, and queries its state in order to determine the appropriate messages to provide as the prompt for the user's next guess and the response to the previous guess. The code for MessageGenerator is as follows:

public class MessageGenerator

{
(1)    @Current Game game;
(2)    public String getChallengeMessage()
    {
        StringBuilder challengeMsg = new StringBuilder( "I'm thinking of a number between " );
        challengeMsg.append( game.getSmallest() );
        challengeMsg.append( " and " );
        challengeMsg.append( game.getBiggest() );
        challengeMsg.append( ". Can you guess what it is?" );
        return challengeMsg.toString();
    }
(3)    public String getResultMessage()
    {
        if ( game.isGameWon() )
        {
            return "You guess it! The number was " + game.getNumber();
        } else if ( game.isGameLost() )
        {
            return "You are fail! The number was " + game.getNumber();
        } else if ( ! game.isValidNumberRange() )
        {
            return "Invalid number range!";
        } else if ( game.getRemainingGuesses() == Game.MAX_NUM_GUESSES )
        {
            return "What is your first guess?";
        } else
        {
            String direction = null;
            if ( game.getGuess() < game.getNumber() )
            {
                direction = "Higher";
            } else
            {
                direction = "Lower";
            }
            return direction + "! You have " + game.getRemainingGuesses() + " guesses left.";
        }
    }
}
1

The instance of Game for the application is injected here.

2

The Game's state is interrogated to determine the appropriate challenge message.

3

And again to determine whether to congratulate, console or encourage the user to continue.

Finally we come to the NumberGuessFrame class which provides the Swing front end to our guessing game.

public class NumberGuessFrame  extends javax.swing.JFrame

{
(1)    private @Current Game game;
(2)    private @Current MessageGenerator msgGenerator;
(3)    public void start( @Observes @Deployed Manager manager )
    {
        java.awt.EventQueue.invokeLater( new Runnable()
            {
                public void run()
                {
                    initComponents();
                    setVisible( true );
                }
            } );
    }
(4)    private void initComponents() {
        buttonPanel = new javax.swing.JPanel();
        mainMsgPanel = new javax.swing.JPanel();
        mainLabel = new javax.swing.JLabel();
        messageLabel = new javax.swing.JLabel();
        guessText = new javax.swing.JTextField();
        ...
        mainLabel.setText(msgGenerator.getChallengeMessage());
        mainMsgPanel.add(mainLabel);
        messageLabel.setText(msgGenerator.getResultMessage());
        mainMsgPanel.add(messageLabel);
        ...
    }
(5)    private void guessButtonActionPerformed( java.awt.event.ActionEvent evt )
    {
        int guess =  Integer.parseInt(guessText.getText());
        game.setGuess( guess );
        game.check();
        refreshUI();
    }
(6)    private void replayBtnActionPerformed( java.awt.event.ActionEvent evt )
    {
       game.reset();
       refreshUI();
    }
(7)    private void refreshUI()
    {
        mainLabel.setText( msgGenerator.getChallengeMessage() );
        messageLabel.setText( msgGenerator.getResultMessage() );
        guessText.setText( "" );
        guessesLeftBar.setValue( game.getRemainingGuesses() );
        guessText.requestFocus();
    }
    // swing components
    private javax.swing.JPanel borderPanel;
    ...
    private javax.swing.JButton replayBtn;
}
1

The injected instance of the game (logic and state).

2

The injected message generator for UI messages.

3

This application is started in the usual Web Beans SE way, by observing the @Deployed Manager event.

4

This method initialises all of the Swing components. Note the use of the msgGenerator.

5

guessButtonActionPerformed is called when the 'Guess' button is clicked, and it does the following:

  • Gets the guess entered by the user and sets it as the current guess in the Game

  • Calls game.check() to validate and perform one 'turn' of the game

  • Calls refreshUI. If there were validation errors with the input, this will have been captured during game.check() and as such will be reflected in the messeges returned by MessageGenerator and subsequently presented to the user. If there are no validation errors then the user will be told to guess again (higher or lower) or that the game has ended either in a win (correct guess) or a loss (ran out of guesses).

6

replayBtnActionPerformed simply calls game.reset() to start a new game and refreshes the messages in the UI.

7

refreshUI uses the MessageGenerator to update the messages to the user based on the current state of the Game.

L'esempio Traduttore prende le frasi che vengono inserite e le traduce in latino.

L'esempio Traduttore è assemblato in un ear, e contiene EJB. Di conseguenza, la sua struttura è più complessa di quella dell'esempio Indovina Numero.

Innanzitutto, diamo un'occhiata all'aggregatore ear, che è situato nel modulo webbeans-translator-ear. Maven genera automaticamente il file application.xml:


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

Qua viene impostato il context path, in modo da avere un url gradevole (http://localhost:8080/webbeans-translator).

Suggerimento

Se non si sta usando Maven per generare questi file, sarebbe necessario avere il file 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
>

Quindi, esaminiamo il war. Proprio come nell'esempio Indovina Numero, abbiamo un faces-config.xml (per abilitare Facelets) e un web.xml (per abilitare JSF) in WebContent/WEB-INF.

Più interessante è il facelet usato per tradurre il testo. Proprio come nell'esempio Indovina Numero c'è un template, che circoscrive la form (qui omessa per brevità):


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

L'utente può inserire del testo nell'area di testo sulla sinistra e premere il pulsante di traduzione per vedere il risultato sulla destra.

Infine, si esamini il modulo ejb, webbeans-translator-ejb. In src/main/resources/META-INF si trova un file vuoto web-beans.xml, usato per marcare l'archivio come contenente Web Beans.

Abbiamo lasciato per ultimo il boccone più prelibato, il codice! Il progetto ha due bean semplici, SentenceParser e TextTranslator e due bean enterprise, TranslatorControllerBean e SentenceTranslator. Dovreste ormai essere piuttosto familiari all'aspetto di un Web Bean, così ci limiteremo a evidenziare le parti più interessanti.

Sia SentenceParser che TextTranslator sono bean dependenti, e TextTranslator usa l'inizializzazione via costruttore:

public class TextTranslator { 

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

TextTranslator è un bean stateless (con un'interfaccia business locale), dove avviene la magia - naturalmente, non potevamo sviluppare un traduttore completo, ma gli abbiamo dato un buon avvio!

Infine, vi è un controller orientato all'UI, che raccoglie il testo dall'utente, e lo invia al traduttore. Questo è un bean stateful di sessione, dotato di nome, con scope richiesta, in cui viene iniettato il traduttore.

@Stateful

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

Il bean possiede pure dei metodi getter e setter per tutti i campi della pagina.

Poichè si tratta di un bean stateful di sessione, è necessario un metodo di rimozione (remove method):

   @Remove

   public void remove()
   {
      
   }

Il manager Web Beans chiamerà il metodo di rimozione quando il bean verrà distrutto; in questo caso al termine della richiesta.

Ciò conclude il nostro breve tour degli esempi della RI di Web Beans. Per saperne di più, o per trovare ulteriore aiuto, per favore visitate http://www.seamframework.org/WebBeans/Development.

Abbiamo bisogno di aiuto in tutte le aree - soluzione dei bug, scrittura di nuove caratteristiche ed esempi e traduzione di questa guida.