在這個五部分教學系列的前兩篇文章中,我們已經探討了 JavaServer Faces (JSF) 這個基於 Java 的框架,它簡化了為 Java EE 應用程式建構圖形使用者介面 (GUI) 的過程。我們成功地在 Apache Geronimo 上建構並部署了一個簡單的 JSF 應用程式,並學習了如何整合 Apache Tomahawk(提供數個與 JSF 100% 相容的元件)來改善介面。在本教學的第 3 部分中,我們將深入了解 Sun 的 Ajax4jsf 開放原始碼框架,這個框架能為 JSF 應用程式新增 Asynchronous JavaScript + XML (Ajax) 功能,而無需撰寫任何 JavaScript 程式碼。我們將使用 Ajax4jsf 來改進範例應用程式,並建立一個啟用 Ajax 的前端介面。
本教學旨在指導 Java 程式設計師如何使用 JSF 元件建構部署在 Apache Geronimo 上、高度互動的 Java EE 應用程式。教學假設讀者將使用 Eclipse IDE 作為開發平台。
本教學將講解 Sun Ajax4jsf 開放原始碼框架,這個框架能為 JSF 應用程式新增 Ajax 功能,而無需撰寫任何 JavaScript 程式碼。我們將繼續改進第 2 部分開發的範例應用程式,新增一個實用程式,能夠轉換溫度並以圖形化方式顯示結果。
本教學是關於使用 JSF 構建 Apache Geronimo 應用程式的五部分教學系列的第 3 部分。整個系列包含以下幾個部分:
為了學習本教學,需要有下列工具:
Ajax4jsf 允許開發人員將 Ajax 功能新增到 JSF 應用程式中,而不需要 JavaScript 或用 Ajax 圖形元件取代現有的元件。這個套件還允許在使用 Java 2D 程式庫時動態地產生圖像。Ajax 是一種程式設計技術,它處理只有頁面的一部分需要處理而不需要重新載入整個頁面的情況。這種方式的好處包括減少伺服器上的處理時間以及加快客戶端的回應速度。
與 Tomahawk 相似,Ajax4jsf 提供了一套可以很容易地與 JSF 標記一起使用的標記。在本教學後面對 Developer Forum Signup 範例應用程式進行改進時,將討論這些標記的範例以及如何將 Ajax4jsf 與 Eclipse 進行整合。接下來,將學習組成這個程式庫的每個元件、在使用它時必須記住的限制以及如何在 Eclipse 專案中新增 Ajax4jsf。
Ajax4jsf 元件被設計成可以輕鬆地與現有的 JSF 應用程式進行整合,同時提供了改進效能的 Ajax 功能。表 1 中描述了 Ajax4jsf 程式庫中包含的所有標記。
| Ajax4jsf 元件 | 描述 |
|---|---|
| <a4j:actionListener> | 其效果類似於 JSF 中的 <f:actionListener> 或 <f:valueChangeListener> 標記,但是它用於 Ajax 容器。 |
| <a4j:actionparam> | 結合了 <f:actionListener> 和 <f:param> JSF 標記的功能。 |
| <a4j:commandButton> | 效果類似於被點擊時的表單 Submit 按鈕,但是呈現為 HTML <input> 標記。 |
| <a4j:commandLink> | 效果類似於被點擊時的表單 Submit 按鈕,但是呈現為 HTML <a> 錨標記。 |
| <a4j:loadBundle> | 載入當前視圖的本地化資源束。 |
| <a4j:mediaOutput> | 允許建立動態產生的內容。 |
| <a4j:outputPanel> | 在頁面上建立一個啟用 Ajax 的部分,允許這個區域中的內容重新呈現(即使導致 Ajax 請求的元件沒有專門提到這個區域)。 |
| <a4j:page> | 呈現完整的 HTML 頁面結構。但是,它必須是 JSF <f:view> 標記的第一個和唯一的子元素,在它外邊不能有 HTML 程式碼。 |
| <a4j:region> | 決定 JSF <f:view> 的哪個部分將透過為相關聯的 Ajax 請求提供內容來處理。這會只更新頁面中需要更新的部分,從而改進效能。 |
| <a4j:status> | 透過指出請求何時開始或結束,提供關於指定區域的 Ajax 請求的客戶端狀態。 |
| <a4j:support> | 將 Ajax 功能新增到與 JSF 相關的元件中。這個標記可能是最常用的。 |
根據場景,某些元件會比其他元件更常用。接下來,將學習在使用 Ajax4jsf 時必須記住的條件。
根據 Ajax4jsf 文件所述,開發人員必須記住某些限制才能建立正確的 Ajax 和 JSF 應用程式:
既然已經了解了元件和使用 Ajax4jsf 的條件,就需要將這個程式庫新增到 Eclipse 專案中,然後才能對 Developer Forum Signup 應用程式進行改進。
本教學中的第 2 部分介紹了將 Tomahawk 作為 JSF 元件程式庫新增進專案中的步驟。使用同樣的步驟將 Ajax 作為 JSF 元件程式庫新增進專案中。
devSignup 專案,並在右鍵選單中選擇 Properties。Eclipse 顯示 Properties 對話方塊。Ajax4jsf)。ajax4jsf.jaroscache-2.3.2.jar為了啟用 Ajax4jsf 元件,必須在應用程式的 web.xml 檔案中宣告相應的 <filter> 標記。
devSignup 專案下面的 WebContent/WEB-INF/web.xml。如果編輯器仍然處於 Design 模式,那麼必須點擊編輯器底部的 Source 選項卡,切換到 Source 模式。web.xml 檔案中。<filter> 和 <filter-mapping> 元素應該是 <web-app> 元素的子元素。<filter>
<display-name>Ajax4jsf Filter</display-name>
<filter-name>ajax4jsf</filter-name>
<filter-class>org.ajax4jsf.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>ajax4jsf</filter-name>
<servlet-name>Faces Servlet</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
一定要將 <filter> 和 <filter-mapping> 標記放在 web.xml 檔案中其他任何過濾器的前面。根據文件所述,必須這樣做才能防止 web.xml 中其他任何過濾器與 Ajax4jsf 衝突。
<filter> 元素僅僅宣告 Ajax4jsf 過濾器類。<filter-mapping> 元素將 Ajax4jsf 過濾器與這個檔案中其他地方宣告的 Faces Servlet 關聯起來。
既然已經在專案中新增並配置了 Ajax4jsf,就可以開始改進 Developer Forum Signup 應用程式了。下一節通過討論 Temperature Conversion 實用程式的細節來討論這個問題。
對於在 Developer Forum 應用程式中登錄後的成員,提供 Temperature Conversion 實用程式作為實際功能。這個實用程式將溫度從攝氏溫度轉換為華氏溫度,或者相反。每種溫度轉換的結果顯示為一個動態產生的溫度計圖像。首先,學習如何用 Ajax4jsf 實現這個使用者介面。然後,看看實現這個實用程式的 bean。
本系列的第 2 部分擴展了 Developer Forum Signup 應用程式,新增了欄位驗證器和彈出式日曆來幫助使用者選擇生日。這些改進都集中在註冊頁面上。現在,將建立一個新頁面,它允許使用者輸入攝氏或華氏溫度,然後就會看到轉換後的溫度顯示為數字和圖像。
首先,需要提供一個連結,它允許成員在成功登錄之後存取這個實用程式。更新後的 signup-success.jsp 頁面使用類似下列的一行程式碼提供這個連結:
*New* The Temperature Converter is
<a href="temperature-converter.faces">here</a>!
這個清單中唯一有意思的東西是溫度轉換器頁面的副檔名。在 Eclipse 中,這個檔案實現為 JSP。當部署這個頁面時,必須用 .faces 副檔名引用它,從而讓 Faces Servlet 處理它。
現在看看溫度轉換器頁面的實現。下列程式碼給出這個頁面的部分清單:
<%@ taglib uri="https://ajax4jsf.dev.java.net/ajax" prefix="a4j"%>
<%@ taglib uri="https://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="https://java.sun.com/jsf/core" prefix="f"% >
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Temperature Converter</title>
</head>
<body>
<h1>Temperature Converter</h1>
<hr/>
<p>The Temperature Converter is provided as a benefit for our
members in the Developer Forum!</p>
<f:view>
<a4j:region id="CtoF" selfRendered="true">
<a4j:mediaOutput element="img" cacheable="false" session="true"
createContent="#{temperature.drawFahrenheitThermometer}"
value="#{thermometer}" mimeType="image/jpeg" />
<a4j:status startText="Processing..." stopText="" for="CtoF"/>
<h:form>
<h:panelGrid columns="2">
<h:outputText value="Temperature in Fahrenheit:" />
<h:outputText id="tempFahr"
value="#{temperature.outputFahrenheit}"/>
<h:outputText value="Temperature in Celsius:" />
<h:inputText size="5" value="#{temperature.inputCelsius}">
<a4j:support event="onkeyup" reRender="tempFahr" />
</h:inputText>
</h:panelGrid>
</h:form>
</a4j:region>
</f:view>
<hr/>
<p>Thank you for using the Temperature Converter!</p>
</body>
</html>
這個 JSP 清單中有幾個有趣的元素。第一個是開頭的 a4j <taglib> 宣告。對於任何使用 Ajax4jsf 元件程式庫的 JSP,這個宣告都是必要的。
<a4j:region> 標記宣告 Ajax 區域的開頭和結尾。selfRendered 屬性設定為 true,這意味著當發出相關聯的 Ajax 請求時將處理這個區域。<a4j:mediaOutput> 標記允許建立動態產生的內容,在這個範例中就是反映轉換後的華氏溫度的溫度計圖像。createContent 屬性指定負責建立此內容的 bean 方法。value 屬性指定一個物件,這個物件包含著 createContent 中的 bean 方法所需的資料。注意,value 中的資料物件必須實作 java.io.Serializable 類別。<a4j:status> 標記提供 Ajax 請求的客戶端狀態。當請求開始時,顯示訊息 "Processing..."。當請求停止時,不顯示訊息。for 屬性將這個狀態與指定的 ID 關聯起來。這正好是前面定義的 "CtoF" 區域。<a4j:support> 標記將 Ajax 支援新增到 JSF <h:inputText> 標記中。event 屬性指出哪個 JavaScript 事件將觸發 Ajax 請求。reRender 屬性包含在發出請求之後將重新呈現的物件的 ID 清單。在這個範例中,當使用者輸入攝氏溫度值時,在每次釋放鍵盤鍵之後,發出一個 Ajax 請求,從而用轉換後的華氏溫度重新呈現匹配的 JSF <h:outputText> 標記。
圖 5. 運行時的 Temperature Conversion 實用程式
這就是在溫度轉換器頁面上使用 Ajax 的方式。正如程式碼所示,這個程式庫提供的元件很容易與 JSF 標記進行整合。接下來,研究一下處理實際溫度轉換和圖像建立的程式碼。
溫度轉換器 bean 是實現 Temperature Conversion 實用程式的模組。負責進行溫度轉換和圖像建立的所有方法都保存在 TemperatureConverter.java 檔案中。下面幾個清單給出令人感興趣的幾個方法:
public void setInputCelsius(String inputCelsius) {
Float tmpCelsius = null;
this.inputCelsius = inputCelsius;
// convert C to F
tmpCelsius = new Float(inputCelsius);
fahrenheitValue = new Float(celsiusToFahrenheit(tmpCelsius.floatValue()));
setOutputFahrenheit(fahrenheitValue.toString());
} // end setInputCelsius
public void setInputFahrenheit(String inputFahrenheit) {
Float tmpFahrenheit = null;
this.inputFahrenheit = inputFahrenheit;
// convert F to C
tmpFahrenheit = new Float(inputFahrenheit);
celsiusValue = new Float(fahrenheitToCelsius(tmpFahrenheit.floatValue()));
setOutputCelsius(celsiusValue.toString());
} // end setInputFahrenheit
當使用者在溫度轉換器頁面上的文字欄位中輸入一個值之後,會呼叫對應的 set 方法。這些方法呼叫程式碼來轉換對應的溫度並相應地設定輸出值。下列程式碼給出實際的轉換公式:
private float celsiusToFahrenheit (float celsius) {
float fahrenheit = 0.0f;
fahrenheit = ((9.0f / 5.0f) * celsius) + 32.0f;
System.out.println("celsius: " + celsius + "\tfahrenheit: " + fahrenheit);
return fahrenheit;
} // end celsiusToFahrenheit
private float fahrenheitToCelsius(float fahrenheit) {
float celsius = 0.0f;
celsius = (5.0f / 9.0f) * (fahrenheit - 32.0f);
System.out.println("fahrenheit: " + fahrenheit + "\tcelsius: " + celsius);
return celsius;
} // end fahrenheitToCelsius
這兩個方法相當簡單明瞭。輸入一個溫度,輸出轉換後的溫度。System.out.println 陳述式在啟動時顯示的 Geronimo 主控台上顯示字串。這適合進行快速除錯,但是真正的日誌記錄實用程式適合更複雜的應用程式。下列程式碼包含負責建立華氏溫度計圖像的方法:
public void drawFahrenheitThermometer(OutputStream out, Object object)
throws IOException {
BufferedImage thermometerImage = null;
Graphics2D image = null;
Thermometer thermometer = null;
if (object instanceof Thermometer) {
thermometer = (Thermometer) object;
} else {
return;
} // end if
thermometerImage = new BufferedImage(
thermometer.getWidth().intValue(),
thermometer.getHeight().intValue(),
BufferedImage.TYPE_INT_RGB);
image = thermometerImage.createGraphics();
image.setBackground(thermometer.getThermometerColor());
image.clearRect(0, 0, thermometer.getWidth().intValue(),
thermometer.getHeight().intValue());
drawMercury(fahrenheitValue, thermometer, image);
drawThermometerUnits(thermometer, image);
ImageIO.write(thermometerImage, "jpeg", out);
} // end drawFahrenheitThermometer
這就是在 <a4j:mediaOutput> 的 createContent 屬性中指定的方法。它需要兩個參數,而且 Object 類型的第二個參數必須實作 java.io.Serializable。這個方法使用標準的 Java 2D 圖形程式庫來建立溫度計圖像。尺寸儲存在資料物件 thermometer 中。drawMercury 和 drawThermometerUnits 方法見下列程式碼:
private void drawThermometerUnits(Thermometer thermo, Graphics2D image2D) {
Integer tmpInteger = null;
int blockCounter = 0;
int textCounter = 0;
int unitCounter = 0;
for (int x = 0; x < thermo.getWidth().intValue(); x++) {
if (x == blockCounter) {
image2D.drawLine(x,0,x,30);
tmpInteger = new Integer(textCounter);
image2D.setColor(thermo.getThermometerTextColor());
image2D.drawChars(tmpInteger.toString().toCharArray(),
0, tmpInteger.toString().length(), x, 45);
blockCounter = blockCounter + BLOCK_INCREMENT;
textCounter = textCounter + TEXT_INCREMENT;
unitCounter = unitCounter + UNIT_INCREMENT;
} else if (x == unitCounter) {
image2D.drawLine(x, 0, x, 10);
unitCounter = unitCounter + UNIT_INCREMENT;
} // end if
} // end for
} // end drawThermometerUnits
private void drawMercury(Float temperature, Thermometer thermo, Graphics2D image2D) {
int mercuryValue = 0;
if (temperature == null) {
temperature = new Float(0.0f);
} // end if
// Scale temperature to thermometer units in image2D
mercuryValue = temperature.intValue() * SCALE_TEMP;
image2D.setColor(thermo.getMercuryColor());
image2D.fill3DRect(0, 11, mercuryValue,
thermo.getMercuryHeight().intValue(), true);
System.out.println("temp.intVal: " + temperature.intValue() +
"\t" + "mercuryValue: " + mercuryValue);
} // end drawMercury
drawThermometerUnits 方法繪製溫度計的刻度來表示單位。這個方法逐一處理圖像寬度上的每個像素。每隔一定的間距,繪製一條短刻度線或長刻度線。短刻度線代表 2 度,長刻度線代表 10 度並在它下面繪製對應的數字。drawMercury 方法將轉換後的溫度乘以一個縮放因子,得出要繪製的水銀柱的像素數。
這就是轉換溫度和建立溫度計圖像所需的程式碼。下列程式碼給出資料物件 Thermometer.java。這個支援 bean 包含建立溫度計圖像所需的圖像資訊。
// Thermometer.java - Contains dimensions and color for thermometer
// image
package devSignup;
import java.awt.Color;
import java.io.Serializable;
public class Thermometer implements Serializable {
private static final long serialVersionUID = 1L;
private static final int MERCURY_HEIGHT = 20;
private static final int THERMOMETER_WIDTH = 600;
private static final int THERMOMETER_HEIGHT = 50;
private Color thermometerColor = new Color(235, 235, 235);
private Color thermometerTextColor = new Color(0, 0, 0);
private Color mercuryColor = new Color(135, 135, 135);
private Integer mercuryValue = null;
private Integer mercuryHeight = null;
private Integer thermometerWidth = null;
private Integer thermometerHeight = null;
public Thermometer() {
mercuryValue = new Integer(0);
mercuryHeight = new Integer(MERCURY_HEIGHT);
thermometerWidth = new Integer(THERMOMETER_WIDTH);
thermometerHeight = new Integer(THERMOMETER_HEIGHT);
} // end Thermometer
public Color getThermometerColor() {
return thermometerColor;
} // end getThermometerColor
// ...additional get/set methods trimmed here...
public void setThermometerColor(Color thermometerColor) {
this.thermometerColor = thermometerColor;
} // end setThermometerColor
} // end Thermometer
Thermometer 就是 <a4j:mediaOutput> 標記的 value 屬性中指定的資料物件。正如前面提到的,這個資料物件必須實作 Serializable Java 類別。注意,溫度計圖像所需的所有顏色和尺寸都在這個模組中定義,但是 mercuryValue 除外。這個值實際上是未定義的,因為它是唯一一個根據使用者輸入而改變的值。
在使用這兩個 bean 之前,需要在 faces-config.xml 部署描述符中定義它們。打開這個檔案,確保選擇 Source 選項卡。確保在這個檔案中輸入下列內容:
<managed-bean>
<description>Temperature Converter Bean</description>
<managed-bean-name>temperature</managed-bean-name>
<managed-bean-class>devSignup.TemperatureConverter</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<description>Thermometer Bean</description>
<managed-bean-name>thermometer</managed-bean-name>
<managed-bean-class>devSignup.Thermometer</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
這兩個標記宣告兩個 bean 並讓它們在被呼叫時對 JSF 程式庫可用。這樣就行了!Temperature Conversion 實用程式現在已經可以進行建構並部署到 Geronimo 上了,下面就來完成這些任務。
本節簡要地討論如何建構和部署 Temperature Conversion 實用程式並在 Developer Forum 應用程式中存取它。還討論在開發期間遇到的一些問題。
首先需要在 Eclipse 中建構範例應用程式。
devSignup 專案,然後選擇 Build Project。devSignup 專案。這一次選擇 Export。devSignup.war)並點擊 Save。既然已經將範例應用程式匯出為 Web 存檔,就可以將它部署到 Geronimo 上了。有兩種部署方法:熱部署和透過 Geronimo 的 Console 應用程式進行部署。在 geronimo-1.1 安裝資料夾下面有一個稱為 deploy 的資料夾。複製到這個資料夾中的任何存檔檔案(比如 EAR 或 WAR)都由 Geronimo 自動地進行部署。反之,如果從這個目錄中刪除存檔檔案,那麼 Geronimo 就會卸載它們。前面的教學使用 Console 應用程式,所以我們繼續使用這種部署方法。
改進後的 Developer Forum 應用程式已經準備好了。用 URL https://localhost:8080/devSignup 存取這個應用程式。
第一個頁面與第 2 部分中建立的註冊頁面相同。建立一個論壇帳號。成功之後,向使用者顯示一個類似下圖的畫面。

