SeamFramework.orgCommunity Documentation

Capítulo 3. Getting started with Web Beans, the Reference Implementation of JSR-299

3.1. Utilizando o JBoss AS 5
3.2. Utilizando o Apache Tomcat 6.0
3.3. Utilizando o GlassFish
3.4. O exemplo numberguess
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. O exemplo translator

A Web Beans está sendo desenvolvida no projeto Seam. Você pode baixar a última versão da Web Beans na página de downloads.

A Web Beans vem com duas aplicações de exemplo: webbeans-numberguess - um war, contendo apenas beans simples (simple beans) e webbeans-translator, e um ear, contendo beans corporativos (enterprise beans) -. Existem ,ainda, duas variações do exemplo numberguess: o exemplo tomcat (adequado para a implantação no Tomcat) e o exemplo jsf2, que você pode usar se estiver utilizando JSF2. Para executar os exemplos, você precisará do seguinte:

Você precisará fazer o download do JBoss AS 5.0.1.GA em jboss.org e descompactá-lo. Por exemplo:"

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

Depois, faça o download da Web Beans em seamframework.org e descompacte-o. Por exemplo

$ cd ~/
$ unzip ~/webbeans-1.0.0.ALPHA1.zip

Em seguida, temos de dizer aos Web Beans onde o JBoss está localizado. Editar o jboss-as/build.properties e definir a propriedade jboss.home. Por exemplo:

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

Para instalar a Web Beans, você precisará do Ant 1.7.0 instalado e a variável de ambiente ANT_HOME setada. Por exemplo:

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

Então, você pode instalar a atualização. O script de atualização utilizará o Maven para fazer o download da Web Beans automaticamente.

$ cd webbeans-1.0.0.ALPHA1/jboss-as
$ ant update

Agora, você está pronto para fazer a publicação do seu primeiro exemplo!

Dica

Os scripts para criar os exemplos oferecem uma série de alvos para JBoss AS, entre eles:

  • ant restart - implanta o exemplo no formato explodido

  • ant explode - atualiza o exemplo explodido, sem reiniciar

  • ant deploy - implanta o exemplo no formato jar compactado

  • ant undeploy - remove o exemplo do servidor

  • ant clean - limpa o exemplo

Para implantar o exemplo numberguess:

$ cd examples/numberguess
ant deploy

Inicializando o JBoss AS:

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

Dica

Se você utiliza o Windows, utilize o script run.bat.

Aguarde até que a aplicação seja implantada, e desfrute de horas de diversão em http://localhost:8080/webbeans-numberguess!

Web Beans inclui um segundo exemplo simples que irá traduzir o seu texto para o Latim. O exemplo numberguess é um war e usa apenas beans simples; o exemplo translator é um exemplo ear e inclui benas corporativos, empacotados em um módulo EJB. Para experimentá-lo:

$ cd examples/translator
ant deploy

Aguarde até que a aplicação seja implantada, e acesse http://localhost:8080/webbeans-translator!

Depois, faça o download do Tomcat 6.0.18 ou posterior em tomcat.apache.org, e descompacte-o. Por exemplo

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

Depois, faça o download da Web Beans em seamframework.org e descompacte-o. Por exemplo

$ cd ~/
$ unzip ~/webbeans-1.0.0.ALPHA1.zip

Em seguida, temos de dizer aos Web Beans onde o Tomcat está localizado. Editar o jboss-as/build.properties e definir a propriedade tomcat.home. Por exemplo:

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

Dica

Os scripts para criar os exemplos oferecem uma série de alvos para Tomcat. São eles:

  • ant tomcat.restart - publica o exemplo no formato explodido

  • ant tomcat.explode - atualiza o exemplo explodido, sem reiniciar

  • ant tomcat.deploy - publica o exemplo no formato jar compactado

  • ant tomcat.undeploy - remove o exemplo do servidor

  • ant tomcat.clean - clean the example

Para implantar o exemplo numberguess no tomcat:

$ cd examples/tomcat
ant tomcat.deploy

Inicializando o Tomcat:

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

Dica

Se você utiliza o Windows, utilize o script startup.bat.

Aguarde até que a aplicação seja implantada, e desfrute de horas de diversão em http://localhost:8080/webbeans-numberguess!

TODO

