99精品国自产在线,欧美日韩在线网站,日本道免费精品一区二区三区http://www.aygfsteel.com/linden/category/23055.htmlzh-cnTue, 12 Jun 2007 17:17:41 GMTTue, 12 Jun 2007 17:17:41 GMT60JavaScript 訪問 JSF 組件的方法http://www.aygfsteel.com/linden/articles/123738.htmlLinden.zhangLinden.zhangTue, 12 Jun 2007 15:14:00 GMThttp://www.aygfsteel.com/linden/articles/123738.htmlhttp://www.aygfsteel.com/linden/comments/123738.htmlhttp://www.aygfsteel.com/linden/articles/123738.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/123738.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/123738.html先看下面的 JSF 頁面:

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>

<html>
  
<head>
    
<title>My JSF 'login.jsp' starting page</title>
    
<script type="text/javascript">
    
function isEmpty() {
      
var username = document.getElementById("formLogin:txtUsername").value;
      
var password = document.getElementById("formLogin:txtPassword").value;
      
if(username == ""{
        alert(
"給老子輸用戶名!");
        document.getElementById(
"formLogin:txtUsername").focus();
        
return false;
      }

      
if(password == ""{
        alert(
"不輸密碼你登錄個鏟鏟!");
        document.getElementById(
"formLogin:txtPassword").focus();
        
return false;
      }

    }

    
</script>
  
</head>

  
<body>
    
<center>
      
<f:view>
        
<h:form id="formLogin">
          
<div id="input">
            
<h:outputLabel value="用戶名:" />
            
<h:inputText value="#{LoginManager.username}" id="txtUsername"
              styleClass
="formText" />
            
<br>
            
<h:outputLabel value="密碼:" />
            
<h:inputSecret value="#{LoginManager.password}" id="txtPassword"
              styleClass
="formText" />
          
</div>
          
<div id="submit">
            
<h:commandButton value="提交" action="#{LoginManager.check}"
              onclick
="return isEmpty();" styleClass="formButton" />
            
<h:commandButton value="重置" type="button"
              onclick
="javascript:document.getElementById('formLogin').reset();
              document.getElementById('formLogin:txtUsername').focus();"

              styleClass
="formButton" />
          
</div>
        
</h:form>
      
</f:view>
    
</center>
  
</body>
</html>

這個頁面使用 JavaScript 來確認登錄時用戶名和密碼是否為空,表單名為 formLogin,兩個輸入組件名為 txtUsernametxtPassword,當單擊按鈕時,將調用 JavaScript 函數 isEmpty(),根據條件判斷顯示對話框或是提交表單。

注意的是,不能在 JavaScript 函數中使用如下類似語法來訪問表單組件:
document.formLogin.txtUsername.value;
而應使用:
document.getElementById("formLogin:txtUsername").value;
或者:
document.forms.formLogin["formLogin:txtUsername"].value;

這是因為 JSF 解釋上面的 <h:form id="formForm">...</h:form> 一段時會生成如下代碼:

<form id="formLogin" method="post" action="/Project_Blog/login.faces"
  enctype
="application/x-www-form-urlencoded">
  
<div id="input">
    
<label>用戶名:</label>
    
<input id="formLogin:txtUsername" type="text"
      name
="formLogin:txtUsername" class="formText" />
    
<br>
    
<label>密碼:</label>
    
<input id="formLogin:txtPassword" type="password"
      name
="formLogin:txtPassword" value="" class="formText" />
  
</div>
  
<div id="submit">
    
<input type="submit" name="formLogin:_id2" value="提交"
      onclick
="return isEmpty();" class="formButton" />
    
<input type="button" name="formLogin:_id3" value="重置"
      onclick
="javascript:document.getElementById('formLogin').reset();
      document.getElementById('formLogin:txtUsername').focus();"
 class="formButton" />
  
</div>
  
<input type="hidden" name="formLogin" value="formLogin" />
</form>

JSF 產生的所有表單控件都有符合 formName:componentName 格式的名稱,這里的 formName 表示控件的表單的名稱,而 componentName 表示組件名稱。如果沒有指定 id 屬性,則 JSF 框架會自動創建標識符,就象上面的 HTML 片段中的按鈕一樣。因此,要訪問上面的用戶名字段,必須使用下列方法:
document.getElementById("formLogin:txtUsername").value;


Linden.zhang 2007-06-12 23:14 發表評論
]]>
JSF中實現分頁(三) http://www.aygfsteel.com/linden/articles/123663.htmlLinden.zhangLinden.zhangTue, 12 Jun 2007 09:01:00 GMThttp://www.aygfsteel.com/linden/articles/123663.htmlhttp://www.aygfsteel.com/linden/comments/123663.htmlhttp://www.aygfsteel.com/linden/articles/123663.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/123663.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/123663.html好久沒有寫點東西了,這次想把JSF中的分頁系列文章再擴充一點,說明一下查詢和分頁結合的情況,當我們把查詢條件和查詢結果放到一個頁面上時,查詢還是非常容易實現的,甚至不需要我們手工去從數據庫中查詢。

在本系列文章中的第二篇中,介紹了一種 Load On Demand的方式,我們在這里需要繼續利用這種方式,并對其做一些小小的擴展。這里我們使用 Hibernate3 作為持久化方案。

簡單的介紹一下應用情景,一個系統中包含了一些 Customer 的信息,我們需要對其進行查詢并對查詢結果進行分頁。

首先處理條件查詢的情況,通常會根據 VO 中的字段進行 like 型查詢,有時候時間或數字之類的會使用Between查詢,因為查詢條件一般不會很復雜,在這里,使用 Hibernate3 中的 Criteria 查詢來處理這樣的情況,我們把所有的查詢條件通過 Customer 這個 VO 傳進來,然后只對非空字段進行 like 查詢,我們用到這樣的方法。

public  List queryByConditions(Customer customer,  int  startRow,  int  pageSize)
{
 Criteria criteria 
=  getSession().createCriteria(Customer. class );
 
if  ( ! StringUtils.isEmpty(customer.getCustomerName()))
 {
  criteria.add(QueryUtils.getCriteriaParam(
" customerName " , customer.getCustomerName()));
 }
 
if  ( ! StringUtils.isEmpty(customer.getAddress()))
 {
  criteria.add(QueryUtils.getCriteriaParam(
" address " , customer.getAddress()));
 }
 
if  ( ! StringUtils.isEmpty(customer.getFax()))
 {
  criteria.add(QueryUtils.getCriteriaParam(
" fax " , customer.getFax()));
 } 
 
return  criteriaPagedList(criteria, startRow, pageSize);
}

另外對應的一個count方法略去,只需要在前面加入一個
criteria.setProjection(Projections.count("customerId"));

因為考慮到以后的擴展,使用了一個Utils方法,QueryUtils.getCriteriaParam方法

public   static   final  SimpleExpression getCriteriaParam(String name, String param)
{
 
return  Expression.like(name,  " % "   +  param  +   " % " );
}

我們可以很容易的在 Backing Bean 上通過 Service 層拿到這個查詢結果的 List 和 Count 值,相關的getDatePage方法如下。
如果你不了解這個 getDataPage 方法的含義,請仔細閱讀“在JSF中實現分頁(二)”一文,并仔細思考該方法的含義。

