开发学院软件开发Java 使用 WebSphere Application Server Feature Pack... 阅读

使用 WebSphere Application Server Feature Pack for Web 2.0 创建基于 Ajax 的动态 Web 应用程序

 2010-01-08 00:00:00 来源:WEB开发网   
核心提示:简介与 Web 2.0 相关的技术,比如 Asynchronous JavaScript™ XML (Ajax)、Web 远程和 Web 消息传递等,使用 WebSphere Application Server Feature Pack for Web 2.0 创建基于 Ajax 的动态 Web 应用程序

简介

与 Web 2.0 相关的技术,比如 Asynchronous JavaScript™ XML (Ajax)、Web 远程和 Web 消息传递等,在当今的 Web 应用程序中变得日益流行。与传统 Web 应用程序相比,基于 Ajax 的应用程序可以提供更好的响应性和交互性。在那些并入了 Ajax 架构的 Web 应用程序中,用户不需要等待整个 Web 重载就能够看到来自服务器的新结果,并且他们可以使用更少的步骤在单个 Web 页面中完成任务,这个 Web 页面通常以增量式或按需供应的方式呈现。

为了满足对快速开发和交付支持 Ajax 的解决方案的日益迫切的需求,IBM WebSphere Application Server Feature Pack for Web 2.0 提供了一组丰富的组件集,使您能够轻松、高效地构建基于 Ajax 的应用程序。它还提供了一个基于开放标准的框架,用于将现有服务或解决方案资产集成到富 Internet 应用程序中。

功能部件包的主要组件包括:

Ajax 客户机/代理运行时

RPC (Remote Procedure Call) 适配器

Web 消息传递服务

JSON4J (JavaScript Object Notation for Java) 库

IBM SOAP 库

IBM Atom 库

IBM OpenSearch 库

IBM Gauge 部件

本文描述了使用 Web 2.0 功能部件包构建基于 Ajax 的图表应用程序的步骤。通过遵循这个示例,您将能够了解如何使用功能部件包中的组件来构建一个具有丰富用户体验的完整 Web 2.0 解决方案。

先决条件

本次练习假设您具有基本的 Web 应用程序开发知识,并且熟悉 Eclipse 或 IBM Rational® Application Developer。要完成这里介绍的步骤,您需要将 WebSphere Application Server Feature Pack for Web 2.0 成功安装到一个正常运行的 WebSphere Application Server(V6.0、6.1 或 7.0)环境中。

样例动态应用程序简介

本文附带的样例应用程序旨在演示使用 Web 2.0 功能部件包的主要组件构建基于 Ajax 的应用程序的方法,同时仍然能够满足不断变化的业务需求。这个样例应用程序使用动态图表报告在给定时间期限内汽车品牌的销售数量(使用条形图),以及让用户选择一个特定品牌来按地区查看销售分布(使用饼图)。此外,当后端数据发生变化时,更新后的数据将自动反应到这些图表中并呈现给用户。

样例应用程序 DynamicCharts 具有以下特性:

提供多个汽车品牌的汽车销售图表视图。

提供一个图表视图,可针对特定品牌按地区下钻销售分布信息。

按照配置好的时间间隔(初始值为 15 秒)自动更新显示在 Web 浏览器中的图表。

提供灵活的布局,使用户能够调整主视图和细节视图的大小。

DynamicCharts 应用程序使用以下功能部件包工具构建:

Ajax 客户机运行时

RPC 适配器

Web 消息传递服务

JSON4J

图 1 演示了 DynamicCharts 应用程序的主要功能,而图 2 描述了应用程序的整体结构和流程。


图 1. DynamicCharts 示例的功能
使用 WebSphere Application Server Feature Pack for Web 2.0 创建基于 Ajax 的动态 Web 应用程序

查看原图(大图)

图 2. DynamicCharts 示例的整体结构和流程
使用 WebSphere Application Server Feature Pack for Web 2.0 创建基于 Ajax 的动态 Web 应用程序

在图 2 中,可以看到由 Ajax 客户机运行时实现的客户机逻辑:

从图表数据服务的 facade 接口检索数据 —— 该服务可以为现有服务或解决方案资产 —— 并由 RPC 适配器公开。

创建或更新图表。

处理由用户和 Web 消息传递服务触发的事件。

由客户机发出的对 RPC 适配器的调用全部基于 JSON RPC,因此返回的数据被封装到 JSON 对象中,后者可以在客户端快速解析。图表数据更新器模拟其他将在某些情况下更新图表数据的应用程序或服务,通过 WebSphere Application Server Service Integration Bus (SIBus) 和 Web 消息传递服务将数据更改事件发布到客户机。图表数据服务和图表数据更新器都依赖图表数据访问器来从数据存储加载数据,或向其中存储数据。(考虑到本文的目的,样例应用程序中的图表数据访问器不会连接到实际的数据存储中)。

下一小节将遍历创建 DynamicCharts 应用程序的过程。涉及的主要步骤包括:

导入基本的组件

使用 Dojo Toolkit 创建图表应用程序

通过 RPC 适配器将 ChartService 公开到 JavaScript 客户机

使用 Web 消息传递将数据更新发布到 Web 浏览器

本文附带了完整的样例应用程序,包括 WAR 和源文件,您可以 下载并部署 它们。

导入基本的组件

在开发环境(即 Eclipse 或 Rational Application Developer)中创建一个名为 “DynamicCharts” 的 Dynamic Web Project and EAR。图 3 展示了项目目录结构。

图 3. DynamicCharts 项目目录结构
使用 WebSphere Application Server Feature Pack for Web 2.0 创建基于 Ajax 的动态 Web 应用程序

如果使用的是 Eclipse,将 Ajax 客户机运行时和实用 JAR 导入到 WebContent 文件夹。

在 dataservice 包中实现一组 Java™ 类。

创建 dynamic-chars.html 页面。

如果使用的是 Rational Application Developer V7.5 或更高版本,那么您就不必显式地导入 Ajax 客户机运行时和 Web 2.0 实用 JAR,因为它们将在您在 Project Faces 中启用 Web 2.0 时获得。

如果使用 Eclipse 创建这个样例应用程序,那么将需要执行额外的一些步骤:

从 Web 2.0 功能部件包安装根目录(通常为 <app_server_root>/web2fep)中将 ajax-rt_1.X 文件夹复制到新创建的项目的 WebContent 文件夹中(图 4)。

图 4. Ajax 客户机运行时
使用 WebSphere Application Server Feature Pack for Web 2.0 创建基于 Ajax 的动态 Web 应用程序

查看原图(大图)

在 Web 2.0 功能部件包安装根目录的 optionalLibraries 文件夹下寻找这 5 个 JAR 文件,然后将它们复制到您的项目的 WebContent/WEB-INF/lib 文件夹中(图 5):

commons-logging-1.0.4.jar

RPCAdapter.jar

RPCAdapter-annotation.jar

retroweaver-rt-2.0.jar

webmsg_applib.ja



图 5. Web 2.0 实用 JAR
使用 WebSphere Application Server Feature Pack for Web 2.0 创建基于 Ajax 的动态 Web 应用程序

查看原图(大图)

当将必需的库导入到项目中时,您就可以开始编写应用程序代码了。

使用 Dojo Toolkit 创建图表应用程序

Dojo Toolkit 是一个强大的、灵活的、模块化的、开源的 Ajax 软件开发工具箱,使您能够轻松地将动态功能构建到 Web 页面中。Web 2.0 功能部件包引入了 Dojo Toolkit 1.1 和 IBM 扩展,比如 Ajax 客户机运行时。在本节中,您将看到使用 Dojo Toolkit 实现一个图表页面是多么的简单,这个图表页面提供用户交互和后端服务。

创建一个 HTML 页面并加载基 Dojo 脚本 (ajax-rt_1.X/dojo/dojo.js),其中头部分有一个 <script/> 标记。这提供了 Dojo 的核心功能以及对所有其他 Dojo 工具的访问。

在 <style/> 标记中导入 Dojo 样式,并使用 dojo.require(…) 声明将在 Web 页面中引用的 Dojo 包,如清单 1 所示。

清单 1. 声明 Dojo 组件和主题
<style type="text/css"> 
  @import "ajax-rt_1.X/dojo/resource/dojo.css"; 
  @import "ajax-rt_1.X/dijit/themes/tundra/tundra.css"; 
  @import "dynamic-charts.css"; 
</style> 
 
<script type="text/javascript" src="ajax-rt_1.X/dojo/dojo.js" 
  djConfig="isDebug: false, parseOnLoad: true, usePlainJson: true"></script> 
 
<script type="text/javascript"> 
  dojo.require("dojox.charting.Chart2D"); 
  dojo.require("dijit.layout.BorderContainer"); 
  dojo.require("dijit.layout.ContentPane"); 
  dojo.require("dojo.rpc.JsonService"); 
  dojo.require("dojox.cometd"); 
  dojo.require("dojox.charting.themes.PlotKit.orange"); 
</script>

此后,在 HTML 文件的脚本体部分定义页面布局(清单 2):

Dojo 布局小部件 dijit.layout.BorderContainer 在这里被用于页面布局,它被分为两个 dijit.layout.ContentPanes,一个用于汽车销售表,另一个用于分布表。

第二个 dijit.layout.ContentPane 的 splitter 属性被设置为 true,这使用户能够修改和调整这两个区域的宽度。

在每个区域中,有一个或两个 <div> 标记被作为节点嵌入,您可以在其中创建图表或单选按钮组。

注意,原始的 dijit.layout.SplitContainer 在 Dojo 1.1 中已经不受支持,并且引入了 dijit.layout.BorderContainer 作为替代品。



清单 2. 定义图表页面布局
<div dojoType="dijit.layout.BorderContainer" design="sidebar" id="main"> 
  <div dojoType="dijit.layout.ContentPane" region="center" id="sale_pane"> 
    <p>Car Sale</p> 
    <div id="car_sale_chart"></div> 
  </div> 
  <div dojoType="dijit.layout.ContentPane" 
    region="trailing" splitter="true" id="distribution_pane"> 
    <p>Choose a brand to view sale distribution by area</p> 
    <div id="brand_picker"></div> 
      <div id="distribution_by_area"></div> 
    </div> 
</div>

您现在将开始处理 JavaScript 代码。下表中总结将要实现的 JavaScript 函数:

函数触发函数/事件任务描述
init()页面装置事件调用 getSaleData() 并注册 Web 消息传递处理程序
webmsgHandler(…)Web 消息传递服务处理由 Web 消息传递服务触发的事件
getSaleData()init()注册 JSON RPC 处理程序并加载销售数据
showCarSale(…)getSaleData()解析返回的销售数据并调用函数来生成图表
makeSaleChart(…)showCarSale()创建销售图表
makeBrandPicker(…)showCarSale()创建品牌单选组合并注册 onclick 处理程序
brandPickerHander()用户的动作识别所选的品牌并调用函数来生成图表
getDistributionData(…)brandPickerHander()注册 JSON RPC 处理程序并加载分布数据
makeDistributionChart(…)getDistributionData(…)创建分布图


接着,您需要加载并解析来自后端服务的汽车销售数据。清单 3 中的 JavaScript 片段解释了如何通过 JSON RPC 调用和 RPC 适配器调用图表数据服务。您只需要使用由 RPC 适配器公开的给定 URL 创建一个 Dojo JSON 服务实例。

第一个参数:

/DynamicCharts/RPCAdapter/jsonrpc/ChartDataService/getCarSale

意味着调用 ChartDataService 类(稍后详细讨论)的 getCarSale() 方法,然后将回调函数 showCarSale(...) 注册到 JSON 服务实例,返回的 JSON 格式化的数据在这里被解析,而品牌和数量数据被分别放入到数组中。

还可以使用另一种方法执行 JSON RPC 调用 —— 使用 Dojo XMLHTTPRequest 包装器 (dojo.xhr) —— 但是这需要更多的代码,而且也没有清单 3 所示的那么简单。



清单 3. 加载和解析汽车销售数据
// Retrieve the sale data via JSON RPC invocation and make 
// the charts. 
function getSaleData() { 
  // Call the json service to retrieve the car sale data 
  var json_svc = new dojo.rpc.JsonService( 
    "/DynamicCharts/RPCAdapter/jsonrpc/ChartDataService/getCarSale"); 
  json_svc.getCarSale().addCallback(showCarSale); 
} 
 
// Parse the sale data and create the sale chart and the brand picker 
// function showChart(data, ioArgs) { 
function showCarSale(sale_data) { 
  // Parse the data returned from the server 
  var sale_brands = new Array(); 
  var sale_quantities = new Array(); 
for (var i=0;i<sale_data.length;i++) { 
    sale_brands[i] = {value:(i+1), text:sale_data[i].brand}; 
    sale_quantities[i] = sale_data[i].quantity; 
  } 
 
  // Make the auto sale chart 
  makeSaleChart(sale_brands, sale_quantities); 
 
  // Make the brand picker 
  makeBrandPicker(sale_brands); 
}

一旦加载并解析了图表数据后,将调用 makeSaleChart 函数来创建销售图表(清单 4)。Dojo Chart2D 对象通常包含 4 个组成部分:主题、图、轴(axis)和系列(series)数据。addPlot 调用判断您将要生成哪种类型的图表,有许多图类型可用。最常用的是线图、条形图、列图、区域图、网格图、标记图、饼图和堆栈图等等。在这里,“列” 被用做竖形条形图,用于表示汽车销售。addPlot 和 addAxis 函数包含两个参数:一个名称和一个参数数组。addSeries 函数接受额外的参数:数据系列数组。

