DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> AJAX入門 >> AJAX基礎知識 >> DWR 簡化 Ajax 的 portlet 間通信
DWR 簡化 Ajax 的 portlet 間通信
編輯:AJAX基礎知識     

2006 年 9 月 04 日

許多開發人員都期待著利用 Ajax 技術來提高基於 Web 的應用程序的用戶體驗,但是 Ajax 編程可能是一項麻煩的任務。開放源碼的 Direct Web Remoting (DWR) 庫通過自動把 Java 類轉換成 JavaScript 類,可以為 Java™ 開發人員簡化 Ajax 開發。在這篇文章中,將學習如何用 DWR 和符合 JSR-168 規范的 portlet 迅速而容易地構建 Ajax 應用程序。

    Portlet 是基於 Java 平台的 Web 門戶應用程序。JSR-168 是開發 portlet 應用程序的 Java Community Process 標准,它描述了 portlet 生命周期管理、portlet 容器合約、打包、部署以及與門戶有關的其他方面。

    異步 JavaScript + XML(或者叫做 Ajax)是一項用於開發豐富、交互的 Web 應用程序的技術。Ajax 組合了 XML、HTML、DHTML、JavaScript 和 DOM。

    Portlet 和 Ajax 看起來彼此之間是完美搭配,因為它們都側重於用 Web 浏覽器作為向用戶呈現用戶界面的工具。把這兩者與 Java 技術組合在一起的簡易方式就是使用 DWR 庫。DWR 是 Apache 許可下的開放源碼 Java 庫,用於構建基於 Ajax 的 Web 應用程序。DWR 的基本目的是向開發人員隱藏 Ajax 的細節。您在服務器端使用普通 Java 對象(POJO),而 DWR 動態地生成 JavaScript 代理函數,所以使用 JavaScript 的客戶端開發感覺起來就像直接調用 JavaBean。DWR 的主要組件是一個 Java servlet,處理從浏覽器到服務器的調用。

    本文使用 DWR、基於三個 portlet 來構建一個示例 Ajax 應用程序。我將介紹如何把 DWR 與 porlet 應用程序集成,但是我不想深入 DWR 的幕後工作細節;在這個項目的 Web 站點和 developerWorks 的頁面上(請參閱 參考資料 獲得細節)可以找到關於這個庫的更多信息。要構建我描述的應用程序,需要 1.3 或以後版本的 Java 平台和符合 JSR-168 規范的的門戶環境。我用來開發和測試這個代碼的環境包含 IBM Rational Application Developer V6.0、Apache Jetspeed 2.0 portal 和 Java 5.0。

    在開始之前,還應當熟悉 portlet 和 Ajax 開發。如果願意學習關於這些主題的更多內容,請參閱下面的 參考資料 小節。可以從 下載 小節下載示例應用程序的完整代碼,包括 DWR。

構建示例的 portlet 間通信應用程序

我們的示例應用程序有三個 portlet:Orders、Order Details 和 Customer Details;圖 1 顯示了示例應用程序:


圖 1. 示例應用程序
示例應用程序

    Orders portlet 有一個訂單列表。當用戶點擊訂單號時,該 portlet 把訂單號發送給 Order Details 和 Customer Details portlet,這兩者然後顯示適當的訂單和客戶細節。