protected  DataPage getDataPage( int  startRow,  int  pageSize)
{
 List queryCustomerList 
=  customerService.queryCustomer(customer,  this .startRow,  this .getPageSize());
 
int  dataSetSize  =  customerService.countQueryCustomer(customer);
 
return   new  DataPage(dataSetSize, startRow, queryCustomerList);
}

在同一個 Backing Bean 中,我們放了一個存放查詢條件的VO - Customer,并在頁面中使用<t:saveState>保存其狀態,使其查詢條件不會隨著翻頁而丟失。

<t:saveState value="#{customerListBean.customer}"/>

在頁面中,我們把所有的查詢條件都放到該 VO 中,在 getDataPage 方法中就會在適當的時候調用新的查詢條件來查詢新的數據,這一切都不需要我們動手的。

在 Backing Bean 中有這樣的一個方法:

public  String query()
{
 dataModel.setWrappedData(getDataPage(
0 , getPageSize()));
 
return   " success " ;
}

只是把數據清空,并強制 PagedListDataModel 讀取數據,然后我們返回相同的頁面,這個時候,系統按照用戶輸入的查詢條件拿到查詢結果以后,返回同一頁面,該頁面中的使用 LocalDataModel 的那個 DataTable 就會把結果顯示出來。

請注意,這里 LocalDataModel 和 Customer 都在同一個 Backing Bean 中。

是不是覺得很簡單呢,一切都歸功于 getDataPage 這個方法,我們幾乎不需要做什么額外的操作就可以達到我們的目的。

因為該方法在一個商業項目中使用,代碼不便公布,只能把里面的一些代碼抽取出來,零零碎碎的拿給大家看,如果大家有什么疑問的話,可以在論壇上提出來,我會盡力解答的,另外這個方法也會在空閑的時候 Merge 到 MyPSP 項目中去。



Linden.zhang 2007-06-12 17:01 發表評論
]]>
JSF中實現分頁(二)http://www.aygfsteel.com/linden/articles/123658.htmlLinden.zhangLinden.zhangTue, 12 Jun 2007 08:54:00 GMThttp://www.aygfsteel.com/linden/articles/123658.htmlhttp://www.aygfsteel.com/linden/comments/123658.htmlhttp://www.aygfsteel.com/linden/articles/123658.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/123658.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/123658.html閱讀全文

Linden.zhang 2007-06-12 16:54 發表評論
]]>
JSF中實現分頁(一) http://www.aygfsteel.com/linden/articles/123656.htmlLinden.zhangLinden.zhangTue, 12 Jun 2007 08:51:00 GMThttp://www.aygfsteel.com/linden/articles/123656.htmlhttp://www.aygfsteel.com/linden/comments/123656.htmlhttp://www.aygfsteel.com/linden/articles/123656.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/123656.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/123656.html對于大多數Web應用,分頁都是必不可少的功能,當然在JSF中也一樣,我在這里用兩篇文章介紹兩種方法來展示一下,如何在JSF中實現分頁。本文假定你已經對JSF有了一些簡單的了解,懂得基本配置和使用,并建立起一個blank項目。

     MyfacesApache基金會中的一個一級項目,除了實現JSF標準外,做了很多的擴展工作,在Myfaces包中有一個擴展包Tomahawk,我們將主要使用其中的兩個Component實現分頁:一個是<t:dataTable>,另一個是<t:dataScroller>,在第一篇里面,我們簡易的組合這兩個Component來實現一種簡單,但并不高效的分頁。

     下面的例子來自于Myfaces-Sample,我省去了其中和分頁邏輯無關的內容,詳細的例子可以下載Myfaces-Sample包或者訪問http://www.irian.at/myfaces/home.jsf 查看。

     第一部分:dataTable

<t:dataTable id="data"
                  var
="car"
                  value
="#{pagedSort.cars}"
                  rows
="10">
    ……
</t:dataTable>

在這一部分中,dataTable綁定了一個backing bean - pagedSort中的cars屬性,我們可以在這個屬性中加入數據訪問邏輯,從數據庫或者其他來源取得用于顯示的數據。比如我們可以通過Hibernate獲取一個List,其中包含有我們用于顯示的POJOs

注意,dataTable中的rows屬性指的是每頁的行數,是必須指定的,否則是無法進行分頁的,如果在項目中會使用固定行數的分頁,建議把這個值寫在BaseBackingBean中,并暴露一個property,供頁面調用,所以每次在頁面中就可以這么寫#{backingBean.pageSize}

第二部分:dataScroller

    <t:dataScroller id="scroll_1"
                        for
="data"
                        fastStep
="10"
                        paginator
="true"
                        paginatorMaxPages
="9">
            
<f:facet name="first" >
                
<t:graphicImage url="images/arrow-first.gif" border="1" />
            
</f:facet>
            
<f:facet name="last">
                
<t:graphicImage url="images/arrow-last.gif" border="1" />
            
</f:facet>
            
<f:facet name="previous">
                
<t:graphicImage url="images/arrow-previous.gif" border="1" />
            
</f:facet>
            
<f:facet name="next">
                
<t:graphicImage url="images/arrow-next.gif" border="1" />
            
</f:facet>
            
<f:facet name="fastforward">
                
<t:graphicImage url="images/arrow-ff.gif" border="1" />
            
</f:facet>
            
<f:facet name="fastrewind">
                
<t:graphicImage url="images/arrow-fr.gif" border="1" />
            
</f:facet>
        
</t:dataScroller>


    這里定義了我們用于分頁的<t:dataScroller>,最主要的是配置該分頁Component針對哪個dataTable進行分頁的“for”屬性,該屬性與dataTable綁定,并對其進行分頁,在這里,綁定了第一部分中的id="data"dataTable,下面有很多的<t:facet>是指定分頁的導航樣式的,這里使用了圖片作為導航,可以把他們改成文字形式的導航。

    當然這只是最簡單,也是一種不推薦的分頁方式,因為在每次進行分頁的時候,將會從數據庫中取回所有的記錄放入List中,然后,dataScroller在對這個List進行分頁,如果在數據量很大的情況下,這種方式顯然是不符合要求的,假設每條記錄占用1k內存,數據庫中有100萬條記錄,每次要把這個List全部讀取出來將占用1G內存。我們需要一種Load on demand方式的讀取,也就是只在需要查看某頁的時候讀取該頁的數據。

    另外一方面,JSF的生命周期中有多個階段會調用到#{pagedSort.cars}中對應的方法,如果在這里調用了數據訪問邏輯,就會在只顯示一次頁面的情況下進行多次數據庫操作,也是相當的耗費資源的。

    所以我們需要有更好的分頁方式去解決以上問題,下一篇我將介紹另一種方法以改善這些問題。



Linden.zhang 2007-06-12 16:51 發表評論
]]>
JSF入門與提高目錄http://www.aygfsteel.com/linden/articles/122644.htmlLinden.zhangLinden.zhangThu, 07 Jun 2007 09:06:00 GMThttp://www.aygfsteel.com/linden/articles/122644.htmlhttp://www.aygfsteel.com/linden/comments/122644.htmlhttp://www.aygfsteel.com/linden/articles/122644.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/122644.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/122644.html如果您是從使用的角度來看JSF,則您不用理會HTTP、數據轉換等細節,JSF將細節都隱藏起來了,無論您是網頁設計人員或是應用程序設計人員,都可以使用自己熟悉的方式來看JSF。

入門
藉由以下的幾個主題,可以大致了解JSF的輪廓與特性,我們來看看網頁設計人員與應用程序設計人員各負責什么。
o 簡介JSF
o 第一個JSF程序
o 簡單的導航 Navigation
o 導航規則設置
o JSF Expression Language
o 國際化訊息
Managed Beans
JSF 使用 Bean 來達到邏輯層與表現層分離的目的,Bean 的管理集中在組態檔案中,您只要修改組態檔案,就可以修改 Bean 之間的相依關系。
o Backing Beans
o Beans 的組態與設定
o Beans 上的 List, Map
數據轉換與驗證
轉換器(Converter)協助模型與視圖之間的數據轉換,驗證器(Validator)協助進行語意檢驗(Semantic Validation)。
o 標準轉換器
o 自訂轉換器
o 標準驗證器
o 自訂驗證器
o 錯誤訊息處理
o 自訂轉換, 驗證標簽
事件處理
JSF的事件模型提供一個近似的桌面GUI事件模式,讓熟悉GUI設計的人員也能快速上手Web程序設計。
o 動作事件
o 實時事件
o 值變事件
o Phase 事件
 JSF 標簽

  網頁設計人員要作的就是了解JSF的標簽的使用方式,這就像是學習進階的HTML標簽,另一件事就是與程序設計人員溝通好各個Bean的名稱綁定。

標簽入門
卷標的相關屬性查詢,您可以參考 Tag Library Documentation,這邊的介紹只是一些簡單的入門實例。
o 簡介JSF標準標簽
o 輸出類標簽
o 輸入類標簽
o 命令類標簽
o 選擇類標簽 一
o 選擇類標簽 二
o 其它標簽


表格處理
對于必須使用表格方式呈現的數據,JSF 的 <h:dataTable> 卷標協助您進行動態表格數據的輸出。
o 簡單的表格
o 表頭, 表尾
o TableModel 類別

 
自訂組件

  JSF 讓您可以自訂組件,每個組件都是可替換的,這使得組件在搭配時更有彈性,但相對的卻使開發組件的過程復雜的多,這邊對自訂JSF 組件只是個入門磚,更多有關自訂組件的細節可得要專書來說明。

JSF 生命周期與組件概述
要開發 JSF 組件,您需要更深入了解 JSF 的一些處理細節,包括了 JSF 生命周期以及 JSF 框架。
o JSF 生命周期
o 概述自訂組件

簡單實例
在不考慮組件有子組件的情況下,這邊以實際的一個例子來說明開發組件的過程,至于考慮子組件的情況請參考專書介紹。
o 編碼, 譯碼
o 組件卷標
o 使用自訂組件
o 自訂 Renderer

 



Linden.zhang 2007-06-07 17:06 發表評論
]]>
AJAX+JSF組件實現高性能的文件上載http://www.aygfsteel.com/linden/articles/122549.htmlLinden.zhangLinden.zhangThu, 07 Jun 2007 03:48:00 GMThttp://www.aygfsteel.com/linden/articles/122549.htmlhttp://www.aygfsteel.com/linden/comments/122549.htmlhttp://www.aygfsteel.com/linden/articles/122549.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/122549.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/122549.html一、 引言

  基于瀏覽器的文件上傳,特別是對于通過<input type="file">標簽包含到Web頁面來實現上傳的情況,還存在較嚴重的性能問題。我們知道,超過10MB的上傳文件經常導致一種非常痛苦的用戶體驗。一旦用戶提交了文件,在瀏覽器把文件上傳到服務器的過程中,界面看上去似乎處于靜止狀態。由于這一切發生在后臺,所以許多沒有耐心的用戶開始認為服務器"掛"了,因而再次提交文件,這當然使得情況變得更糟糕。

  為了盡可能使得文件上傳感覺更友好些,一旦用戶提交文件,許多站點將顯示一個中間過程動畫(例如一旋轉圖標)。盡管這一技術在上傳提交到服務器時起一些作用,但它還是提供了太少的有關文件上傳狀態的信息。解決這個問題的另外一種嘗試是實現一個applet——它通過FTP把文件上傳到服務器。這一方案的缺點是:限制了你的用戶,必須要有一個支持Java的瀏覽器。

  在本文中,我們將實現一個具有AJAX能力的組件——它不僅實現把文件上傳到服務器,而且"實時地"監視文件上傳的實際過程。這個組件工作的四個階段顯示于下面的圖1,2,3和4中:


圖1.階段1:選擇文件上傳

圖2.階段2:上傳該文件到服務器

圖3.階段3:上傳完成

圖4.階段4:文件上傳摘要

  二、 實現該組件

  首先,我們分析創建多部分過濾的過程,它將允許我們處理并且監視文件上傳。然后,我們將繼續實現JavaServer Faces(JSF)組件-它將提供給用戶連續的回饋,以支持AJAX的進度條方式。

  (一) 多部分過濾:UploadMultipartFilter

  多部分過濾的任務是攔截到來的文件上傳并且把該文件寫到一個服務器上的臨時目錄中。同時,它還將監視接收的字節數并且確定已經上載該文件的程度。幸運的是,現在有一個優秀的Jakarta-Commons開源庫可以利用(FileUpload),可以由它來負責分析一個HTTP多部分請求并且把文件上傳到服務器。我們要做的是擴展該庫并且加入我們需要的"鉤子"來監視已經處理了多少字節。

public class UploadMultipartFilter implements Filter{
 public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
 throws IOException, ServletException {
  HttpServletRequest hRequest = (HttpServletRequest)request;
  //檢查是否我們在處理一個多部分請求
  String contentHeader = hRequest.getHeader("content-type");
  boolean isMultipart = ( contentHeader != null && contentHeader.indexOf("multipart/form-data") != -1);
  if(isMultipart == false){
   chain.doFilter(request,response);
  }else{
   UploadMultipartRequestWrapper wrapper = new UploadMultipartRequestWrapper(hRequest);
   chain.doFilter(wrapper,response);
  }
  ...
 }

  正如你所見,UploadMultipartFilter類簡單地檢查了當前的請求是否是一個多部分請求。如果該請求不包含文件上傳,該請求將被傳遞到請求鏈中的下一個過濾,而不進行任何另外的處理。否則,該請求將被包裝在一個UploadMultipartRequestWrapper中。