清单 4. 创建汽车销售图表
// Create the sale chart 
function makeSaleChart(brands, quantities) { 
  if (sale_chart == undefined) { 
    // Make the sale chart for the first request of a given user 
    sale_chart = new dojox.charting.Chart2D("car_sale_chart"); 
    sale_chart.setTheme(dojox.charting.themes.PlotKit.orange); 
    sale_chart.addPlot("default", {type:"Columns", gap:2}); 
    sale_chart.addAxis("x", {labels:brands}); 
    sale_chart.addAxis("y", {vertical:true, includeZero:true, max:50}); 
    sale_chart.addSeries("Auto Sale", 
      quantities, {stroke:{color:"black"}, fill:"lightblue"}); 
    sale_chart.render(); 
  } 
  else { 
    // Update the sale chart 
    sale_chart.updateSeries("Auto Sale", quantities); 
    sale_chart.render();  
  } 
}

品牌选择器单选组合的创建如清单 5 所示,其创建基础就是在生成上面的销售图时返回的品牌。



清单 5. 创建品牌选择器
// Create the brand picker 
function makeBrandPicker(brands) { 
  var picker = dojo.byId("brand_picker"); 
  var pickerNode = dojo.byId("picker_node"); 
  if (pickerNode == undefined) { 
    pickerNode = document.createElement("div"); 
    dojo.attr(pickerNode, "id", "picker_node"); 
    for (var i=0;i<brands.length;i++) { 
      var option; 
      // Different code for IE and FF respectively 
      if (dojo.isIE) { 
        option = document.createElement("<input name='auto_brand'>"); 
      } 
      else { 
        option = document.createElement("input"); 
        dojo.attr(option, "name", "auto_brand"); 
      } 
      // Create the radio option 
      dojo.attr(option, "type", "radio"); 
      dojo.attr(option, "id", brands[i].text); 
      dojo.attr(option, "value", brands[i].text); 
      dojo.attr(option, "dojoType", "dijit.form.RadioButton"); 
      // connect onclick to the picker handler 
      dojo.connect(option, "onclick", brandPickerHandler);   
      pickerNode.appendChild(option); 
      // Create the label 
      var lbl = document.createElement("label"); 
      dojo.attr(lbl, "for", brands[i].text); 
      var txt = document.createTextNode(brands[i].text); 
      lbl.appendChild(txt); 
      pickerNode.appendChild(lbl) 
      var nl = document.createElement("br"); 
      pickerNode.appendChild(nl); 
    } 
    picker.appendChild(pickerNode); 
 } 
  else { 
    // if the picker node exists, just refresh the selection 
    brandPickerHandler();   
  } 
}

在 makeBrandPicker 函数中只涉及普通的 HTML DOM (Document Object Model) 操作。唯一需要注意的是,是否使用 dojo.connect 将 brandPickerHander 注册为 onclick 处理程序,它将定位用户选择的品牌并触发分布图的创建和更新,如清单 6 所示。



清单 6. 处理品牌选择事件
// Brand picker event handler 
function brandPickerHandler() { 
  // Find out the selected brand 
  var pickerNode = dojo.byId("picker_node"); 
  var children = pickerNode.childNodes; 
  var selected_brand; 
  for (var i=0;i<children.length;i++) { 
    if (children[i].checked != undefined && children[i].checked == true) { 
      selected_brand = children[i].value; 
      // Retrieve the updated distribution data and refresh the distribution 
      // chart based on the selected brand 
      getDistributionData(selected_brand); 
      break; 
    } 
  } 
}

分布数据的加载和分布图的创建类似于销售数据和销售图的创建,唯一不同的是使用了 Pie 类型的图,如清单 7 所示。

清单 7. 加载分布数据并创建分布图
// Retrieve the distribution data via JSON RPC invocation and 
// make the distribution chart. 
function getDistributionData(brand) { 
  // Call the json service to retrieve the distribution data 
  var json_svc = new dojo.rpc.JsonService( 
    "/DynamicCharts/RPCAdapter/jsonrpc/ChartDataService/getDistributionByArea"); 
  json_svc.getDistributionByArea(brand).addCallback(makeDistritubtionChart); 
} 
 