圖 6. 成功!
在這個頁面上,論壇成員現在可以點擊 here 連結來存取 Temperature Conversion 實用程式。在存取這個實用程式之後,輸入攝氏或華氏溫度值。應該會出現兩個現象。首先,在輸入每個值之後,溫度立刻被轉換。其次,在輸入值時,在溫度計旁邊顯示狀態訊息 Processing...。在輸入所希望的溫度之後,按 Enter 鍵來更新溫度計圖像。恭喜!Ajax4jsf 提供的 Ajax 功能已經發揮作用了。
請您自己試試。但是要記住兩個問題。正如前面所提到的,Ajax4jsf 的過濾器必須列在 web.xml 描述符檔案中其他任何過濾器的前面。必須這樣做才能防止同一個檔案中宣告的其他任何過濾器與 Ajax4jsf 過濾器衝突。根據文件所述,這個條件和解決方案只適用於在 Apache MyFaces 中使用 Ajax4jsf。
Geronimo 提供了兩種部署應用程式的方法:熱部署和 Geronimo Console。這兩種機制看起來是相互獨立工作的。當總是只使用其中一種方法進行部署時,開發人員很容易管理應用程式的安裝。但是,熱部署的應用程式不會出現在 Geronimo Console 的 Web App WAR 連結中。同樣,透過 Geronimo Console 的 Deploy New 連結部署的應用程式也不會出現在為熱部署保留的 geronimo-1.1/deploy 資料夾中。因此,有可能在 Geronimo 上部署了同一應用程式的多個實例,而您卻不知道。這種情況可能會導致不可預測的結果。強烈建議您選擇一種部署方法並堅持使用它。
在這個教學中,透過新增 Temperature Conversion 實用程式改進了 Developer Forum Signup 應用程式。我們研究了 Ajax4jsf 程式庫並使用幾個 Ajax4jsf 元件建立了一個啟用 Ajax 的前端。還學習了如何將這個程式庫新增到 Eclipse Web 應用程式專案中。
本系列的第 4 部分將研究 Oracle 的 ADF Faces,這是建構在 JSF 之上的另一組 UI 元件,它最近被捐贈給了 Apache。這個教學將講解如何整合這個程式庫中的元件,從而進一步改進 Developer Forum Signup 應用程式。敬請期待!
| 描述 | 名字 | 大小 | 下載方法 |
|---|---|---|---|
| 第 3 部分原始碼 | devSignup-p3src.zip | 19KB | HTTP |
| 第 3 部分 WAR 檔案 | devsignup-p3war.zip | 4240KB | HTTP |