  (二) UploadMultipartRequestWrapper類

public class UploadMultipartRequestWrapper
extends HttpServletRequestWrapper{
 private Map<String,String> formParameters;
 private Map<String,FileItem> fileParameters;
 public UploadMultipartRequestWrapper(HttpServletRequest request) {
  super(request);
  try{
   ServletFileUpload upload = new ServletFileUpload();
   upload.setFileItemFactory(new ProgressMonitorFileItemFactory(request));
   List fileItems = upload.parseRequest(request);
   formParameters = new HashMap<String,String>();
   fileParameters = new HashMap<String,FileItem>();
   for(int i=0;i<fileItems.size();i++){
    FileItem item = (FileItem)fileItems.get(i);
    if(item.isFormField() == true){
     formParameters.put(item.getFieldName(),item.getString());
    }else{
     fileParameters.put(item.getFieldName(),item);
     request.setAttribute(item.getFieldName(),item);
    }
   }
   }catch(FileUploadException fe){
    //請求時間超過-用戶可能已經轉到另一個頁面。
    //作一些記錄
    //...
   }
   ...

  在UploadMultipartRequestWrapper類中,我們將初始化ServletFileUpload類,它負責分析我們的請求并且把文件寫到服務器上的缺省臨時目錄。ServletFileUpload實例針對在該請求中遇到的每一個字段創建一個FileItem實例(它們包含文件上傳和正常的表單元素)。之后,一個FileItem實例用于檢索一個提交字段的屬性,或者,在文件上傳的情況下,檢索一個到底層的臨時文件的InputStream。總之,UploadMultipartRequestWrapper負責分析該文件并且設置任何FileItem-它在該請求中把文件上傳描述為屬性。然后,這些屬性由JSF組件所進一步收集,而正常表單字段的行為保持不變。

  默認情況下,通用FileUpload庫將使用DiskFileItems類的實例來處理文件上傳。盡管DiskFileItem在處理整個臨時文件業務時是很有用的,但在準確監視該文件已經處理程度方面存在很少支持。自版本1.1以來,通用FileUpload庫能夠使開發者指定用于創建FileItem的工廠。我們將使用ProgressMonitorFileItemFactory和ProgressMonitorFileItem類來重載缺省行為并監視文件上傳過程。

  (三) ProgressMonitorFileItemFactory類

public class ProgressMonitorFileItemFactory extends DiskFileItemFactory {
 private File temporaryDirectory;
 private HttpServletRequest requestRef;
 private long requestLength;
 public ProgressMonitorFileItemFactory(HttpServletRequest request) {
  super();
  temporaryDirectory = (File)request.getSession().getServletContext().getAttribute("javax.servlet.context.tempdir");
  requestRef = request;
  String contentLength = request.getHeader("content-length");
  if(contentLength != null){requestLength = Long.parseLong(contentLength.trim());}
 }
 public FileItem createItem(String fieldName, String contentType,boolean isFormField, String fileName) {
  SessionUpdatingProgressObserver observer = null;
  if(isFormField == false) //這必須是一文件上傳.
   observer = new SessionUpdatingProgressObserver(fieldName,fileName);
   ProgressMonitorFileItem item = new ProgressMonitorFileItem(
     fieldName,contentType,isFormField,
     fileName,2048,temporaryDirectory,
     observer,requestLength);
    return item;
 }
 ...
 public class SessionUpdatingProgressObserver implements ProgressObserver {
  private String fieldName;
  private String fileName;
  ...
  public void setProgress(double progress) {
   if(request != null){
    request.getSession().setAttribute("FileUpload.Progress."+fieldName,progress);
    request.getSession().setAttribute("FileUpload.FileName."+fieldName,fileName);
   }
  }
 }
}

  ProgressMonitorFileItemFactory Content-Length頭由瀏覽器設置并且假定它是被設置的上傳文件的精確長度。這種確定文件長度的方法確實限制了你在每次請求中上傳的文件-如果有多個文件在該請求中被編碼的話,不過這個值是不精確的。這是由于,瀏覽器僅僅發送一個Content-Length頭,而不考慮上傳的文件數目。

  除了創建ProgressMonitorFileItem實例之外,ProgressMonitorFileItemFactory還注冊了一個ProgressObserver實例,它將由ProgressMonitorFileItem來發送文件上傳過程中的更新。我們所使用的ProgressObserver的實現(SessionUpdatingProgressObserver)針對被提交字段的id把進度百分數設置到用戶的會話中。然后,這個值可以由JSF組件存取以便把更新發送給用戶。
(四) ProgressMonitorFileItem類

public class ProgressMonitorFileItem extends DiskFileItem {
 private ProgressObserver observer;
 private long passedInFileSize;
 ...
 private boolean isFormField;
 ...
 @Override
 public OutputStream getOutputStream() throws IOException {
  OutputStream baseOutputStream = super.getOutputStream();
  if(isFormField == false){
   return new BytesCountingOutputStream(baseOutputStream);
  }else{return baseOutputStream;}
 }
 ...
 private class BytesCountingOutputStream extends OutputStream{
  private long previousProgressUpdate;
  private OutputStream base;
  public BytesCountingOutputStream(OutputStream ous){ base = ous; }
  ...
  private void fireProgressEvent(int b){
   bytesRead += b;
   ...
   double progress = (((double)(bytesRead)) / passedInFileSize);
   progress *= 100.0
   observer.setProgress();
  }
 }
}

  ProgressMonitorFileItem把DiskFileItem的缺省OutputStream包裝到一個BytesCountingOutputStream中,這可以在每次讀取一定數目的字節后更新相關的ProgressObserver。

  (五) 支持AJAX的JavaServer Faces(JSF)上傳組件

  這個組件負責生成HTML文件上傳標簽,顯示一個進度條以監視文件上傳,并且生成一旦文件上傳成功需要被顯示的組件。使用JavaServer Faces實現這個組件的一個主要優點是,大多數復雜性被隱藏起來。開發人員只需要把組件標簽添加到JSP,而后由組件負責所有的AJAX及相關的進度條監控細節問題。下面的JSP代碼片斷用于把上傳組件添加到頁面上。

<comp:fileUpload
 value="#{uploadPageBean.uploadedFile}"
 uploadIcon="images/upload.png"
 styleClass="progressBarDiv"
 progressBarStyleClass="progressBar"
 cellStyleClass="progressBarCell"
 activeStyleClass="progressBarActiveCell">
<%--下面是一旦文件上傳完成將成為可見的組件--%>
<h:panelGrid columns="2" cellpadding="2" cellspacing="0" width="100%">
<f:facet name="header">
<h:outputText styleClass="text"
value="文件上傳成功." />
</f:facet>
<h:panelGroup style="text-align:left;display:block;width:100%;">
<h:commandButton action="#{uploadPageBean.reset}"
image="images/reset.png"/>
</h:panelGroup>
<h:panelGroup style="text-align:right;display:block;width:100%;">
<h:commandButton action="#{uploadPageBean.nextPage}"
image="images/continue.png"/>
</h:panelGroup>
</h:panelGrid>
</comp:fileUpload>

  文件上傳組件的value屬性需要用一個擁有一個FileItem的屬性綁定到一個bean上。組件只有在該文件被服務器成功收到時才顯示。

  三、 實現AJAX文件上傳組件

  實質上,上載組件或者生成一個完整的自已,或者在一個AJAX請求的情況下,只生成部分XML以更新在頁面上進度條的狀態。為了防止JavaServer Faces生成完整的組件樹(這會帶來不必要的負荷),我們還需要實現一個PhaseListener(PagePhaseListener)以取消該faces的請求處理的其它部分-如果遇到一個AJAX請求的話。我在本文中略去了所有的關于標準配置(faces-config.xml和標簽庫)的討論,因為它們相當直接且已經在以前討論過;而且這一切都包含在隨同本文的源碼中,你可以詳細分析。

  (一) AJAX文件上傳組件生成器

  該組件和標簽類的實現比較簡單。大量的邏輯被包含到生成器中,具體地說,它負責以下:

  · 編碼整個的上傳組件(和完整的HTML文件上傳標簽)、文件被上傳完成后要顯示的組件,還有實現AJAX請求的客戶端JavaScript代碼。

  · 適當地處理部分AJAX請求并且發送回必要的XML。

  · 解碼一個文件上傳并且把它設置為一個FileItem實例。

  (二) 編碼整個上傳組件

  前面已經提及,文件上傳組件由三個階段組成。在該組件的整個編碼期間,我們將詳細分析這三個階段的編碼。注意,在頁面上的該組件的可視化(使用CSS顯示)屬性將由AJAX JavaScript來控制。

  (三) 階段一

  圖5顯示了該上傳組件的第一個階段。


圖5.選擇文件上傳


  在第一階段中,我們需要生成HTML文件Upload標簽和點擊Upload按鈕時相應的執行代碼。一旦用戶點擊了Upload按鈕,表單將被一個IFRAME(為防止頁面阻塞)提交并初始化第二個階段。下面是生成代碼的一部分:

//文件上傳組件
writer.startElement("input", component);
writer.writeAttribute("type", "file", null);
writer.writeAttribute("name", component.getClientId(context), "id");
writer.writeAttribute("id", component.getClientId(context),"id");
if(input.getValue() != null){
 //如果可用,則生成該文件名.
 FileItem fileData = (FileItem)input.getValue();
 writer.writeAttribute("value", fileData.getName(), fileData.getName());
}
writer.endElement("input");
String iconURL = input.getUploadIcon();
//生成圖像,并把JavaScript事件依附到其上.
writer.startElement("div", component);
writer.writeAttribute("style","display:block;width:100%;text-align:center;", "style");
writer.startElement("img", component);
writer.writeAttribute("src",iconURL,"src");
writer.writeAttribute("type","image","type");
writer.writeAttribute("style","cursor:hand;cursor:pointer;","style");
UIForm form = FacesUtils.getForm(context,component);
if(form != null) {
 String getFormJS = "document.getElementById('" + form.getClientId(context) + "')";
 String jsFriendlyClientID = input.getClientId(context).replace(":","_");
 //設置表單的編碼為multipart以用于文件上傳,并且通過一個IFRAME
 //來提交它的內容。該組件的第二個階段也在500毫秒后被初始化.
 writer.writeAttribute("onclick",getFormJS + ".encoding='multipart/form-data';" +
getFormJS + ".target='" + iframeName + "';" + getFormJS + ".submit();" +
getFormJS + ".encoding='application/x-www-form-urlencoded';" +
getFormJS + ".target='_self';" +
"setTimeout('refreshProgress" + jsFriendlyClientID + "();',500);",null);
}
...
writer.endElement("img");
//現在實現我們將要把該文件/表單提交到的IFRAME.
writer.startElement("iframe", component);
writer.writeAttribute("id", iframeName, null);
writer.writeAttribute("name",iframeName,null);
writer.writeAttribute("style","display:none;",null);
writer.endElement("iframe");
writer.endElement("div");
writer.endElement("div"); //階段1結束

  (四) 階段二

  第二階段是顯示當前百分比的進度條和標簽,如圖6所示。該進度條是作為一個具有100個內嵌span標簽的div標簽實現的。這些將由AJAX JavaScript根據來自于服務器的響應進行設置。


圖6.上傳文件到服務器

writer.startElement("div",component);
writer.writeAttribute("id", input.getClientId(context) + "_stage2", "id");
...
writer.writeAttribute("style","display:none", "style");
String progressBarID = component.getClientId(context) + "_progressBar";
String progressBarLabelID = component.getClientId(context) + "_progressBarlabel";
writer.startElement("div", component);
writer.writeAttribute("id",progressBarID,"id");
String progressBarStyleClass = input.getProgressBarStyleClass();
if(progressBarStyleClass != null)
writer.writeAttribute("class",progressBarStyleClass,"class");
for(int i=0;i<100;i++){
 writer.write("<span> </span>");
}
writer.endElement("div");
writer.startElement("div",component);
writer.writeAttribute("id",progressBarLabelID,"id");
...
writer.endElement("div");
writer.endElement("div"); //階段2結束

  (五) 階段三

  最后,作為階段三,一旦文件成功上傳,需要被顯示的組件即被生成,見圖7。這些是在生成器的encodeChildren方法中實現的。


圖7.上傳完成

public void encodeChildren(FacesContext context,
UIComponent component) throws IOException {
 ResponseWriter writer = context.getResponseWriter();
 UIFileUpload input = (UIFileUpload)component;
 //一旦文件上傳成功,處理將被顯示的子結點
 writer.startElement("div", component);
 writer.writeAttribute("id", input.getClientId(context) + "_stage3", "id"); //階段3.
 if(input.getValue() == null){
  writer.writeAttribute("style","display:none;",null);
 }else{
  writer.writeAttribute("style","display:block",null);
 }
 List<UIComponent> children = input.getChildren();
 for(UIComponent child : children){
  FacesUtils.encodeRecursive(context,child);
 }
 writer.endElement("div"); //階段3結束
}
四、處理AJAX請求

  AJAX請求的生成是在這個組件的解碼方法中處理的。我們需要檢查這是否是一個實際的AJAX請求(為了區別于正常的編譯行為),然后基于由ProgressMonitorFileItemFactory類的SessionUpdatingProgressObserver實例設置在會話中的值把一個XML響應發送回客戶端。

public void decode(FacesContext context, UIComponent component) {
 UIFileUpload input = (UIFileUpload) component;
 //檢查是否這是一個上傳進度請求,或是一個實際的上傳請求.
 ExternalContext extContext = context.getExternalContext();
 Map parameterMap = extContext.getRequestParameterMap();
 String clientId = input.getClientId(context);
 Map requestMap = extContext.getRequestParameterMap();
 if(requestMap.get(clientId) == null){
  return;//什么也不做,返回
 }
 if(parameterMap.containsKey(PROGRESS_REQUEST_PARAM_NAME)){
  //這是一個在該文件請求中的得到進度信息的請求.
  //得到該進度信息并把它生成為XML
  HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
  //設置響應的頭信息
  response.setContentType("text/xml");
  response.setHeader("Cache-Control", "no-cache");
  try {
   ResponseWriter writer = FacesUtils.setupResponseWriter(context);
   writer.startElement("progress", input);
   writer.startElement("percentage", input);
   //從會話中獲得當前進度百分數(由過濾器所設置).
   Double progressCount = (Double)extContext.getSessionMap().
   get("FileUpload.Progress." +input.getClientId(context));
   if(progressCount != null){
    writer.writeText(progressCount, null);
   }else{
    writer.writeText("1", null);//我們還沒有收到上傳
   }
   writer.endElement("percentage");
   writer.startElement("clientId", input);
   writer.writeText(input.getClientId(context), null);
   writer.endElement("clientId");
   writer.endElement("progress");
  } catch(Exception e){
   //做一些錯誤記錄...
  }
  }else{
   //正常的譯碼請求.
  ...

  五、 正常的譯碼行為

  在正常的編譯期間,文件上傳生成器從請求屬性中檢索FileItem,正是在此處它被過濾器所設置,并且更新該組件的值綁定。然后,該會話中的進度被更新到100%,這樣在頁面上的JavaScript就可以把組件送入第3個階段。

//正常的譯碼請求.
if(requestMap.get(clientId).toString().equals("file")){
try{
 HttpServletRequest request = (HttpServletRequest)extContext.getRequest();
 FileItem fileData = (FileItem)request.getAttribute(clientId);
 if(fileData != null) input.setSubmittedValue(fileData);
 //現在我們需要清除與該項相關的任何進度
 extContext.getSessionMap().put("FileUpload.Progress." + input.getClientId(context),new Double(100));
}catch(Exception e){
 throw new RuntimeException("不能處理文件上傳" +" - 請配置過濾器.",e);
}
}

  客戶端JavaScript負責向服務器發出進度請求并通過不同階段來移動組件。為了簡化處理所有的瀏覽器特定的XMLHttpRequest對象的問題,我選用了Matt Krause提供的AjaxRequest.js庫。該庫最大限度地減少我們需要編寫的JavaScript代碼的數量,同時可以使這個組件正常工作。也許把這部分JavaScript代碼打包為該組件的一部分,然后從PhaseListener生成它更好一些,但是,我已經通過定義一個到JSP頁面上的JavaScript庫的鏈接來盡力使得它簡單。

  組件中的getProgressBarJavaScript方法被調用以生成JavaScript。使JavaScript正常工作通常是實現AJAX組件最困難的部分;不過我想,下面的代碼已經非常清晰易于理解了。盡管在我的示例中JavaScript是嵌入到Java代碼中的,但是把它放到一個外部獨立的文件中也許更好一些。在本文中,我只是想使問題更為簡單些且只關心本文的主題。下面是一個將由組件生成的JavaScript的示例。其中假定,fileUpload1是被賦值到該文件組件的客戶端JSF Id,而uploadForm是HTML表單的Id。

function refreshProgress(){
 // 假定我們正在進入到階段2.
 document.getElementById('fileUpload1_stage1').style.display = 'none';
 document.getElementById('fileUpload1_stage2').style.display = '';
 document.getElementById('fileUpload1_stage3').style.display = 'none';
 //創建AJAX寄送
 AjaxRequest.post(
 {
  //指定正確的參數,以便
  //該組件在服務器端被正確處理
  'parameters':{ 'uploadForm':'uploadForm',
  'fileUpload1':'fileUpload1',
  'jsf.component.UIFileUpload':'1',
  'ajax.abortPhase':'4' } //Abort at Phase 4.
  //指定成功處理相應的回調方法.
  ,'onSuccess':function(req) {
  var xml = req.responseXML;
  if( xml.getElementsByTagName('clientId').length == 0) {
   setTimeout('refreshProgress()',200); return;
  }
  var clientId = xml.getElementsByTagName('clientId');
  clientId = clientId[0].firstChild.nodeValue + '_progressBar';
  //從XML獲取百分比
  var percentage = xml.getElementsByTagName('percentage')[0].firstChild.nodeValue;
  var innerSpans = document.getElementById(clientId).getElementsByTagName('span');
  document.getElementById(clientId + 'label').innerHTML = Math.round(percentage) + '%';
  //基于當前進度,設置這些span的式樣類。
  for(var i=0;i<innerSpans.length;i++){
   if(i < percentage){
    innerSpans[i].className = 'active';
   }else{
    innerSpans[i].className = 'passive';
   }
  }
  //如果進度不是100,我們需要繼續查詢服務器以實現更新.
  if(percentage != 100){
   setTimeout('refreshProgress()',400);
  } else {
   //文件上傳已經完成,我們現在需要把該組件送入到第3個階段.
   document.getElementById('fileUpload1_stage1').style.display = 'none';
   document.getElementById('fileUpload1_stage2').style.display = 'none';
   document.getElementById('fileUpload1_stage3').style.display = '';
  }
 }
});
}
return builder.toString();

  六、 結論

  我很希望,本文能夠在有關如何使得文件上傳更具有用戶友好性,并且把AJAX和JavaServer Faces用于實現高級用戶接口組件的可能性方面引發你的進一步思考。毫無疑問,本文中的方案比較冗長并且有可能得到進一步的改進。我希望你能詳細地分析一下本文中所提供的完整的源代碼來深入理解本文中所討論的概念。


Linden.zhang 2007-06-07 11:48 發表評論
]]>
jsf國際化http://www.aygfsteel.com/linden/articles/122415.htmlLinden.zhangLinden.zhangWed, 06 Jun 2007 09:32:00 GMThttp://www.aygfsteel.com/linden/articles/122415.htmlhttp://www.aygfsteel.com/linden/comments/122415.htmlhttp://www.aygfsteel.com/linden/articles/122415.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/122415.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/122415.html
messages.properties  
   
 titleText=JSF  Demo  
 hintText=Please  input  your  name  and  password  
 nameText=name  
 passText=password  
 commandText=Submit  

資源檔名稱由basename加上語言與地區來組成,例如:  

basename.properties    
basename_en.properties    
basename_zh_TW.properties    
   

       沒有指定語言與地區的basename是預設的資源檔名稱,JSF會根據瀏覽器送來的Accept-Language  header中的內容來決定該使用哪一個資源檔名稱,例如:  


Accept-Language:  zh_TW,  en-US,  en  


       如果瀏覽器送來這些header,則預設會使用繁體中文,接著是美式英文,再來是英文語系,如果找不到對應的信息資源檔,則會使用預設的信息資源檔。  


       由于信息資源檔必須是ISO-8859-1編碼,所以對于非西方語系的處理,必須先將之轉換為Java  Unicode  Escape格式,例如您可以先在信息資源檔中寫下以下的內容:  

messages_zh_TW.txt    
   
 titleText=JSF示範  
 hintText=請輸入名稱與密碼  
 nameText=名稱  
 passText=密碼  
 commandText=送出  
   


然后使用JDK的工具程式native2ascii來轉換,例如:  


native2ascii  -encoding  Big5  messages_zh_TW.txt  messages_zh_TW.properties  


轉換后的內容會如下:  

messages_zh_TW.properties    
   
 titleText=JSF\u793a\u7bc4  
 hintText=\u8acb\u8f38\u5165\u540d\u7a31\u8207\u5bc6\u78bc  
 nameText=\u540d\u7a31  
 passText=\u5bc6\u78bc  
 commandText=\u9001\u51fa  
   
(所有資源文件放入你的JFS應用的WEB-INF/classes目錄下)  

接下來您可以使用<f:loadBundle>標記來指定載入信息資源,一個例子如下:  

index.jsp    
   
 <%@  taglib  uri="http://java.sun.com/jsf/core"  prefix="f"  %>  
 <%@  taglib  uri="http://java.sun.com/jsf/html"  prefix="h"  %>  
 <%@page  contentType="text/html;charset=UTF8"%>  

 <f:view>  
 <f:loadBundle  basename="messages"  var="msgs"/>  

 <html>  
 <head>  
 <title><h:outputText  value="#{msgs.titleText}"/></title>  
 </head>  
 <body>  

       <h:form>  
               <h3><h:outputText  value="#{msgs.hintText}"/></h3>  
               <h:outputText  value="#{msgs.nameText}"/>:  
                               <h:inputText  value="#{user.name}"/><p>  
               <h:outputText  value="#{msgs.passText}"/>:    
                               <h:inputSecret  value="#{user.password}"/><p>    
               <h:commandButton  value="#{msgs.commandText}"    
                                               actionListener="#{user.verify}"  
                                               action="#{user.outcome}"/>  
     </h:form>  

 </body>  
 </html>  

 </f:view>  

     如此一來,如果您的瀏覽器預設接受zh_TW語系的話,則頁面上就可以顯示中文,否則預設將以英文顯示,也就是messages.properties的內容,為了能顯示多國語系,我們設定網頁編碼為UTF8。  


<f:view>可以設定locale屬性,直接指定所要使用的語系,例如:  

   
 <f:view  locale="zh_TW">  
 <f:loadBundle  basename="messages"  var="msgs"/>  
   
     直接指定以上的話,則會使用繁體中文來顯示,JSF會根據<f:loadBundle>的basename屬性加上<f:view>的locale屬性來決定要使用哪一個信息資源檔,就上例而言,就是使用messages_zh_TW.properties,如果設定為以下的話,就會使用messages_en.properties:  

   
 <f:view  locale="en">  
 <f:loadBundle  basename="messages"  var="msgs"/>  
   


您也可以在faces-config.xml中設定語系,例如:  

   
 <faces-config>  
       <application>  
               <local-config>  
                       <default-locale>en</default-locale>  
                       <supported-locale>zh_TW</supported-locale>  
               </local-config>  
       </application>  
   
 .....  
 </faces-config>  
   

       在<local-config>一定有一個<default-locale>,而<supported-locale>可以有好幾個,這告訴JSF您的應用程序支援哪些語系。  


       當然,如果您可以提供一個選項讓使用者選擇自己的語系會是更好的方式,例如根據user這個Bean的locale屬性來決定頁面語系:  

   
 <f:view  locale="#{user.locale}">  
 <f:loadBundle  basename="messages"  var="msgs"/>    


在頁面中設定一個表單,可以讓使用者選擇語系,例如設定單選鈕:  

   
 <h:selectOneRadio  value="#{user.locale}">  
         <f:selectItem  itemValue="zh_TW"    
                                     itemLabel="#{msgs.zh_TWText}"/>  
         <f:selectItem  itemValue="en"    
                                     itemLabel="#{msgs.enText}"/>  
 </h:selectOneRadio>  


Linden.zhang 2007-06-06 17:32 發表評論
]]>
jsf簡單例子 http://www.aygfsteel.com/linden/articles/122411.htmlLinden.zhangLinden.zhangWed, 06 Jun 2007 09:27:00 GMThttp://www.aygfsteel.com/linden/articles/122411.htmlhttp://www.aygfsteel.com/linden/comments/122411.htmlhttp://www.aygfsteel.com/linden/articles/122411.html#Feedback0http://www.aygfsteel.com/linden/comments/commentRss/122411.htmlhttp://www.aygfsteel.com/linden/services/trackbacks/122411.html   首先下載jsf-1_1_01.zip,并解壓。在解壓的目錄中找到samples目錄,把其中的jsf-cardemo.war放到Tomcat  5的webapps目錄中,啟動Tomcat  5,在瀏覽器中輸入:http://127.0.0.1:8080/jsf-guessNumber/。




       
例子是簡單了點!
greeting.jsp

程序代碼:
<%@ page contentType="text/html;charset=GBK"%> 
<HTML>
<HEAD> <title>Hello</title> </HEAD>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<body bgcolor="white">
<f:view>
<h:form id="helloForm" >
<h2>我的名字叫Duke. 我想好了一個數字,范圍是
<h:outputText value="#{UserNumberBean.minimum}"/> 到
<h:outputText value="#{UserNumberBean.maximum}"/>. 你能猜出它?
</h2>   


<h:graphicImage id="waveImg" url="/wave.med.gif" />
<h:inputText id="userNo" value="#{UserNumberBean.userNumber}"    validator="#{UserNumberBean.validate}"/> 
<h:commandButton id="submit" action="success" value="Submit" />
<p>
<h:message style="color: red; font-family: 'new Century Schoolbook', serif; font-style: oblique; text-decoration: overline" id="errors1" for="userNo"/>

</h:form>
</f:view>
</body>
</HTML> 


       數字的最大值、最小值、猜對與否、及數字的驗證都綁到了UserNumberBean上,沒有使用標準驗證器。且來看看這個應用中的主角:  

程序代碼:
package guessNumber;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.LongRangeValidator;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
import java.util.Random;
public class UserNumberBean {
    Integer userNumber = null;
    Integer randomint null;
    String  response = null;
    protected String[] status = null;
    private int minimum = 0;
    private boolean minimumSet = false;
    private int maximum = 0;
    private boolean maximumSet = false;
    public UserNumberBean() {
        Random randomGR = new Random();
        randomint new Integer(randomGR.nextInt(10));
        System.out.println("Duke's number: " + randomInt);
    }
    public void setUserNumber(Integer user_number) {
        userNumber = user_number;
        System.out.println("Set userNumber " + userNumber);
    }
    public Integer getUserNumber() {
        System.out.println("get userNumber " + userNumber);
        return userNumber;
    }
    public String getResponse() {
        if (userNumber != null && userNumber.compareTo(randomInt) == 0) {
            return "Yay! You got it!";
        } else {
            return "Sorry, " + userNumber + " is incorrect.";
        }
    }
  
    public String[] getStatus() {
        return status;
    }
    public void setStatus(String[] newStatus) {
        status = newStatus;
    }
   