// Create the distribution chart 
function makeDistritubtionChart(dist_data) { 
  // Parse the distribution data 
  var dist_percentage = new Array(); 
  for (var i=0;i<dist_data.length;i++) { 
    var data = dist_data[i] 
    dist_percentage[i] = { 
    y:data.percent,text:data.region+"("+Math.round(data.percent)+"%)", 
    color:data.color}; 
  } 
 
  if (dist_chart == undefined) { 
    // Make the distribution chart for the first request of a given user 
    dist_chart = new dojox.charting.Chart2D("distribution_by_area"); 
    dist_chart.setTheme(dojox.charting.themes.PlotKit.orange); 
    dist_chart.addPlot("default", 
      {type:"Pie",font:"normal normal bold 6pt sans-serif",fontColor:"white"}); 
    dist_chart.addSeries("Area Distribution", dist_percentage); 
    dist_chart.render(); 
  } 
  else { 
    // Update the distribution chart 
    dist_chart.updateSeries("Area Distribution", dist_percentage); 
    dist_chart.render(); 
  } 
}

JavaScript 实现的最后一部分是定义将在页面加载完成后立即调用的 init 函数。这个函数负责全局初始化,比如初始化 Web 消息传递 servlet,注册 Web 消息传递处理程序,并订阅 /charttopic 主题,以及调用其他函数加载销售数据。(后面的小节将详细介绍 Web 消息传递)。

清单 8. 初始化 Web 消息传递处理程序
// Web Messaging handler. 
function webmsgHandler(msg) { 
  if (msg.data == "UPD") { 
    // Get the updated sale data from the backend 
    getSaleData(); 
  } 
} 
 
// Perform initialization when loading the page. 
function init() { 
  // Use the "tundra" style 
  dojo.addClass(dojo.body(), "tundra"); 
 
  // Get the sale data from the backend 
  getSaleData(); 
 
  // Initalize the web messaging client 
  dojox.cometd.init("webmsgServlet"); 
  dojox.cometd.subscribe("/charttopic", window, "webmsgHandler"); 
}

通过 RPC 适配器将 ChartService 公开到 JavaScript 客户机

RPC 适配器(Web 远程)为 JavaScript 或客户端代码提供了通过 JSON RPC 调用直接调用服务器端逻辑的能力。这意味着 POJO 方法可以轻松地被 Ajax 应用程序调用,而不需要针对轻量级客户机重新构建现有实现。

清单 9 展示了 Java bean ChartData,它将提供通过 RPC 适配器公开的图表数据服务。在这个 Java 类中有两种方法可用:

getCarSale() 用于检索所有汽车品牌的销售数据。

getDistributionByArea(...) 用于通过图表数据访问组件 ChartDataAccessor,检索所选品牌的分布数据。


清单 9. 实现图表服务
public class ChartData{ 
 
  private static final Logger logger = Logger.getLogger("dataservice.ChartData"); 
 
  /** 
   * Gets the sale data of all auto brands 
   * @return sale data for all brands 
   */ 
  public CarSale[] getCarSale() { 
    // Retrieve the sale data with the chart DAO. 
    logger.log(Level.INFO, "Retrieving car sale data for all brands."); 
    return ChartDataAccessor.getInstance().loadSaleData(); 
} 
 
  /** 
 * Gets the distribution data by area for a given auto brand. 
 * @param brand 
 * @return distribution data for the given brand 
*/ 
  public AreaDistribution[] getDistributionByArea(String brand) { 
    // Retrieve the distribution data with the chart DAO. 
    logger.log(Level.INFO, "Retrieving distribution data for " + brand + "."); 
    return ChartDataAccessor.getInstance().loadDistributionDataByBrand(brand); 
  } 
}

要将这些方法发布到 Ajax 客户机,您需要在项目的 WebContent/Web-INF 文件夹中创建 RpcAdapterConfig.xml 配置文件:

如果使用的是 Rational Application Developer V7.5,那么导航到 Project => Services => Expose RPC Adapter Service,并且您不需要手动创建配置文件。指定 <default-format /> to JSON,因为您希望返回的 JSON 格式的数据由 JavaScript 直接、迅速地处理。

分别将服务名和实现设置为 ChartDataService 和 dataservice.ChartData,然后在 <methods /> 中公开 getCarSale() 和 getDistributionByArea() 这两个方法。

除了在 RpcAdapterConfig.xml 中定义已公开的方法外,还有另一种公开 RPC 适配器服务的方法,那就是让 ChartData 类实现 com.ibm.webphere.rpcadapter.SelfBeanInfo 接口,然后在 getBeanDescriptorInfo() 方法中提供已公开方法的信息。