Na aplicação numberguess você terá 10 tentativas para adivinhar um número entre 1 e 100. Após cada tentativa, você será informado se você disse muito acima, ou muito abaixo.

O exemplo numberguess é composto de um número de Web Beans, arquivos de configuração e páginas Facelet JSF , empacotados como um war. Vamos começar com os arquivos de configuração.

Todos os arquivos de configuração para este exemplo estão localizados no WEB-INF/, que é armazenado no WebContent na árvore de fontes. Primeiro, temos faces-config.xml, onde dizemos para o JSF usar o 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
>

Existe um arquivo web-beans.xml vazio, que assinala essa aplicação como uma aplicação Web Beans.

Finalmente no 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.

No exemplo, existem 4 classes: as duas primeiras são tipos de binding. Primeiro, há o tipo de binding @Random, utilizado para a injeção de um número aleatório:

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

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

Há também o binding type @MaxNumber, utilizado para injetar o número máximo que pode ser injetado:

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

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

A classe Generator é responsável por criar um número aleatório, através de um método produtor. Ela também expõe o número máximo possível através de um método produtor:

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

Você perceberá que o Generator está no escopo de aplicação; portanto, não obtemos um número aleatório diferente a cada vez.

O Web Bean final da aplicação é o Game em escopo de sessão.

Você notará que nós utilizamos a anotação @Named, para que possamos utilizar o bean através EL na página JSF. Finalmente, utilizamos injeção de construtor para inicializar o jogo com um número aleatório. E, claro, precisamos de dizer ao jogador quando ele venceu. Por isso, informaremos através do 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.

O exemplo translator pegará qualquer frase que você fornecer e a traduzirá para o Latim.

O exemplo translator é construído como um ear e contém EJBs. Como resultado, a sua estrutura é mais complexa do que o exemplo numberguess.

Primeiro, vamos dar uma olhada no ear aggregator, que está localizado módulo webbeans-translator-ear. Maven gera automaticamente oapplication.xml para nós:


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

Aqui nós definiremos o caminho do contexto, que nos dá uma url amigável (http://localhost:8080/webbeans-translator). ulink>) .

Dica

Se você não está usando o Maven para gerar esses arquivos, você precisaria 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
>

Em seguida, vamos ver o war. Tal como no exemplo numberguess, temos um faces-config.xml (para habilitar o Facelets) e um web.xml (para habilitar o JSF) no WebContent/WEB-INF.

Mais interessante é o facelet utilizado para traduzir texto. Tal como no exemplo numberguess, temos um template, que envolve o formulário (omitido aqui por brevitude):


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

O usuário pode digitar um texto no textarea esquerdo e clicar no botão traduzir para ver o resultado à direita.

Por fim, vamos olhar o módulo EJB webbeans-translator-ejb. Em src/main/resources/META-INF existe apenas um web-beans.xml vazio, utilizado para marcar o arquivo como contendo Web Beans.

Deixamos o pedaço mais interessante para o final: o código! O projeto tem dois beans simples, SentenceParser e TextTranslator e dois beans corporativosTranslatorControllerBean e SentenceTranslator. Você deve estar bastante familiarizado com o que um Web Bean parece até agora. Então, vamos apenas destacar as partes mais interessantes aqui.

Tanto SentenceParser quanto TextTranslator são beans dependentes, e TextTranslator usa inicialização por construtor :

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

TextTranslator é um bean stateless (com uma interface de negócios local), onde a mágica acontece . Obviamente, não poderíamos desenvolver um tradutor completo, mas lhe demos um bom caminho!

Finalmente, há um controlador orientado à interface, que recolhe o texto do usuário e despacha para o tradutor. Esse é um escopo de requisição, com o nome, stateful session bean, que injeta o tradutor.

@Stateful

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

O bean também tem getters e setters para todos os campos da página.

Como esse é um stateful session bean, temos de ter um método de remoção:

   @Remove

   public void remove()
   {
      
   }

O gerenciador do Web Beans chamará o método remover para você quando o bean for destruído - neste caso, no final da requisição.

Está encerrado o nosso curto passeio pelos exemplos de Web Beans. Para mais informações sobre a Web Beans, ou para ajudar, por favor visite http://www.seamframework.org/WebBeans/Development.

Precisamos de ajuda em todas as áreas - correção de bugs, escrita de novas funcionalidades, escrita de exemplos e tradução deste guia de referência.