    public int getMaximum() {
        return (this.maximum);
    }
    public void setMaximum(int maximum) {
        this.maximum = maximum;
        this.maximumSet = true;
    }
    public int getMinimum() {
        return (this.minimum);
    }
    public void setMinimum(int minimum) {
        this.minimum = minimum;
        this.minimumSet = true;
    }
    public void validate(FacesContext context,
                         UIComponent component,
                         Object value) throws ValidatorException {
        if ((context == null) || (component == null)) {
            throw new nullPointerException();
        }
        if (value != null) {
            try {
                int converted = intValue(value);
                if (maximumSet &&
                    (converted > maximum)) {
                    if (minimumSet) {
                        throw new ValidatorException(
                            MessageFactory.getMessage
                            (context,
                             Validator.NOT_IN_RANGE_MESSAGE_ID,
                             new Object[]{
                                 new Integer(minimum),
                                 new Integer(maximum)
                             }));
                    } else {
                        throw new ValidatorException(
                            MessageFactory.getMessage
                            (context,
                             LongRangeValidator.MAXIMUM_MESSAGE_ID,
                             new Object[]{
                                 new Integer(maximum)
                             }));
                    }
                }
                if (minimumSet &&
                    (converted < minimum)) {
                    if (maximumSet) {
                        throw new ValidatorException(MessageFactory.getMessage
                                                     (context,
                                                      Validator.NOT_IN_RANGE_MESSAGE_ID,
                                                      new Object[]{
                                                          new Double(minimum),
                                                          new Double(maximum)
                                                      }));
                    } else {
                        throw new ValidatorException(
                            MessageFactory.getMessage
                            (context,
                             LongRangeValidator.MINIMUM_MESSAGE_ID,
                             new Object[]{
                                 new Integer(minimum)
                             }));
                    }
                }
             } catch (NumberFormatException e) {
                throw new ValidatorException(
                    MessageFactory.getMessage
                    (context, LongRangeValidator.TYPE_MESSAGE_ID));
            }
        }
    }
    private int intValue(Object attributeValue)
        throws NumberFormatException {
        if (attributeValue instanceof Number) {
            return (((Number) attributeValue).intValue());
        } else {
            return (Integer.parseInt(attributeValue.toString()));
        }
    }
}