清单 10. 在 RpcAdapterConfig.xml 中发布图表服务
<?xml version="1.0" encoding="UTF-8"?> 
<rpcAdapter 
 xmlns="http://www.ibm.com/xmlns/prod/websphere/featurepack/v6.1/RpcAdapterConfig" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 
 <default-format>json</default-format> 
   <services> 
     <pojo> 
       <name>ChartDataService</name> 
       <implementation>dataservice.ChartData</implementation> 
       <description>The facade for the chart data service.</description> 
       <methods> 
         <method> 
         <name>getCarSale</name> 
           <description>Gets all the car sale data.</description> 
           <http-method>GET</http-method> 
         </method> 
         <method> 
           <name>getDistributionByArea</name> 
           <description>Gets the distribution data.</description> 
           <http-method>GET</http-method> 
           <parameters> 
             <parameter> 
               <name>brand</name> 
               <description>a specific brand</description> 
             </parameter> 
           </parameters> 
         </method> 
       </methods> 
     </pojo> 
   </services> 
</rpcAdapter>

发布图表数据服务的最后一步是对 web.xml 文件进行配置,这样 com.ibm.websphere.rpcadapter.RPCAdapter servlet 就被公开到以下 Web 地址中: http://<host>:<port>/DynamicCharts/RPCAdapter/*

为此,将清单 11 中的 servlet 配置添加到 web.xml 文件中。



清单 11. 在 web.xml 中配置 RPC 适配器 Servlet
<servlet> 
  <display-name>RPCAdapter</display-name> 
  <servlet-name>RPCAdapter</servlet-name> 
  <servlet-class>com.ibm.websphere.rpcadapter.RPCAdapter</servlet-class> 
</servlet> 
<servlet-mapping> 
  <servlet-name>RPCAdapter</servlet-name> 
  <url-pattern>/RPCAdapter</url-pattern> 
</servlet-mapping> 
<servlet-mapping> 
  <servlet-name>RPCAdapter</servlet-name> 
  <url-pattern>/RPCAdapter/*</url-pattern> 
</servlet-mapping>

您现在可以通过 RPC 适配器测试图表服务。在您的 Web 浏览器中打开这些 URL 并下载结果: http://<host>:<port>/DynamicCharts/RPCAdapter/httprpc/ChartDataService/getCarSale 
http://<host>:<port>/DynamicCharts/RPCAdapter/httprpc/ChartDataService/ 
    getDistributionByArea?brand=BMW

使用 Web 消息传递将数据更新发布到 Web 浏览器

Web 消息传递服务将一个浏览器连接到 WebSphere Application Server SIBus,用于通过发布/订阅实现推送服务器端事件,从而方便您使用实时数据更新构建 Web 应用程序,比如股票报价、拍卖竞标和汽车新闻更新。

客户机/服务器通信是通过称为 Bayeux 协议的基于 HTTP 的消息路由协议实现的。Cometd 是一个 Dojo Foundation 项目,用于提供使用 JavaScript 和其他语言的 Bayeux 协议的实现。清单 8 中的代码使用 Dojo Cometd 初始化 Web 消息传递 servlet,提交 /charttopic 主题,并在客户端注册 Web 消息处理程序。要在服务器端启用 Web 消息传递服务,几乎使用相同的代码,唯一的区别在于对 Web 消息传递和 SIBus 的配置(下一小节将介绍部署指导,解释了如何设置 webmsgenabled 参数并在应用服务器上创建 chartbus SIBus)。

DynamicCharts 项目的 WebContent/WEB-INF 中还有一个 Web 消息传递配置文件 webmsg.json(清单 12)。在这个文件中,busName 被设置为 chartbus。考虑到本例的意图,clientCanPublish 被设置为 false,因为在这里使用服务器端发布已经足够满足需求。


清单 12. 在 webmsg.json 中配置 Web 消息传递服务
{ 
  "WebMsgServlet": 
  {  
    "busName": "chartbus", 
    "destination": "Default.Topic.Space", 
    "clientCanPublish": false, 
    "longPollTimeout": 30 
  } 
}

让我们进一步了解图表数据更新器的实现,以及如何使用 JMS 主题连接工厂将服务器事件发布到客户机。

ChartDataUpdater 类实现 TimerListener 接口 CommonJ Timer。当定时器超时后,ChartDataUpdater 对象的 timerExpired 方法将由其他解决方案或服务运行,以模拟图表数据更新,随后将一条消息发布到 Web 客户机。为了帮助实现到 Web 消息传递客户机的发布,Web 消息传递应用程序实用库中提供了一个发布 API。清单 13 演示了这个发布 API 的使用;即通过 BayeuxJmsTextMsg 中封装的 Bayeux 通道 /charttopic 发布消息 UPD。


清单 13. 为图表数据更新器实现 timerExpired 接口
/** 
* Perform the scheduled task at the given interval, implementing 
* the interface of TimerListener. 
*/ 
public void timerExpired(Timer timer) { 
  logger.log(Level.INFO, "Entering timer listener."); 
  try { 
    // Update the data 
    logger.log(Level.INFO, "Updating chart data."); 
    ChartDataAccessor cda = ChartDataAccessor.getInstance(); 
    cda.storeSaleData(simulateSaleData()); 
    cda.storeDistributionData(simulateDistributionData()); 
    // Notify the clients of the chart data updates 
    logger.log(Level.INFO, "Notifying Web browser clients of data updates"); 
    publisher.publish(new BayeuxJmsTextMsg("/charttopic", "UPD")); 
  } catch (PublisherException e) { 
    e.printStackTrace(); 
    logger.log(Level.SEVERE, e.getMessage()); 
  } 
logger.log(Level.INFO, "Exiting timer listener."); 
}

