SeamFramework.orgCommunity Documentation

章 3. Web Bean 參照實做

3.1. numberguess 範例
3.2. 轉譯器範例

Web Bean 參照實作(Web Beans Reference Implementation)開發於 the Seam project。您可藉由 the downloads page 來下載最新開發人員版本的 Web Bean。

Web Bean RI 包含了兩個可建置的範例應用程式:webbeans-numberguess,這是個 war 範例,並且只包含著基本的 bean,以及 webbeans-translator,這是個 ear 範例,並包含著企業級的 bean。若要執行這些範例,您將需要滿足下列需求:

目前,Web Beans RI 只能在 JBoss Application Server 5 上執行。您將需要由 jboss.org 來下載 JBoss AS 5.0.0.GA,然後再將它解壓縮。例如:

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

接下來,請由 seamframework.org 下載 Web Beans RI 並將它解壓縮。例如

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

接下來,我們需要讓 Web Bean 知道 JBoss 的位置在哪裡。請編輯 jboss-as/build.properties 然後設置 jboss.home 內容。例如:

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

因為 Web Bean 是個新的軟體,您需要更新 JBoss AS 才可執行 Web Bean RI。未來版本的 JBoss AS 將會包含這些更新,因此這個步驟將可被忽略。

注

目前您需要兩個更新。首先一個新的建置器 webbeans.deployer 被新增了。這可為 JBoss AS 提供 Web Bean archive,並能讓 Web Bean RI 查詢 EJB3 container,並找出安裝在您應用程式中的 EJB 為何。接下來,您需要 JBoss EJB3 的更新。

若要安裝更新,您將需要先安裝 Ant 1.7.0,並設置 ANT_HOME 環境變數。例如:

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

接下來,您便可安裝更新。更新 script 將會使用 Maven 來自動地下載 Web Bean 以及 EJB3。

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

現在,您已準備好建置您的第一個範例!

提示

這些範例的建置 script 提供了一些目標,它們包含了:

  • ant restart - 以分解的格式來建置範例

  • ant explode - 在不重新建置的情況下更新一個已分解的範例

  • ant deploy - 以 jar 格式來建置範例

  • ant undeploy - 將範例由伺服器中移除

  • ant clean - 清除範例

若要建置 numberguess 範例:

$ cd examples/numberguess
ant deploy

Start JBoss AS:

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

提示

If you use Windows, use the run.batscript.

請等待應用程式的建置,然後在 http://localhost:8080/webbeans-numberguess 花上幾個小時!

Web Bean RI 包含了第二個基本範例,它可將您的文字轉譯為拉丁文。numberguess 範例為一個 war 範例,並且只使用基本的 bean;轉譯器範例則為 ear 範例,並且包含著企業級的 bean,封裝於一個 EJB 模組中。若要嘗試:

$ cd examples/translator
ant deploy

請等待應用程式的建置並進入 http://localhost:8080/webbeans-translator!

在 numberguess 應用程式中,您將會有 10 次機會來猜一個介於 1 至 100 之間的號碼。每當猜過一遍,系統便會告知您您所輸入的數字是否太大或太小。

numberguess 範例包含了 Web Bean 的一個數字、配置檔案,以及 Facelet JSF 頁面,並且封裝為 war。讓我們先從配置檔案開始。

此範例的所有配置檔案都位於 WEB-INF/ 中,並且它又儲存在來源樹中的 WebContent 裡。首先,我們有個可使用來指定 JSF 來使用 Facelet 的 faces-config.xml:


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

有個空的 web-beans.xml 檔案,它會將此應用程式標記為一個 Web Bean 應用程式。

最後為 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.

範例存有 4 個類別,前兩個為綁定類型。首先,有個使用來注入亂數號碼的 @Random 綁定類型:

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

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

還有個用來注入可注入之最大號碼的 @MaxNumber 綁定類型:

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

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

Generator 類別負責透過產生器的方式來建立亂數號碼。它也會透過一個產生器的方式來顯示最大的可能號碼:

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

您將會注意到 Generator 是屬於應用程式導向的;因此,我們不會每次都一定能得到不同的亂數號碼。

應用程式中最後的 Web Bean 為 session 導向的 Game。

您將會注意到我們使用了 @Named 標記,如此一來我們便可在 JSF 頁面中的 EL 上使用這個 bean。最後,我們使用了 constructor injection 來利用亂數號碼來初始化了這個遊戲。當然,當玩家贏時我們將需要告知玩家,並藉由一個 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
>(){});
   }
   
}

轉譯器範例能接受您所輸入的任何句子,然後將它們翻譯成拉丁文。

轉換器範例被建置為一個 ear 並包含著 EJB。正因如此,它的結構比 numberguess 範例要複雜得多。

首先,讓我們先來看一下 ear 聚合器,它位於 webbeans-translator-ear 模組中。Maven 會自動地為我們產生 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
>

在此我們將設置 context 路徑,它能提供給我們一個網址(http://localhost:8080/webbeans-translator)。

提示

若您不使用 Maven 來產生這些檔案,那麼您將需要 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
>

接下來,讓我們看一下 war。就和 numberguess 範例中一樣,我們在 WebContent/WEB-INF 中有個 faces-config.xml(用來啟用 Facelets)以及一個 web.xml(用來啟用 JSF)。

還有更有趣的就是使用來轉換文字的 facelet。就和 numberguess 範例中一樣,我們有個圍繞著 form 的頁面格式(在此將省略不提):


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

用戶可在左手邊的文字區域中輸入一些文字,然後點選轉譯按鈕並於右手邊的區域中查看結果。

最後,讓我們來看一下 ejb 模組 webbeans-translator-ejb。在 src/main/resources/META-INF 中只有一個用來將 archive 標記為包含著 Web Bean 的空 web-beans.xml。

我們將最有趣的部份保留到了最後,那就是程式碼!該專案含有兩個基本的 bean,SentenceParser 和 TextTranslator,以及兩個企業級的 bean,TranslatorControllerBean 和 SentenceTranslator。到了現在您應該已經很熟悉 Web Bean 長得如何了,因此我們在此將只著重於其它較有趣的部份。

SentenceParser 和 TextTranslator 兩者皆為相依性的 bean,並且 TextTranslator 使用了 constructor 初始化:

public class TextTranslator { 

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

TextTranslator 是個無狀態的 bean(以及一個本地的商業介面)- 當然,我們無法開發一個完整的轉譯器。

最後,有個 UI 導向的控制器,它會藉由用戶收集文字然後將它發送給轉譯器。這是個請求導向、被命名,而有狀態的 session bean,並且會注入轉譯器。

@Stateful

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

這個 bean 針對於頁面上所有欄位都有 getter 與 setter。

因為這是個 stateful(有狀態)的 session bean,因此我們必須要有個 remove method:

   @Remove

   public void remove()
   {
      
   }

當 bean 被毀掉後,Web Bean 管理員會為您調用 remove 這個 method;在此情況下為請求結束之後。

這結束了我們簡短的 Web Bean RI 範例。如欲取得更多有關於 RI 的相關資訊,或是提供相關協助,請參閱 http://www.seamframework.org/WebBeans/Development。

我們在所有層面都需要協助 - 錯誤修正、編寫新功能、編寫範例,以及翻譯此參照指南。