         數字的最小值,最大值在faces-config.xml中設置。這里主要看看那個validate方法,它在數字不在0-10范圍時,拋出ValidatorException異常。異常信息將會由標記<h:message  for="userNo">輸出。這些異常信息如何本地化定制?那就是從資源包中獲取!請看:

程序代碼:
package guessNumber;
import javax.faces.application.Application;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
public class MessageFactory extends Object {
   
    private MessageFactory() {
    }
   //這個方法是用做參數替換
    
    public static String substituteParams(Locale locale, String msgtext, Object params[]) {
        String localizedStr = null;
        if (params == null || msgtext == null) {
            return msgtext;
        }
        StringBuffer b = new StringBuffer(100);
        MessageFormat mf = new MessageFormat(msgtext);
        if (locale != null) {
            mf.setLocale(locale);
            b.append(mf.format(params));
            localizedStr = b.toString();
        }
        return localizedStr;
    }
//上面方法的用法例子
    public static void main(String args[]){         String msgtext="我由于{0},不能去{1}";         String params[]={"有事","北京"};         String s=substituteParams(Locale.CHINESE,msgtext,params);         System.out.println(s);   } 
/**
 運行結果:  D:\java>java   Test  我由于有事,不能去北京
   D:\java>
*/
 

    /**
     * This version of getMessage() is used in the RI for localizing RI
     * specific messages.
     */