图表数据更新器的启动、调度和停止是通过调用 CommonJ Timer 的 TimerManager 实现的(清单 14)。


清单 14. 实现图表数据更新器的启动/停止方法
/** 
* Starts the chart data updater. 
* 
*/ 
public void startUpdater() { 
  try { 
    if (!timerRunning) { 
      InitialContext ctx = new InitialContext(); 
      tmgr = (TimerManager)ctx.lookup("java:comp/env/tm/default");  
      tmgr.schedule(this, 5, UPDATE_INTERVAL); 
      timerRunning = true; 
      logger.log(Level.INFO, "The chart data updater is started."); 
    } 
  } catch (IllegalArgumentException e) { 
    e.printStackTrace(); 
    logger.log(Level.SEVERE, e.getMessage()); 
  } catch (IllegalStateException e) { 
    e.printStackTrace(); 
    logger.log(Level.SEVERE, e.getMessage()); 
  } catch (NamingException e) { 
    e.printStackTrace(); 
    logger.log(Level.SEVERE, e.getMessage()); 
  }  
} 
 
/** 
 * Stops the chart data updater. 
 * 
 */ 
public void stopUpdater() { 
  if (timerRunning) { 
    if (tmgr != null) { 
      tmgr.stop(); 
      logger.log(Level.INFO, "The chart data updater is stopped."); 
    } 
  } 
}

此外,您需要获得 Publisher 实例,使用发布程序(publisher)初始化 ChartDataUpdater,并在启动 servlet 的 init 方法中启动 ChartDataUpdater。并且,当 servlet 上下文被破坏后,您需要在 servlet 上下文侦听器中关闭 ChartDataUpdater(清单 15)。


清单 15. 实现图表数据更新器的生命周期管理
/* (non-Javadoc) 
 * @see javax.servlet.GenericServlet#init() 
 */ 
public void init() throws ServletException { 
  // Call the init() of the super class 
  super.init(); 
  // Get and initialize the instance of ChartDataUpdater (pass in the publisher) 
  ServletContext servletContext = getServletConfig().getServletContext(); 
Publisher publisher = (Publisher) servletContext.getAttribute( 
    JmsPublisherServlet.PUBLISHER_SERVLET_CONTEXT_KEY); 
  ChartDataUpdater cdu = new ChartDataUpdater(publisher); 
  cdu.startUpdater(); 
  // Keep the chart data updater, and clean it up when the context is destroyed 
  servletContext.setAttribute(ChartDataUpdater.UPDATER_KEY, cdu); 
}  
 
 
/* (non-Java-doc) 
 * @see javax.servlet.ServletContextListener#contextDestroyed(ServletContextEvent arg0) 
 */ 
public void contextDestroyed(ServletContextEvent arg0) { 
  // Stop the timer when context is destroyed 
ChartDataUpdater cdu = (ChartDataUpdater) arg0.getServletContext() 
  .getAttribute(ChartDataUpdater.UPDATER_KEY); 
  if (cdu != null) { 
    cdu.stopUpdater(); 
  } 
}

最后,为 JMS 发布程序和 Web 消息传递准备 servlet 配置,并为 CommonJ Timer 和 JMS Topic Connection Factory 提供资源引用(清单 16)。


清单 16. 在 web.xml 中配置 Web 消息传递
<servlet> 
  <description></description> 
  <display-name>Publisher</display-name> 
  <servlet-name>Publisher</servlet-name> 
<servlet-class> 
    com.ibm.websphere.webmsg.publisher.jndijms.JmsPublisherServlet 
  </servlet-class> 
  <init-param> 
    <description></description> 
    <param-name>CONNECTION_FACTORY_JNDI_NAME</param-name> 
    <param-value>java:comp/env/jms/ChartPublish</param-value> 
  </init-param> 
  <load-on-startup>1</load-on-startup> 
