posts - 310, comments - 6939, trackbacks - 0, articles - 3
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          自定義開發具有Ajax功能的JSF組件

          Posted on 2007-12-23 10:01 詩特林 閱讀(2740) 評論(4)  編輯  收藏 所屬分類: JSF
           

          自定義開發具有Ajax功能的JSF組件

          使用過VB的開發人員,對組件應該是了如指掌的了,感覺使用起來非常的方面且簡單。同樣的,JSF 擁有一個與 AWT GUI 組件模型類似的組件模型。可以用 JSF 創建可重用組件。但不幸的是,存在一個誤解:用 JSF 創建組件很困難。不要相信這些從未試過它的人們的 FUD!開發 JSF 組件并不困難。由于不用一遍又一遍重復相同的代碼,可以節約時間。一旦創建了組件,就可以容易地把組件拖到任何 JSP、甚至任何 JSF 表單中,如果正在處理的站點有 250 個頁面,這就很重要了。JSF 的大多數功能來自基類。因為所有的繁重工作都由 API 和基類完成,所以 JSF 把組件創建變得很容易。

          JSF 組件由兩部分構成:組件和渲染器。JSF組件類定義UI組件的狀態和行為;渲染器定義如何從請求讀取組件、如何顯示組件——通常通過HTML渲染。渲染器把組件的值轉換成適當的標記。事件排隊和性能驗證發生在組件內部。

          這里采用自定義開發具有Ajax功能的JSF組件為例,進行JSF組件開發的講解。

          下面是我要采取的步驟:

          定義監聽器

          l         創建一個類,擴展 PhaseListener

          擴展 UIComponent

          l         創建一個類,擴展 UIComponent

          l         保存組件狀態

          l         faces-config.xml 登記組件

          定義渲染器或者內聯地實現它

          l         覆蓋 encode

          l         覆蓋 decode

          l         faces-config.xml 登記渲染器

          創建定制標記,繼承 UIComponentTag

          l         返回渲染器類型

          l         返回組件類型

          l         設置可能使用 JSF 表達式的屬性

          代碼下載:JSFAjax.rar

          本文中所用到的lib如下圖所示:



          一、定義監聽器

          com.sterning.jsf.ajax. AjaxListener類:

          package com.sterning.jsf.ajax;

          import javax.faces.component.UIComponent;
          import javax.faces.component.UIViewRoot;
          import javax.faces.context.FacesContext;
          import javax.faces.event.PhaseEvent;
          import javax.faces.event.PhaseId;
          import javax.faces.event.PhaseListener;
          import javax.servlet.http.HttpServletRequest;

          import org.apache.commons.logging.*;

          public class AjaxListener implements PhaseListener {
              
          private static final transient Log log = LogFactory.getLog(com.sterning.jsf.ajax.AjaxListener.class);
              
          // 下面的常量定義了請求的參數,用以決定是否為Ajax組件的請求
              private static final String AJAX_PARAM_KEY = "com.sterning.jsf.ajax.AJAX_REQUEST";
              
          private static final String AJAX_CLIENT_ID_KEY = "com.sterning.jsf.ajax.AJAX_CLIENT_ID";
                  
              
          public AjaxListener() {
              }


              
          /**
               * 處理請求,從請求中獲得組件,處理后轉給response
               
          */

              
          public void afterPhase(PhaseEvent event) {
                  
          if (log.isInfoEnabled()) { log.info("BEGIN afterPhase()"); }
                  FacesContext context 
          = event.getFacesContext().getCurrentInstance();        
                  
                  HttpServletRequest request 
          = (HttpServletRequest)context.getExternalContext().getRequest();
                  String ajaxParam 
          = request.getParameter(AJAX_PARAM_KEY);
                  
                  
          // 檢查Ajax參數
                  if (ajaxParam != null && ajaxParam.equals("true")){
                      
          if (log.isInfoEnabled()) { log.info("This is an ajax request."); }
                      context.responseComplete(); 
                      
                      
          //取得Ajax組件ID
                      String componentId = request.getParameter(AJAX_CLIENT_ID_KEY);  
                      
          if (componentId == null){
                          
          if (log.isWarnEnabled()) { log.warn("No Client ID found under key: " + componentId); }
                      }
           else {
                          handleAjaxRequest(context, componentId);
                      }

                      
                      
          //保存頁面狀態
                      context.getApplication().getStateManager().saveSerializedView(context);
                  }

              }

              
              
          protected void handleAjaxRequest(FacesContext context, String ajaxClientId) {
                  UIViewRoot viewRoot 
          = context.getViewRoot();
                  
                  AjaxInterface ajaxComponent 
          = null;
                  
          try {
                      ajaxComponent 
          = (AjaxInterface)viewRoot.findComponent(ajaxClientId);
                  }
           catch (ClassCastException cce){
                      
          throw new IllegalArgumentException("Component found under Ajax key was not of expected type.");
                  }

                  
          if (ajaxComponent == null){
                      
          throw new NullPointerException("No component found under specified client id: " + ajaxClientId);
                  }

                  
                  ajaxComponent.handleAjaxRequest(context);
              }


              
          public void beforePhase(PhaseEvent arg0) {
                  
          // We do nothing in the before phase.
              }


              
          public PhaseId getPhaseId() {
                  
          return PhaseId.RESTORE_VIEW;
              }

          }


           

          二、擴展 UIComponent

           

          1定義接口

          com.sterning.jsf.ajax.AjaxInterface接口:

          package com.sterning.jsf.ajax;

          import javax.faces.context.FacesContext;

          /**
           * 該接口應該由Ajax組件類實現
           
          */


          public interface AjaxInterface {
              
              
          public void handleAjaxRequest(FacesContext context);
          }


          2.實現接口并繼承UIComponentBase類
          com.sterning.jsf.ajax.component. AjaxComponent
          package com.sterning.jsf.ajax.component;

          import javax.faces.component.UIComponentBase;
          import javax.faces.context.FacesContext;

          import org.apache.commons.logging.Log;
          import org.apache.commons.logging.LogFactory;

          import com.sterning.jsf.ajax.AjaxInterface;
          import com.sterning.jsf.ajax.AjaxRendererInterface;


          public class AjaxComponent extends UIComponentBase implements AjaxInterface {
              
          private static final transient Log log = LogFactory
                      .getLog(com.sterning.jsf.ajax.component.AjaxComponent.
          class);

              
          public static final String DEFAULT_RENDERER_TYPE = "com.sterning.jsf.ajax.component.AjaxComponentRenderer";

              
          public static final String COMPONENT_FAMILY = "com.sterning.jsf.ajax.component.AjaxComponent";

              
          public static final String COMPONENT_TYPE = "com.sterning.jsf.ajax.component.AjaxComponent";                                                                                        // Handler

              
          /**
               * 在構函數中的setRendererType(AjaxComponent.DEFAULT_RENDERER_TYPE) 和getFamily
               * 是指定用來生成HTML代碼的渲染器,渲染器需要在faces-config.xml中進行配制
               
          */

              
          public AjaxComponent() {
                  
          this.setRendererType(AjaxComponent.DEFAULT_RENDERER_TYPE);
              }


              @Override
              
          public String getFamily() {
                  
          return COMPONENT_FAMILY;
              }


              
          /**
               * 當Ajax發出請求時,Ajax監聽器將執行此方法
               
          */

              
          public void handleAjaxRequest(FacesContext context) {
                  
          // 通過Renderer進行代理
                  AjaxRendererInterface renderer = (AjaxRendererInterface) this
                          .getRenderer(context);
                  renderer.handleAjaxRequest(context, 
          this);
              }

          }


           

          三、定義渲染器

          下面要做的是內聯地定義渲染器的功能。

          1.定義渲染器接口

          com.sterning.jsf.ajax. AjaxRendererInterface接口

          package com.sterning.jsf.ajax;

          import javax.faces.component.UIComponent;
          import javax.faces.context.FacesContext;

          public interface AjaxRendererInterface {
              
          public void handleAjaxRequest(FacesContext context, UIComponent component);
          }


          2.實現接口并繼承Renderer類
          com.sterning.jsf.ajax.component. AjaxComponentRenderer類
          package com.sterning.jsf.ajax.component;

          import java.io.IOException;

          import javax.faces.component.UIComponent;
          import javax.faces.context.FacesContext;
          import javax.faces.context.ResponseWriter;
          import javax.faces.render.Renderer;
          import javax.servlet.http.HttpServletRequest;
          import javax.servlet.http.HttpServletResponse;

          import org.apache.commons.logging.*;

          import com.sterning.jsf.ajax.AjaxRendererInterface;


          public class AjaxComponentRenderer extends Renderer implements
                  AjaxRendererInterface 
          {
              
          private static final transient Log log = LogFactory
                      .getLog(com.sterning.jsf.ajax.component.AjaxComponentRenderer.
          class);

              
          private static final String INPUT_ID = "com.sterning.jsf.ajax.component.INPUT";

              
          private static final String INPUT_NAME = "com.sterning.jsf.ajax.component.INPUT";

              
          private static final String BUTTON_ID = "com.sterning.jsf.ajax.component.BUTTON";

              
          private static final String MESSAGE_DIV_ID = "com.sterning.jsf.ajax.component.MESSAGE_DIV";

              
          private static final String CLIENT_ID = "com.sterning.jsf.ajax.component.CLIENT_ID";

              
          /**
               * 定義渲染器。渲染器我們需要從Renderer類中繼承,不過我們一般情況下會繼承HtmlRenderer這個類,我們可以覆蓋decode
               * encodeBegin encodeChildren encodeEnd 來生成HTML
               
          */

              
          public AjaxComponentRenderer() {
              }


              
          public void encodeBegin(FacesContext context, UIComponent component)
                      
          throws IOException {
                  
          if (log.isTraceEnabled()) {
                      log.trace(
          "begin encodeBegin()");
                  }


                  HttpServletRequest request 
          = (HttpServletRequest) context
                          .getExternalContext().getRequest();

                  AjaxComponent ajaxComp 
          = (AjaxComponent) component;

                  ResponseWriter out 
          = context.getResponseWriter();

                  String clientId 
          = ajaxComp.getClientId(context);

                  out.startElement(
          "div", ajaxComp);
                  out.writeAttribute(
          "id", clientId, null);
                  out.writeAttribute(
          "style""border:solid; width:200; height:200;",
                          
          null);

                  out.startElement(
          "div", ajaxComp); // Message div
                  out.writeAttribute("id", MESSAGE_DIV_ID, null);
                  out.endElement(
          "div"); // Message div

                  out.startElement(
          "input", ajaxComp);
                  out.writeAttribute(
          "type""text"null);
                  out.writeAttribute(
          "id", INPUT_ID, null);
                  out.writeAttribute(
          "name", INPUT_NAME, null);
                  out.endElement(
          "input");

                  out.startElement(
          "button", component);
                  out.writeAttribute(
          "type""button"null);
                  out.writeAttribute(
          "name", BUTTON_ID, null);
                  out.writeAttribute(
          "id", BUTTON_ID, null);
                  out.writeAttribute(
          "value", BUTTON_ID, null);
                  out.writeText(
          "Ajax It""null");
                  out.endElement(
          "button");

                  
          // A hidden field to hold the URL of the server for the ajax request
                  out.startElement("input", ajaxComp);
                  out.writeAttribute(
          "id""com.sterning.jsf.ajax.component.SERVER"null);
                  out.writeAttribute(
          "type""hidden"null);
                  out.writeAttribute(
          "value", request.getScheme() + "://"
                          
          + request.getServerName() + ":" + request.getServerPort()
                          
          + request.getRequestURI(), null);
                  out.endElement(
          "input");

                  
          // A hidden field to hold the component Client ID
                  out.startElement("input", ajaxComp);
                  out.writeAttribute(
          "id", CLIENT_ID, null);
                  out.writeAttribute(
          "type""hidden"null);
                  out.writeAttribute(
          "value", clientId, null);
                  out.endElement(
          "input");

                  out.write(
          "\nAjax組件\n");
                  out.startElement(
          "script", ajaxComp);
                  out.write(
          "dojo.addOnLoad(AjaxComponent.loadComponent());\n"); 
                  out.endElement(
          "script");
              }


              
          /**
               * 處理頁面按鈕的請求, 該項按鈕通過上面的encodeBegin()方法設置
               
          */

              
          public void handleAjaxRequest(FacesContext context, UIComponent component) {
                  
          if (log.isInfoEnabled()) {
                      log.info(
          "BEGIN handleAjaxRequest()");
                  }

                  HttpServletRequest request 
          = (HttpServletRequest) context
                          .getExternalContext().getRequest();

                  String textField 
          = request.getParameter(INPUT_NAME);
                  String serverContribution 
          = "SERVER RESPONSE: ";
                  StringBuffer xml 
          = null;

                  
          if (textField == null{
                      
          if (log.isInfoEnabled()) {
                          log.info(
          "No parameter found for text field.");
                      }

                  }
           else {
                      
          if (log.isTraceEnabled()) {
                          log.trace(
          "textField: " + textField);
                      }


                      xml 
          = new StringBuffer("<response>");

                      xml.append(
          "<message>" + serverContribution + textField
                              
          + "</message>");

                      xml.append(
          "<status>OK</status></response>");
                  }


                  
          if (xml == null{
                      
          if (log.isInfoEnabled()) {
                          log.info(
          "Response is null.");
                      }

                      xml 
          = new StringBuffer(this.getErrorString());
                  }

                  HttpServletResponse response 
          = (HttpServletResponse) context
                          .getExternalContext().getResponse();

                  response.setContentType(
          "text/xml");
                  response.setHeader(
          "Cache-Control""no-cache");

                  
          try {
                      response.getWriter().write(xml.toString());
                      
          if (log.isInfoEnabled()) {
                          log.info(
          "Response sent: " + xml);
                      }

                  }
           catch (IOException e) {
                      
          if (log.isErrorEnabled()) {
                          log.error(
          "Error writing ajax response.", e);
                      }

                  }


              }


              
          protected String getErrorString() {
                  
          return new String(
                          
          "<response><message>There was a problem</message><status>ERROR</status></response>");
              }

          }


           

          四、創建定制標記

          JSF 組件不是天生綁定到 JSP 上的。要連接起 JSP 世界和 JSF 世界,需要能夠返回組件類型的定制標記(然后在 faces-context文件中登記)和渲染器。

          1.繼承UIComponentTagBase

          com.sterning.jsf.ajax.component. AjaxComponentTag

          package com.sterning.jsf.ajax.component;

          import org.apache.myfaces.shared_impl.taglib.UIComponentTagBase;

          import org.apache.commons.logging.*;

          public class AjaxComponentTag extends UIComponentTagBase {
              
          private static final transient Log log = LogFactory
                      .getLog(com.sterning.jsf.ajax.component.AjaxComponentTag.
          class);

              
          /**
               * 定義標簽,在這一步中我們需要繼承UIComponentTag這個類,但在實際應用中,
               * 我們可以繼承他的子類,比如在例子中我們就繼承HtmlOutputTextTagBase。在標簽類中,
               * 我們必須要覆蓋getComponentType方法和getRendererType,來指定這個標簽屬于哪個組件和渲染器,
               * 這兩個屬性的返回值都應和配制文件指定的值相同。
               
          */

              
          public AjaxComponentTag() {
              }


              @Override
              
          public String getComponentType() {
                  
          return AjaxComponent.COMPONENT_TYPE;
              }


              @Override
              
          public String getRendererType() {
                  
          return AjaxComponent.DEFAULT_RENDERER_TYPE;
              }

          }

           

          五、登記定制標記

          現在要做的全部工作就是創建一個 TLD(標記庫描述符)文件,以登記定制標記

          WebRoot/WEB-INF/tutorial.tld

          <?xml version="1.0" encoding="ISO-8859-1"?>

          <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

          <taglib xmlns="http://java.sun.com/JSP/TagLibraryDescriptor">
              
          <tlib-version>1.3</tlib-version>
              
          <jsp-version>1.2</jsp-version>
              
          <short-name>tut</short-name>
              
          <uri>http://www.tutorial.org/jsf</uri>
              
          <description>JSF Tutorial - Ajax</description>
              
          <tag>
                  
          <name>ajaxComponent</name>
                  
          <tag-class>com.sterning.jsf.ajax.component.AjaxComponentTag</tag-class>
                  
          <body-content>JSP</body-content>
                  
          <description>
                      The AjaxComponent example.
                  
          </description>
                  
          <!-- UIComponent attributes -->
                  
          <attribute>
                      
          <name>id</name>
                      
          <required>false</required>
                      
          <rtexprvalue>false</rtexprvalue>
                      
          <type>java.lang.String</type>
                      
          <description>
                          The developer-assigned ID of this component. The ID must
                          be unique within the scope of the tag's enclosing naming
                          container (e.g. h:form or f:subview). This value must be
                          a static value.
                      
          </description>
                  
          </attribute>
                  
          <attribute>
                      
          <name>binding</name>
                      
          <required>false</required>
                      
          <rtexprvalue>false</rtexprvalue>
                      
          <type>java.lang.String</type>
                      
          <description>
                          Identifies a backing bean property (of type UIComponent
                          or appropriate subclass) to bind to this component
                          instance. This value must be an EL expression.
                      
          </description>
                  
          </attribute>
                  
          <attribute>
                      
          <name>rendered</name>
                      
          <required>false</required>
                      
          <rtexprvalue>false</rtexprvalue>
                      
          <type>java.lang.String</type>
                      
          <description>
                          A boolean value that indicates whether this component
                          should be rendered. Default value: true.
                      
          </description>
                  
          </attribute>
              
          </tag>
          </taglib>
           

          一旦定義了 TLD 文件,就可以開始在 JSP 中使用標記了。

          WebRoot/webpages/index.jsp

          <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
           
          <%@ page language="java" pageEncoding="GB2312"%>
          <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
          <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

          <%@ taglib uri="http://www.tutorial.org/jsf" prefix="tut" %>

          <%--
          <%@ taglib uri="http://myfaces.apache.org/tomahawk" prefix="t" %>
          --%>

          <link rel="stylesheet" type="text/css" href='<c:url value="/includes/styles.css"/>'>
          <script type="text/javascript" src='<%=request.getContextPath()%>/javascript/utils.js'></script>
          <script type="text/javascript" src='<%=request.getContextPath()%>/javascript/dojo.js'></script>

          <f:view>
              
          <html>
                  
          <head>
                  
          </head>
                  
          <body>
                         
          <h:outputText value="自定義JSF Ajax組件"></h:outputText>
                         
          <form>
                             
          <tut:ajaxComponent/>
                         
          </form>
                  
          </body>
              
          </html>
          </f:view>

           

          順便,WebRoot/index.html的內容如下:

          <meta http-equiv="refresh" content="0; url=webpages/index.jsf">
           

          另外,還有兩個js文件,分別是Utils.jsdojo.js。分別位于WebRoot/javascript目錄下,請查看源代碼。

          六、配置文件

          WebRoot/WEB-INF/Web.xml文件的配置如下:

          <?xml version="1.0" encoding="UTF-8"?>
          <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
              xmlns:xsi
          ="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
              xsi:schemaLocation
          ="http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
              
          <!--
           * <b>Created:</b> Oct, 2007<br>
           * <b>Title:</b> JSF Ajax Component<br>
          -->

              
          <!-- SERVLET -->
              
          <servlet>
                  
          <servlet-name>Faces Servlet</servlet-name>
                  
          <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
                  
          <load-on-startup>1</load-on-startup>
              
          </servlet>
              
          <servlet-mapping>
                  
          <servlet-name>Faces Servlet</servlet-name>
                  
          <url-pattern>*.jsf</url-pattern>
              
          </servlet-mapping>
              
          </web-app>

           

          WebRoot/WEB-INF/faces-config.xml的代碼如下:

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">

          <faces-config>
                  
          <component>
                     
          <component-type>com.sterning.jsf.ajax.component.AjaxComponent</component-type>
                     
          <component-class>com.sterning.jsf.ajax.component.AjaxComponent</component-class>
                  
          </component>              
              
          <render-kit>    
                  
          <renderer>
                      
          <component-family>com.sterning.jsf.ajax.component.AjaxComponent</component-family>
                      
          <renderer-type>com.sterning.jsf.ajax.component.AjaxComponentRenderer</renderer-type>
                      
          <renderer-class>com.sterning.jsf.ajax.component.AjaxComponentRenderer</renderer-class>
                  
          </renderer>
              
          </render-kit>
              
          <!-- LIFECYCLE -->

              
          <lifecycle>
                  
          <phase-listener>com.sterning.jsf.ajax.AjaxListener</phase-listener>
              
          </lifecycle>

          </faces-config>

           

          其運行效果如下圖所示:


          評論

          # re: 自定義開發具有Ajax功能的JSF組件  回復  更多評論   

          2007-12-23 11:32 by JSF Fan
          真正的開發這么簡單的組件是遠遠不夠的,能否介紹像金蝶operamasks中datagrid那樣的復雜組件是怎么寫的?謝謝拉

          # re: 自定義開發具有Ajax功能的JSF組件  回復  更多評論   

          2007-12-23 13:16 by 千里冰封
          凡事都是從簡單做起的嘛

          # re: 自定義開發具有Ajax功能的JSF組件  回復  更多評論   

          2008-03-10 10:50 by gembin
          照上面的代碼我運行之后,為什么有如下錯誤
          SEVERE: Exception in PhaseListener RESTORE_VIEW(1) afterPhase
          java.lang.NullPointerException: No component found under specified client id: j_id_jsp_1270516925_2
          at com.sterning.jsf.ajax.AjaxListener.handleAjaxRequest(AjaxListener.java:60)
          at com.sterning.jsf.ajax.AjaxListener.afterPhase(AjaxListener.java:42)

          # re: 自定義開發具有Ajax功能的JSF組件  回復  更多評論   

          2008-03-10 10:57 by gembin
          不是原創吧
          主站蜘蛛池模板: 光山县| 武乡县| 上虞市| 泸水县| 田东县| 增城市| 洪泽县| 海城市| 灌阳县| 芮城县| 光山县| 白银市| 宁安市| 团风县| 淮阳县| 五指山市| 娱乐| 准格尔旗| 赤壁市| 汝阳县| 龙海市| 烟台市| 苍南县| 郸城县| 密山市| 武乡县| 阿克苏市| 兴安县| 张家口市| 航空| 桑植县| 晋中市| 崇左市| 上杭县| 陕西省| 通州区| 庆安县| 铜鼓县| 资源县| 陇南市| 清水河县|