    public static FacesMessage getMessage(String messageId, Object params[]) {
        Locale locale = null;
        FacesContext context = FacesContext.getCurrentInstance();
        // context.getViewRoot() may not have been initialized at this point.
        if (context != null && context.getViewRoot() != null) {
            locale = context.getViewRoot().getLocale();
            if (locale == null) {
                locale = Locale.getDefault();
            }
        } else {
            locale = Locale.getDefault();
        }
        return getMessage(locale, messageId, params);
    }
//從資源包中獲取信息,從而構造jsf頁面標記<h:message>與<h:messages>中所要顯示的詳細和摘要信息
    public static FacesMessage getMessage(Locale locale, String messageId,
                                          Object params[]) {
        FacesMessage result = null;
        String
            summary = null,
            detail = null,
            bundleName = null;
        ResourceBundle bundle = null;
        // see if we have a user-provided bundle
        if (null != (bundleName = getApplication().getMessageBundle())) {//資源包的名字
            if (null !=
                (bundle =
                ResourceBundle.getBundle(bundleName, locale,
                                         getCurrentLoader(bundleName)))) {//獲取資源包
                // see if we have a hit
                try {
                    summary = bundle.getString(messageId);//從資源包中獲取信息摘要
                } catch (MissingResourceException e) {
                }
            }
        }
        // 如果不能在用戶提供的資源包中獲取信息摘要,再從默認的資源包中查找
        if (null == summary) {
            // see if we have a summary in the app provided bundle
            bundle = ResourceBundle.getBundle(FacesMessage.FACES_MESSAGES,
                                              locale,
                                              getCurrentLoader(bundleName));
            if (null == bundle) {
                throw new nullPointerException(" bundle " + bundle);
            }
            // see if we have a hit
            try {
                summary = bundle.getString(messageId);
            } catch (MissingResourceException e) {
            }
        }
        // we couldn't find a summary anywhere!  return null
        if (null == summary) {
            return null;
        }
        // At this point, we have a summary and a bundle.
        if (null == summary || null == bundle) {
            throw new nullPointerException(" summary " + summary + " bundle " + 
                bundle);
        }
//參數替換,用參數params替換摘要中的{0},{1}等占位符。
        summary = substituteParams(locale, summary, params);
        try {
//詳細信息
            detail = substituteParams(locale,
                                      bundle.getString(messageId + "_detail"),
                                      params);
        } catch (MissingResourceException e) {
        }
        return (new FacesMessage(summary, detail));
    }
    //
    // Methods from MessageFactory
    // 
    public static FacesMessage getMessage(FacesContext context, String messageId) {
        return getMessage(context, messageId, null);
    }
    public static FacesMessage getMessage(FacesContext context, String messageId,
                                          Object params[]) {
        if (context == null || messageId == null) {
            throw new nullPointerException(" context " + context + " messageId " + 
                messageId);
        }
        Locale locale = null;
        // viewRoot may not have been initialized at this point.
        if (context != null && context.getViewRoot() != null) {
            locale = context.getViewRoot().getLocale();
        } else {
            locale = Locale.getDefault();
        }
        if (null == locale) {
            throw new nullPointerException(" locale " + locale);
        }
        FacesMessage message = getMessage(locale, messageId, params);
        if (message != null) {
            return message;
        }
        locale = Locale.getDefault();
        return (getMessage(locale, messageId, params));
    }
    public static FacesMessage getMessage(FacesContext context, String messageId,
                                          Object param0) {
        return getMessage(context, messageId, new Object[]{param0});
    }
    public static FacesMessage getMessage(FacesContext context, String messageId,
                                          Object param0, Object param1) {
        return getMessage(context, messageId, new Object[]{param0, param1});
    }
    public static FacesMessage getMessage(FacesContext context, String messageId,
                                          Object param0, Object param1,
                                          Object param2) {
        return getMessage(context, messageId,
                          new Object[]{param0, param1, param2});
    }
    public static FacesMessage getMessage(FacesContext context, String messageId,
                                          Object param0, Object param1,
                                          Object param2, Object param3) {
        return getMessage(context, messageId,
                          new Object[]{param0, param1, param2, param3});
    }
    protected static Application getApplication() {
        return (FacesContext.getCurrentInstance().getApplication());
    }
    //獲取當前的類加載器,需要該類加載器來查找資源包
    protected static ClassLoader getCurrentLoader(Object fallbackClass) {
        ClassLoader loader =
            Thread.currentThread().getContextClassLoader();
        if (loader == null) {
            loader = fallbackClass.getClass().getClassLoader();
        }
        return loader;
    }
} // end of class MessageFactory



當然我們需要在faces-config.xml中定義資源包名

程序代碼:
<application>
<locale-config>
  <default-locale>zh_CN</default-locale>
   <supported-locale>en</supported-locale>
   <supported-locale>fr</supported-locale>
   <supported-locale>es</supported-locale>
</locale-config>
<message-bundle>guessNumber.messages</message-bundle>
</application>



并寫出messages_zh_CN.properties文件:
javax.faces.validator.NOT_IN_RANGE=驗證錯誤:  數字應該在{0}和{1}之間
javax.faces.validator.LongRangeValidator.MAXIMUM=驗證錯誤:  數字不應該比{0}大
javax.faces.validator.LongRangeValidator.MINIMUM=驗證錯誤:數字不應該比{0}小  
javax.faces.validator.LongRangeValidator.TYPE=驗證錯誤:數字類型錯誤.  
javax.faces.component.UIInput.CONVERSION=轉換數字時發生錯誤.  
javax.faces.component.UIInput.REQUIRED=請輸入數字  ...

Linden.zhang 2007-06-06 17:27 發表評論
]]>
主站蜘蛛池模板: 乌拉特前旗| 确山县| 三明市| 汤原县| 通辽市| 山丹县| 东宁县| 桑植县| 轮台县| 杭州市| 高陵县| 阿克陶县| 南陵县| 永嘉县| 常山县| 翁牛特旗| 景德镇市| 雅江县| 青神县| 定陶县| 罗定市| 林州市| 武夷山市| 延边| 拜城县| 郸城县| 晋州市| 潼南县| 平凉市| 桦川县| 麟游县| 朝阳市| 余干县| 仁化县| 广灵县| 西乌珠穆沁旗| 阿勒泰市| 武安市| 宿迁市| 渑池县| 林州市|