</servlet> 
<servlet> 
  <description/> 
  <display-name>WebMsgServlet</display-name> 
  <servlet-name>WebMsgServlet</servlet-name> 
  <servlet-class>com.ibm.websphere.webmsg.servlet.WebMsgServlet</servlet-class> 
</servlet> 
<servlet-mapping> 
  <servlet-name>WebMsgServlet</servlet-name> 
  <url-pattern>/webmsgServlet</url-pattern> 
</servlet-mapping> 
<resource-ref id="ResourceRef_1224495798546"> 
  <description></description> 
  <res-ref-name>tm/default</res-ref-name> 
  <res-type>commonj.timers.TimerManager</res-type> 
  <res-auth>Container</res-auth> 
  <res-sharing-scope>Unshareable</res-sharing-scope> 
</resource-ref> 
<resource-ref id="ResourceRef_1224558884500"> 
  <description></description> 
  <res-ref-name>jms/ChartPublish</res-ref-name> 
  <res-type>javax.jms.TopicConnectionFactory</res-type> 
  <res-auth>Container</res-auth> 
  <res-sharing-scope>Shareable</res-sharing-scope> 
</resource-ref>

您已经完成了 DynamicCharts 样例的开发。下一节将解释如何导出 WAR 文件,以及如何部署和运行示例。

下载和部署示例

本文提供可 下载 DynamicCharts WAR 文件包含有源代码。这些文件已经针对 Internet Explorer 和 Firefox 以及 WebSphere Application Server V6.1.0.13 和 PK56881 进行了检验。在部署之前,您必须确保导入所有依赖 JAR 文件和 Ajax 运行时。

要安装 DynamicCharts 示例:

确保 WebSphere Application Server Feature Pack for Web 2.0 已安装并正常运行。

启用 Web 消息传递服务:

登录到 WebSphere Application Server 管理控制台并导航到 Servers => Application servers。选择将要部署 DynamicChartsEAR.ear 的服务器。

展开 Web Container Settings,选择 Web container transport chains,然后选择 WCInBoundDefault 传输链。

选择 Web container inbound channel,然后选择 Customer Properties。

单击 New 并为名称属性输入 webmsgenabled,值为 true。

单击 Apply,然后单击 Save,保存存储库信息。

重启应用服务器。

创建和配置 chartbus SIBus:

登录到管理控制台并导航到 Service integration => Buses。

单击 New 并输入 chartbus 作为名称,并接受所有其他默认配置。

单击 Next,然后单击 Finish。

在 chartbus detail 页面的 Topology 下单击 Bus members。

单击 Add 然后选择将要在其上安装 DynamicChartsEAR.ear 的服务器。单击 Next。

接受默认值并单击 Next,然后再次单击 Next,最后单击 Finish。

将更改保存到存储库。

重启应用服务器。

为 DynamicCharts 创建主题连接工厂:

登录到管理控制台并导航到 Resources => JMS => Topic connection factories。

选择服务器级别范围。

单击 New。选择 Default messaging provider 并单击 OK。

输入 DynamicCharts 作为名称,输入 jms/ChartPublish 作为 JNDI 名,以及输入 chartbus 作为 Bus 名,然后保留其他所有默认值。

单击 Apply,然后单击 Save 保存存储库信息。

安装 DynamicChartsEAR.ear:

登录到管理控制台并导航到 Applications => Install New Application。

浏览文件系统并选择 DynamicChartsEAR.ear,然后单击 Next。

接受默认值并单击 Next,然后再次单击 Next,最后单击 Finish。

单击 Save,保存主配置。

启动 DynamicChartEAR。

启动这个 URL: http://<host>:<port>/DynamicCharts/dynamic-charts.html。

结束语

WebSphere Application Server Feature Pack for Web 2.0 为构建基于 Ajax 的应用程序提供了完整的解决方案。本文解释了如何使用 Dojo Toolkit 创建动态图表,如何通过 RPC 适配器重用现有服务,以及如何集成其他应用程序,并使用 Web 消息传递和使用 SIBus 将数据更改发布到客户机。本文给出的考虑事项和技巧将帮助您更好地理解 Web 2.0 功能部件包,使您能够快速、成功地构建基于 Ajax 的应用程序,或增强它们。

本文示例源代码或素材下载

Tags:使用 WebSphere Application

编辑录入:爽爽 [复制链接] [打 印]
[]
  • 好
  • 好的评价 如果觉得好,就请您
      0%(0)
  • 差
  • 差的评价 如果觉得差,就请您
      0%(0)
更多精彩
    赞助商链接

    热点阅读
      焦点图片
        最新推荐
          精彩阅读