設置開發環境

     在可以開發 portlet 之前,需要設置開發環境和 DWR。我使用 IBM Rational Application Developer V6.0,它對 Java portlet 開發具有內置的支持,但是其他開發環境也可以。按以下步驟開始:

  1. 創建一個新的 JSR-168 portlet 項目。給項目起名為 InterPortletMessaging,但是現在還不創建任何 portlet。

  2. 下載 dwr.jar(版本 1.1;請參閱 參考資料 中的鏈接)。把 dwr.jar 添加到項目的 /WebContent//WEB-INF/lib 目錄。

  3. 打開 web.xml 並添加清單 1 中的代碼。這會把 DWR servlet 添加到應用程序。這個 servlet 被用在後台處理請求並把響應發送回浏覽器。



    清單 1. DWR servlet
    <servlet>
    
      <servlet-name>dwr-invoker</servlet-name>
    
      <display-name>DWR Servlet</display-name>
    
      <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
    
      <init-param>
    
        <param-name>debug</param-name>
    
        <param-value>true</param-value>
    
      </init-param>
    
    </servlet>
    
    <servlet-mapping>
    
      <servlet-name>dwr-invoker</servlet-name>
    
      <url-pattern>/dwr/*</url-pattern>
    
    </servlet-mapping>
    
                      



  4. 用清單 2 中的代碼在 WEB-INF 目錄創建一個名為 dwr.xml 的文件。這個文件是 DWR 的配置文件,它告訴容器哪些類在 JavaScript 中是可用的。DWR 讀取這個 XML 文件並動態地生成把 Java 類表示成 JavaScript 類的 JavaScript 代碼。這樣您就可以在浏覽器中提供由這些 Java 類提供的功能。目前還沒有真正創建在清單 2 中引用的 Java 類,但是我們先來看一下。



    清單 2. dwr.xml
    <!DOCTYPE dwr PUBLIC
    
        "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN"
    
        "http://www.getahead.ltd.uk/dwr/dwr10.dtd">
    
    <dwr>
    
      <allow>
    
        <create creator="new" javascript="MessagingBean">
    
          <param name="class" value="msg.MessagingBean"/>
    
        </create>
    
      </allow>
    
    </dwr>
    
                      

     現在可以在 portlet 中使用 DWR 了。但是在創建示例 portlet 之前,需要創建一個消息 bean 和一個偽裝數據庫 bean(充當示例應用程序的後端)。

 

 

 

創建 MockupDB 和 MessagingBean

     清單 3 所示的 MockupDB 是個單體類,模擬客戶訂單的數據庫。所有訂單都硬編碼在這個類中。真實應用程序可能使用關系數據庫系統,但這個示例對我們的目的來說足夠了。


清單 3. MockupDB
package db;

import java.util.Hashtable;

import java.util.Map;

public class MockupDB {

  private static MockupDB instance=new MockupDB();

  private String[] orders=new String[4];

  private Map orderDetails=new Hashtable();

  private Map customerDetails=new Hashtable();

  private MockupDB()

  {

    String ordStart="ORD";

    orders[0]=ordStart+"000408015";

    orders[1]=ordStart+"001600023";

    orders[2]=ordStart+"000042000";

    orders[3]=ordStart+"011235813";

    orderDetails.put(orders[0],"1. WebSphere Everyplace Connection Manager<br/>"+

                     "2. WebSphere Portal");

    orderDetails.put(orders[1],"1. DB2 Universal Database<br/>2. DB2 Everyplace");

    orderDetails.put(orders[2],"1. Tivoli Access Manager for e-business <br/>2."+

                     "Tivoli Directory Integrator");

    orderDetails.put(orders[3],"1. IBM System z9<br/>2. IBM System p5 550 Express");

    customerDetails.put(orders[0],"<b>Systems and Technology Group</b><br/>"+

                        "Some Road<br/>Finland");

    customerDetails.put(orders[1],"<b>Global Financing</b><br/>Another Street"+

                        "<br/>Finland");

    customerDetails.put(orders[2],"<b>Software</b><br/>Yet Another Road"+

                        "<br/>Finland");

    customerDetails.put(orders[3],"<b>Global Services</b><br/>Still Another "+

                        "Street<br/>Finland");

  }

  public static MockupDB getInstance()

  {

    return instance;

  }

  public String[] getOrders()

  {

    return orders;

  }

  public String getOrderDetails(String orderNro)

  {

    return (String)orderDetails.get(orderNro);

  }

  public String getCustomerDetails(String orderNro)

  {

    return (String)customerDetails.get(orderNro);

  }

}

                  

     清單 4 所示的 MessagingBean 是個簡單的 POJO,有兩個方法,都接受訂單號,但是分別返回訂單細節和客戶細節。MessagingBeanMockupDB 得到細節。


清單 4. MessagingBean
package msg;

import javax.servlet.http.HttpSession;

import db.MockupDB;

public class MessagingBean {

  public MessagingBean()

  {        

  }

  public String getOrderDetails(String orderNumber,HttpSession httpSession)

  {

    String orderDetails=MockupDB.getInstance().getOrderDetails(orderNumber)

    httpSession.setAttribute("orderDetailsOrderNumber",orderNumber);

    httpSession.setAttribute("orderDetails",orderDetails);

    return orderDetails;

  }

  public String getCustomerDetails(String orderNumber,HttpSession httpSession)

  {

    String customerDetails=MockupDB.getInstance().getCustomerDetails(orderNumber);

    httpSession.setAttribute("customerDetailsOrderNumber",orderNumber);

    httpSession.setAttribute("customerDetails",customerDetails);

    return customerDetails;

  }

}

                  

MessagingBean 還把訂單細節和客戶細節添加到 HttpSession

javaScriptFunctions.jsp

    javaScriptFunctions.jsp 導入了來自 DWR 的 JavaScript 庫(engine.js)並動態地創建庫 MessagingBean.js。注意,MessagingBean.js 使用的名稱與 dwr.xml(清單 2)中的 JavaBean 的名稱相同;實際上,DWR 生成 MessagingBean.js。DWR 框架使用 engine.js 庫;作為開發人員,通常不需要考慮直接使用它。

    如清單 5 所示,sendOrderNr() 函數調用 清單 4 中定義的 MessagingBean 函數。DWR 自動把 HttpSession 添加到方法調用。JavaScript 函數中的最後一個參數是 callback 函數。在稍後創建的 portlet JSP 中,包含這個 JSP。


清單 5. javaScriptFunctions.jsp
<%@ page contentType="text/html" 

import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>

<%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>

<portlet:defineObjects/>

<SCRIPT type="text/javascript"

 src='<%= renderResponse.encodeURL(renderRequest.getContextPath() +

 "/dwr/interface/MessagingBean.js") %>'> 

</SCRIPT>

<SCRIPT type="text/javascript"

 src='<%= renderResponse.encodeURL(renderRequest.getContextPath() + 

 "/dwr/engine.js") %>'> 

</SCRIPT>

<SCRIPT type="text/javascript">

function <portlet:namespace />sendOrderNr(orderNr)

{

document.getElementById("orderDetailsOrderNumber").innerHTML=orderNr;

document.getElementById("customerDetailsOrderNumber").innerHTML=orderNr;

MessagingBean.getOrderDetails(orderNr,<portlet:namespace />showOrderDetails);

MessagingBean.getCustomerDetails(orderNr,<portlet:namespace />showCustomerDetails);

return false;

}

function <portlet:namespace />showOrderDetails(orderDetails)

{

document.getElementById("orderDetails").innerHTML=orderDetails;

return false;

}

function <portlet:namespace />showCustomerDetails(customerDetails)

{

document.getElementById("customerDetails").innerHTML=customerDetails;

return false;

}

</SCRIPT>

                  

 

 


 

創建 portlet

    現在有了後端和代理函數,可以開發 portlet 本身了。所有三個 portlet 都使用相同的代碼基;惟一的區別是每個 portlet 使用的 JSP 的名稱。

  1. 使用清單 6 中的代碼創建一個新 portlet,並給它起名為 Orders:



    清單 6. Orders.java
    package interportletmessagingusingajax;
    
    import java.io.*;
    
    import javax.portlet.*;
    
    public class Orders extends GenericPortlet {
    
      // JSP folder name
    
      public static final String JSP_FOLDER = "/interportletmessagingusingajax/jsp/";
    
      // JSP file name to be rendered on the view mode
    
      public static final String VIEW_JSP = "OrdersView";         
    
      public void init(PortletConfig config) throws PortletException{
    
        super.init(config);
    
      }
    
      public void doView(RenderRequest request, RenderResponse response) 
    
        throws PortletException, IOException {
    
        // Set the MIME type for the render response
    
        response.setContentType(request.getResponseContentType());
    
        // Invoke the JSP to render
    
        PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher( 
    
          getJspFilePath(request, VIEW_JSP));
    
        rd.include(request,response);
    
        //this is workaround for portletsession sharing between
    
        //servlets and portlets
    
        //see http://weblogs.java.net/blog/wholder/archive/2005/02/session_session.html
    
        //and http://mail-archives.apache.org/mod_mbox/portals-pluto-dev/200502.mbox/%3Ca
    
        //2519328f3ba1d1eddfc33c924b6805d@umich.edu%3E
    
        //
    
        PortletRequestDispatcher rd2 = getPortletContext().getRequestDispatcher("/dwr/");
    
        rd2.include(request, response);
    
      }
    
      private static String getJspFilePath(RenderRequest request, String jspFile) {
    
        String markup = request.getProperty("wps.markup");
    
        if( markup == null )
    
          markup = getMarkup(request.getResponseContentType());
    
        return JSP_FOLDER+markup+"/"+jspFile+"."+getJspExtension(markup);
    
      }
    
      private static String getMarkup(String contentType) {
    
        if( "text/vnd.wap.wml".equals(contentType) )
    
          return "wml";
    
        return "html";
    
      }
    
      private static String getJspExtension(String markupName) {
    
        return "jsp";
    
      }
    
    }
    
                      



  2. 創建並打開 OrdersView.jsp(在 interportletmessagingusingajax/jsp/html 目錄),並把清單 7 中的代碼添加到它:



    清單 7. OrdersView.jsp
    <%@ page contentType="text/html" 
    
    import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
    
    <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
    
    <portlet:defineObjects/>
    
    <jsp:include page="javascriptFunctions.jsp" />
    
    <DIV style="margin: 6px">
    
    <H4 style="margin-bottom: 3px">Orders</H4>
    
    <table cellspacing="0" cellpadding="5" border="1">
    
    <% db.MockupDB database= db.MockupDB.getInstance();
    
    String[] orders=database.getOrders();
    
    for(int i=0;i<orders.length;i++)
    
    {
    
    %>
    
    <tr>
    
    <td><%="000000000"+String.valueOf(i+1) %></td>
    
    <td><a href="" onclick="return <portlet:namespace />sendOrderNr('<%=
    
      orders[i]%>');"><%=orders[i]%></a></td>
    
    </tr>
    
    <%
    
    }
    
     %>
    
    </table>
    
    </DIV>
    
                      



  3. 第二個 portlet 是 OrderDetailsPortlet.java。對這個 portlet 使用 清單 6 中的代碼,並把 VIEW_JSP 變量的值改成 OrdersDetailsPortletView.jsp。這個 JSP 的代碼如清單 8 所示:

    清單 8. OrdersDetailsPortletView.jsp
    <%@ page contentType="text/html" 
    
    import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
    
    <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
    
    <portlet:defineObjects/>
    
    <DIV style="margin: 6px">
    
    <H4 style="margin-bottom: 3px">Order details</H4>
    
    <table cellspacing="0" cellpadding="5" border="1">
    
    <tr>
    
    <th>Order number</th>
    
    <th>Order details</th>
    
    </tr>
    
    <tr>
    
    <%
    
    String orderDetailsOrderNumber=(String)renderRequest.getPortletSession().getAttribute(
    
      "orderDetailsOrderNumber",PortletSession.APPLICATION_SCOPE);
    
    String orderDetails=(String)renderRequest.getPortletSession().getAttribute(
    
      "orderDetails",PortletSession.APPLICATION_SCOPE);
    
    if(orderDetailsOrderNumber==null)
    
    {
    
    orderDetailsOrderNumber="";
    
    }
    
    if(orderDetails==null)
    
    {
    
    orderDetails="";
    
    }
    
    %>
    
    <td><div id="orderDetailsOrderNumber"><%=orderDetailsOrderNumber%>
    
    </div></td>
    
    <td><div id="orderDetails"><%=orderDetails%></div></td>
    
    </tr>
    
    </table>
    
    </DIV>
    
                      



  4. 第三個 portlet 是 CustomerDetailsPortlet.java。對這個 portlet 使用 清單 6 中的代碼,並把 VIEW_JSP 變量的值改成 CustomerDetailsPortletView.jsp。這個 JSP 的代碼如清單 9 所示:

    清單 9. CustomerDetailsPortletView.jsp
    <%@ page contentType="text/html" 
    
    import="java.util.*,javax.portlet.*,interportletmessagingusingajax.*" %>
    
    <%@taglib uri="http://java.sun.com/portlet" prefix="portlet" %>
    
    <portlet:defineObjects/>
    
    <%
    
    %>
    
    <DIV style="margin: 6px">
    
    <H4 style="margin-bottom: 3px">Customer details</H4>
    
    <table cellspacing="0" cellpadding="5" border="1">
    
    <tr>
    
    <th>Order number</th>
    
    <th>Customer details</th>
    
    </tr>
    
    <tr>
    
    <%
    
    String customerDetailsOrderNumber=
    
      (String)renderRequest.getPortletSession().getAttribute(
    
      "customerDetailsOrderNumber",PortletSession.APPLICATION_SCOPE);
    
    String customerDetails=(String)renderRequest.getPortletSession().getAttribute(
    
      "customerDetails", PortletSession.APPLICATION_SCOPE);
    
    if(customerDetailsOrderNumber==null)
    
    {
    
    customerDetailsOrderNumber="";
    
    }
    
    if(customerDetails==null)
    
    {
    
    customerDetails="";
    
    }
    
    %>
    
    <td><div id="customerDetailsOrderNumber"><%=customerDetailsOrderNumber%>
    
    </div></td>
    
    <td><div id="customerDetails"><%=customerDetails%></div></td>
    
    </tr>
    
    </table>
    
    </DIV>
    
                      

示例應用程序現在准備好了。下一步是把 portlet 打包成 WAR 文件並在 Apache Jetspeed 門戶中測試它。

 

 

 

測試示例應用程序

     在這一節,將看到示例應用程序的作用。首先,創建 portlet WAR 並把它安裝到 Jetspeed 門戶。然後,把三個 portlet 添加到門戶,看它們是如何工作的。將把它們全都構建到一個頁面,但如果需要也可以把它們放到多個頁面;幕後的機制仍然起作用。

把 portlet 應用程序安裝到 Jetspeed

把 portlet WAR 文件安裝到 Jetspeed 的方法是把 WAR 文件拷貝到 <Jetspeed install dir>/webapps/jetspeed/WEB-INF/deploy 目錄。然後 Jetspeed 會自動安裝 portlet,portlet 即可使用了。

使用以下步驟把新頁面添加到 Jetspeed 門戶:

  1. 進入 Jetspeed 門戶,並作為管理員登錄。

  2. 點擊右下角的 Edit 圖標,並添加名為 Inter-Portlet Messaging 的新頁面,如圖 2 所示:



    圖 2. 添加新頁面
    添加新頁面



  3. 選擇 Inter-Portlet Messaging 頁面並點擊 Edit 圖標。然後點擊 Add a portlet 圖標,在這個頁面上添加 portlet。選擇 Orders、Order DetailsCustomer Details portlet,並點擊 Select portlets,把選中的 portlet 添加到門戶頁面。完成之後,頁面看起來應當像圖 3:

    圖 3. 頁面上的 Portlet
    頁面上的 Portlet

Portlet

Orders portlet 如圖 4 所示,列出訂單:


圖 4. Orders portlet
Orders portlet

     在點擊訂單號時,其他 portlet 顯示這個訂單的細節。Customer Details portlet 顯示客戶信息,如圖 5 所示。信息檢索自 MockupDB


圖 5. Customer Details portlet
Customer Details portlet

Order Details portlet 也顯示檢索自 MockupDB 的信息,如圖 6 所示:


圖 6. Order Details portlet
Order Details portlet

     如果喜歡,可以回過去,向不同的頁面添加一個或多個 portlet。將會看到,portlet 不需要在單個頁面上,因為 portlet 內容保存在用戶會話中。

 

 

 

結束語

     這篇文章介紹了用 Ajax 實現 portlet 間通信的一種方式。Ajax 是開發交互式 Web 頁面的一種非常強大的技術,而支持 Ajax 的 portlet 通過消除門戶中典型存在的請求-響應延遲,極大地改善了用戶體驗。

     可以用本文中的代碼作為開發您自己的應用程序的起點;文中的代碼還顯示了 DWR 如何把 Java 編程模型擴展到 Web 浏覽器。使用 DWR,JavaBean 幾乎就像是在浏覽器中可用一樣。DWR 幾乎隱藏了 Ajax 的全部細節,讓您可以專注於手頭的工作,而不必考慮 Ajax 開發的具體細節,從而簡化了工作。

下載


描述 名字 大小 下載方法 Source code j-ajaxportlet.war 3MB HTTP 關於下載方法的信息 Get Adobe® Reader®

參考資料

學習
  • 您可以參閱本文在 developerWorks 全球站點上的 英文原文

  • 掌握 Ajax”(Brett McLaughlin,developerWorks,2006 年):趕上 Ajax 編程和概念的速度。

  • DWR: Easy Ajax for Java:用來自包創建者的這套資源開始了解 DWR。

  • 結合 Direct Web Remoting 使用 Ajax ”(Philip McCarthy,developerWorks,2005 年 11 月):學習如何用 DWR 直接把 JavaBean 方法公開給 JavaScript 代碼並自動進行 Ajax 的繁重工作。

  • 使用 Apache Pluto 構建和測試 JSR 168 兼容的 portlets (Mark Talbot 和 Kulvir Singh Bhogal,developerWorks,2006 年 4 月):構建、編譯、打包和部署簡單的 portlet 到 Pluto,測試 JSR 168 的符合程度。

  • developerWorks Web 開發專區:專注於介紹各種基於 Web 的解決方案的文章。

  • 比較 JSR 168 Java Portlet 規范與 IBM Portlet API”(Stefan Hepper,developerWorks,2003 年 12 月):學習關於 Java portlet 開發的更多內容。


獲得產品和技術
  • Direct Web remoting:下載這個開放源碼的、使得本文中開發的應用程序成為可能的 Java 庫。

  • Apache Jetspeed:下載並找到關於這個門戶的更多內容。

  • Rational Application Developer for WebSphere Software V6.0:下載這個開發環境的免費試用版。

關於作者

Sami Salkosuo 從 1999 年起一直在 IBM 工作。他的主要興趣領域是 Java 編程,是 Sun 認證 Java 程序員,IBM 認證的 XML 和相關技術解決方案開發人員,IBM 認證的 IBM WebSphere Portal 解決方案開發人員。除了 Java 技術,他還有 Python、Fortran、LabVIEW、Visual Basic、LISP、Perl 和 PHP 的經驗。

 

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved