Adobe Flex 大师之路第19章 访问远程服务_Adobe Flex 大师之路第19章 访问远程服务试读-查字典图书网
查字典图书网
当前位置: 查字典 > 图书网 > web > Adobe Flex 大师之路 > 第19章 访问远程服务

Adobe Flex 大师之路——第19章 访问远程服务

19.1  远程服务介绍 19.2  访问远程Java对象 19.3  远程服务中的数据序列化 19.4  示例:购物车应用   远程服务,或者称为远程对象服务,是Flex客户端应用可以访问的第三种RPC服务。其他两种分别是我们上一章介绍过的HTTP服务和Web服务。访问远程对象服务是指Flex应用客户端使用RemoteObject组件,通过连接LiveCycle Data Service定义的远程对象服务端点,向远程Java对象发出请求,并获取响应的过程。事实上,远程对象可以是Java类,也可以是ColdFusion组件、PHP或.NET对象。 本章将主要介绍Flex应用客户端如何访问远程Java对象提供的服务。 19.1  远程服务介绍 远程对象服务是LiveCycle Data Service中最常用的一项服务。使用远程对象服务,开发者可以编写Flex客户端应用异步调用远程Java对象的公共方法。这里所访问的公共方法,我们也简称为远程方法。在客户端应用中,代表远程对象的RemoteObject组件连接到LiveCycle Data Service定义的远程对象服务目标,在LiveCyle Data Service服务器端,配置服务目标指向部署在服务器Web应用中的Java对象。 客户端Flex应用和远程Java对象以AMF格式传输数据。AMF是为在HTTP或HTTPS上进行数据传输而设计的二进制数据格式。 使用LiveCycle Data Service,不仅能够集中管理远程Java对象,也可以为访问远程对象提供多种传输通道,并利用日志功能监控远程服务的传输。 图19-1描述了客户端RemoteObject对象透过LiveCycle Data Service提供的远程服务,与远程Java对象进行交互的过程。 图19-1  访问远程Java对象 19.1.1  RemoteObject组件 使用ActionScript类mx.rpc.remoting.RemoteObject,开发者可以在Flex应用中访问使用 Action Message Format(AMF)编码的Java对象的方法。RemobeObject组件的目标属性(destination)指向LiveCycle Data Service远程服务定义的目标,而该目标声明了最终提供服务的Java类。 开发者既可以使用ActionScript代码,也可以利用MXML标签来实例化RemoteObject组件,为其设置属性,调用其方法访问远程服务。 表19-1中列出了RemoteObject组件的常用属性。 表19-1  mx.rpc.remoting.RemoteObject常用属性(引自ActionScript3.0 Language Reference) 属性描述idRemoteObject实例名destination远程服务的目标。该值应与 remote-config.xml 文件中的目标条目匹配 表19-2列出了RemoteObject组件的常用事件。 表19-2  mx.rpc.remoting.RemoteObject常用事件(引自ActionScript3.0 Language Reference) 事件描述fault当服务调用失败并且操作自身不处理时,将调度 fault 事件result当服务调用成功返回并且操作自身不处理时,将调度 result 事件 19.2  访问远程Java对象 为了实现对远程Java对象的访问,在服务器端,需要完成如下几个步骤: 1.     创建LiveCycle Data Service Web应用; 2.     实现POJO Java类; 3.     在LiveCycle Data Service Web应用中部署POJO Java类; 4.     配置remote-config.xml文件,声明desination节点,指向POJO Java类。 在客户端应用中,需要实现: 1.     实例化RemoteObject对象,并设定destination等属性; 2.     调用远程Java对象的方法,并传递参数; 3.     注册RemoteObject对象的result和fault事件侦听器,实现侦听器方法以处理返回响应或者调用异常。 我们已经在第17章的17.5.1节“创建服务器端LiveCycle Data Service应用”(见第416页)中介绍过如何创建LiveCycle Data Service Web应用。因此,我们这里将从实现POJO Java类开始逐步实现访问远程Java对象。 19.2.1  服务器端:实现POJO Java类 使用POJO类作为远程Java对象 我们当然不会在一本Flex书籍中讲解如何编写POJO Java类。我们须要在这里解释一下可以作为远程Java对象的Java类。 只有所谓的POJO类能够作为远程Java对象。POJO(Plain Old Java Object)简称为简单Java对象,通常包含了一系列get和set方法。通常所说的POJO Java类就是纯的Java对象,不实现任何Java对象模型、协议及框架,例如不能实现EJB标准。严格地说,POJO类不能继承其他父类,不能实现接口。LiveCycle Data Service要求,作为LiveCycle Data Service远程服务的远程Java对象,其构造器不能接受任何参数,也就是说必须为零参数构造器。这样,LiveCycle Data Service才能够构造POJO类实例。 然而,POJO类仍然可以作为后端业务逻辑服务的窗口,例如使用POJO类访问JNDI,获取EJB服务等,以便进一步集成后端业务逻辑。 POJO Java类的公共方法提供了远程服务。需要注意的是,LiveCycle Data Service保留了一些方法名,因此POJO Java类不能使用这些方法名,我们称这些方法为“保留方法”。保留方法是RemoteObject类或其父类定义的方法。 保留方法包括: ・       addHeader(); ・       addProperty(); ・       clearUsernamePassword(); ・       deleteHeader(); ・       hasOwnProperty(); ・       isPropertyEnumerable(); ・       isPrototypeOf(); ・       registerClass(); ・       setUsernamePassword(); ・       toLocaleString(); ・       toString(); ・       unwatch(); ・       valueOf(); ・       watch()。 此外,作为远程对象的POJO Java类也不能使用“_”作为远程服务方法名的起始字母。 19.2.2  服务器端:部署POJO Java类 在我们使用HTTPService组件和WebService组件,通过HTTPProxyService服务访问远程HTTP服务和Web服务时,远程服务并不一定要部署在LiveCycle Data Service服务器上。 而调用远程Java对象,则与这种情况大不相同。提供远程服务的Java类必须与Flex应用部署在同一LiveCycle Data Service的Web应用中。 部署POJO Java类的唯一目的就是当客户端RemoteObject组件向LiveCycle Data Service发出访问请求时,LiveCylce Data Service能够找到对应的Java类。 因此,我们须要把POJO Java类部署到LiveCycle Data Service的类路径中。我们可以直接将POJO Java类文件(.class文件)按照类包结构部署到Web应用的WEB-INFclasses目录下,或者将POJO Java类打包为Jar包(.jar文件),部署在WEB-INFlib目录中。 19.2.3  服务器端:配置remote-config.xml 原始remote-config.xml文件 lcds-root/webapps/ lcds目录中包含了空的remote-config.xml文件。我们先来看看原始的remote-config.xml文件。 请参考图19-2,该图展示了原始的remote-config.xml文件,并对主要节点做了注释。 图19-2  尚未配置目标的remote-config.xml 配置远程服务目标 在标签<service></service>内,添加<destination></destination>标签来声明新的远程对象目标。 下面的代码显示了包含一个destination定义的remote-config.xml文件: <?xml version="1.0" encoding="UTF-8"?> <service id="remoting-service"     class="flex.messaging.services.RemotingService">     <adapters>         <adapter-definition id="java-object"             class="flex.messaging.services.remoting.adapters.JavaAdapter"             default="true"/>     </adapters>     <default-channels>         <channel ref="my-amf"/>     </default-channels>        <destination id="shoppingCart">         <properties>             <source>com.mark.pojo.shoppingCart</source>             <scope>session</scope>         </properties>     </destination> </service> destination节点的source属性说明了远程对象服务的目标,也就是远程Java对象。在设定source属性时,必须使用全路径java类名称来指向部署在LiveCycle Data Service类路径中的Java类。 desitnation节点的scrop属性的可选值为application、session和request,指定了远程对象实例作用域。 如果作用域为request,那么服务器会为每一次请求都创建一个远程Java对象实例,request是默认的作用域。 如果作用域是session,服务器则为每一个会话创建一个共享的远程Java对象实例。通常来说,连接服务器的每一个浏览器实例构成了一个会话。例如,如果我们启动Firefox来访问连接LiveCycle Data Service的Flex应用,启动的Firefox就是一个浏览器实例。在该Firefox浏览器中,如果打开其他标签页也访问同一Flex应用,那么这些应用就处于同一会话中,共享同一远程对象实例。 如果作用域是application,那么服务器则为每一个Flex应用创建一个远程的Java对象实例。 限制远程Java对象可访问方法 有些时候,远程Java对象定义了许多公共方法,但是我们也许并不希望这些方法都开放给Flex应用。在这种情况下,可以通过include-methods属性定义允许访问的方法列表,如果Flex应用试图访问该列表外的方法就会导致异常,在客户端会调用fault事件。 include-methods属性位于destination节点的<properties></properties>内。下面的设定只允许Flex客户端访问shoppingCart类的purchaseItem和deleteItem方法。     <destination id="shoppingCart">         <properties>             <source>com.mark.pojo.shoppingCart</source>             <scope>session</scope>             <include-methods>                 <method name="purchaseItem"/>                 <method name="deleteItem"/>             </include-methods>         </properties>     </destination> 如果你只想限制对某几个公共方法的远程访问,使用<exclude-methods></exclude- methods>属性会更加方便。下面的配置设定了Flex客户端可以访问shoppingCart类中除了purchaseItem方法之外的其他公共方法。 <destination id="shoppingCart">         <properties>             <source>com.mark.pojo.shoppingCart</source>             <scope>session</scope>             <exclude-methods>                 <method name="purchaseItem"/>             </exclude-methods>         </properties>     </destination> 19.2.4  客户端:实例化RemoteObject组件对象 创建RemoteObject组件对象的MXML标签语法如下: <mx:RemoteObject id=”remoteObj” destination=”shoppingCart”/> id属性指定了RemoteObject组件实例名称,在接下来调用该服务对象及处理远程服务响应时使用。destination属性指定了要连接的远程服务的目标,该值必须与服务器端remote-config.xml文件中配置的目标id属性相匹配。 我们也可以使用ActionScript代码来创建RemoteObject对象: var remoteObj:RemoteObject = new RemoteObject(); remoteObj.destination = "shoppingCart"; 19.2.5  客户端:调用远程Java对象方法 使用RemoteObject组件最终访问的是远程Java对象公开的公共方法。调用远程Java对象方法的最简单方式即直接调用方法名: remoteObj.methodName(parm1,parm2,…); 在MXML标签中,我们可以在用户事件中直接使用这种方式调用远程服务。例如,如下代码实现了当用户点击“购物”按钮时,调用remoteObj.purchase(‘TV’)。(假定我们配置的远程Java对象开放了公共方法purchase(String product))。 <mx:RemoteObject id="remoteObj" destination="shoppingCart"/> <mx:Button label="购物" click="remoteObj.purchase(‘TV’)"/> 在本章“服务器端:实现并部署POJO Java类”节中,我们介绍过,远程Java对象应该避免使用保留方法名。然而,如果由于不可避免的原因你不得不这样做,例如你的远程Java对象开发者是个“disconnect”方法名的偏执狂,那么使用RemoteObject类的getOperation方法可以缓解你的尴尬。 public function getOperation(name:String):Operation 例如,远程Java对象开放了公共方法toString(),我们可以通过如下代码创建Operation对象来调用该方法: public var remoteObj:RemoteObject = new RemoteObject(); remoteObj.destination = "shoppingCart"; //创建代表toString方法的操作对象 public var oper:Operation = remoteObj.getOperation("toString"); //调用该方法 oper.send(); 19.2.6  客户端:处理响应和异常 RemoteObject使用异步方式调用远程Java对象。在调用操作后,Flex应用并不停止执行其他操作来等待响应,而是允许用户继续交互,执行其他操作。当响应返回时,或者抛出异常时,系统调度result事件或者fault事件来通知应用,以执行事件侦听器方法。也就是说,我们通过侦听RemoteObject组件的result和fault事件处理响应和异常。这种方式与处理远程HTTP服务和Web服务响应的方式如出一辙。 当RemoteObject调用远程方法成功时,系统会调度result事件。开发者可以使用result事件生成的mx.rpc.events.ResultEvent对象访问服务响应数据。 如下所示,我们为RemoteObject对象实例添加result事件和fault事件侦听器,并声明侦听器方法。 …… <mx:Script>     <![CDATA[         import mx.rpc.events.FaultEvent;         import mx.rpc.events.ResultEvent;                 //RemoteObject对象result事件侦听器方法         private function roRespondHandler(event:ResultEvent):void{             //处理远程服务响应            }                 //RemoteObject对象fault事件侦听器方法         private function roErrorHandler(event:FaultEvent):void{             //处理远程服务调用异常            }     ]]> </mx:Script>     <!--实例化远程对象组件-->     <mx:RemoteObject id="remoteObj" destination="shoppingCart"         result="roRespondHandler(event)" fault="roErrorHandler(event)"/>     <!--调用远程对象组件-->     <mx:Button label="购物" click="remoteObj.purchase()"/> …… 我们可以采用ResultEvent.result属性或远程服务组件的Operation.lastResult属性两种方式获取返回数据。 与处理Web服务返回对象类似,对于复杂类型的返回数据,例如数组或自定义对象,默认情况下,Flex会将数组类型数据封装为mx.collection.ArrayCollection,将自定义对象类型封装为mx.utils.ObjectProxy。 如果远程对象调用失败,系统会触发fault事件,开发者可以为该事件定义侦听器方法,使用fault事件生成的mx.rpc.events.FaultEvent对象处理异常。具体方式可以参见第18章的18.1.6节“处理异常”(见第425页)。 19.2.7  使用<mx:operation>处理多个方法 类似Web服务能够提供多个操作,远程Java对象也可以开放多个公共方法,供RemoteObject组件调用。我们可以使用operation类或<mx:method>标签,为一个RemoteObject组件调用多个远程方法。 使用这种方式还能够为处理不同方法的响应提供更多的选择,我们可以为每一个方法定义专有的result和fault事件侦听器,而不是仅仅在RemoteObject组件对象上定义统一的侦听器。 假设我们定义了Java类shoppingCart,开放了公共方法clearCart()、purchaseItem(String itemName,int quantity)、deleteItem(String itemName)等方法。 使用mx.rpc.remoting.Operation类 mx.rpc.remoting.Operation类专门用于为RemoteObject类指定远程对象的操作。Operation 是服务上的单个方法,可以通过在服务上调用同名函数,即RemoteObject.operationName()的方式,或者在服务上访问作为属性的Operation 并调用 send() 方法来调用 Operation,比如RemoteObject.operation.send()。 使用<mx:method>标签 我们也可以使用<mx:method>标签为RemoteObject组件定义多个方法。 <mx:method>标签必须内嵌在<mx:RemoteObject></mx:RemoteObject>标签对内,通过name属性指定匹配的远程Java对象方法名称。开发者可以为每个method标签注册result和fault事件侦听器方法,来分别处理远程Java对象不同方法的响应或调用异常。 在<mx:method></mx:method>标签对中,可以嵌入<mx:arguments>标签指定传递给远程方法的参数。Flex不要求使用与远程Java对象方法参数名称完全相同的名字来定义<mx:arguments>标签。但是,当调用多参的远程方法时,必须保证<mx:arguments>标签指定的参数顺序与远程方法中的参数顺序相匹配。 <!--实例化远程对象组件--> <mx:RemoteObject id="remoteObj" destination="shoppingCart"     fault="roErrorHandler(event)">     <mx:method name="clearCart" result="clearROResultHandler(event);"/>     <mx:method name="deleteItem"             result="deleteROResultHandler(event);">         <mx:arguments>             <item>{comboItem.selectedItem.data}</item>         </mx:arguments>     </mx:method> <!-―参数顺序必须与purchaseItem(String itemName,int quantity)方法相匹配-->     <mx:method name="purchaseItem"             result="purchaseROResultHandler(event);">         <mx:arguments>             <item>{comboItem.selectedItem.data}</item>             <qty>{txtQuantity.text}</qty>         </mx:arguments>     </mx:method> </mx:RemoteObject> 由于我们已经在<mx:method>标签中定义了参数,因此只须要使用无参数的send()方法实现调用。例如: <!--调用远程Java对象purchaseItem方法-->     <mx:Button label="购物" click="remoteObj.purchaseItem.send()"/> 19.3  远程服务中的数据序列化 RemoteObject组件向远程Java对象发送请求,传递参数,当远程Java对象方法返回数据时,LiveCycle Data Service负责充当ActionScript对象和Java对象之间的“翻译官”。 当RemoteObject调用远程方法并传递参数时,LiveCycle Data Service自动把ActionScript数据类型转换为Java数据类型。而当远程方法返回数据时,LiveCycle Data Service则完成相反的工作,将Java数据类型转换为ActionScript数据类型。如图19-3所示。 图19-3  远程服务中的数据序列化示意 19.3.1  数据类型对照 表19-3和表19-4列出了二者之间进行转换的数据类型对照关系。 表19-3  ActionScript数据类型转换为Java数据类型对照表 ActionScript数据类型Java数据类型Array(strict)java.util.ListArray(associative)java.util.MapBooleanjava.lang.BooleanDatejava.util.Dateint/unitjava.lang.IntegerNumberjava.lang.Double无类型对象java.util.Map强类型对象使用[RemoteClass]标签声明的强类型对象XMLorg.w3c.document未定义的,或nullNull 当我们编写Flex应用,为将要调用的远程Java对象方法参数准备数据时,要特别注意处理数据是null的情况。当远程Java对象的方法参数是Boolean或Number这样的基本类型时,传递null会可能会导致意想不到的结果,null值会被LiveCycle Data Service自动解析为基本类型数据的默认值,而不是期望的null值,然后再传递给远程Java对象。也就是说,对于Number类型,例如double、float、long、int、short、byte等基本类型,会将传递来的null解析为0,对于chart类型则解析为u0000,而对于boolean类型则解析为false。 表19-4  Java数据类型转换为ActionScript数据类型对照表 Java数据类型ActionScript数据类型String, Character,char, Character[],char[], BigInteger,BigDecimalStringBooleanBooleanint, short, byteintdouble, long, floatNumberCalendar, DateDateObjectObject 续表 Java数据类型ActionScript数据类型CollectionArrayCollectionObject[]ArrayMap, Dictionary无类型对象org.w3c.DocumentXML 对象nullnull 19.3.2  强类型对象的映射 使用远程对象服务的Flex应用通常会利用Value Object模式。Value Object也被称为Data Transfer Object,代表了在服务器和客户端传输的强类型对象实体。 在服务器端,Value Object通常是通过一系列get和set方法来访问其私有属性的Java实体类。 例如,下面的代码定义了服务器端的Product Java类。 package com.mark.shoppingCart.Entity; public class Product {     private String productName;     private int productID;         public String getProductName(){         return productName;     }         public void setProductName(String val){         productName=val;     }         public int getProductID(){         return productID;     }         public void setProductID(int val){         productID=val;     } } 而在客户端,Value Object定义为标记有[Bindable]和[RemoteClass]元数据标签的ActionScript类。对应于Java类对象的属性可以声明为公共属性,或者定义为使用getter和setter方法访问的私有属性。 [Bindable]确保该ActionScript类的所有公共属性是可以绑定的。 [RemoteClass]声明了客户端ActionScript与服务器端Java类之间的映射关系。下面代码中的ActionScript类valueObject.Product映射了服务器端Java类com.mark.shoppingCart. Entity.Product。此时,Product可以作为强类型对象格式在服务器端和客户端之间进行数据传输。 package valueObject {     [RemoteClass(alias="com.mark.shoppingCart.Entity.Product")]     [Bindable]     public class Product     {         public var productID:int;         public var productName:String;     } } 19.4  示例:购物车应用 我们将创建一个购物车示例程序。Flex客户端实现用户对购物车的操作,而服务器端代码负责管理购物车。 19.4.1  样例架构 我们的购物车示例非常简单。如图19-4中所示。应用包括如下4种基本操作: 图19-4  购物车示例应用UI 1.     在产品列表下拉框中将列出所有可以购买的商品; 2.     用户选择商品,并填写购买数量后,点击“购买”按钮。代表购物车的数据表格中就会添加新的一行,表示购物车中已经放入一个商品。如果该商品已经在购物车中存在,那么就会在已有商品中增加购买数量; 3.     如果用户选择购物车表格中已经购买的产品,然后点击“取消”按钮,那么就会从购物车中全部删除该货品; 4.     如果用户点击“清空”按钮,那么就会清空购物车。 购物车示例不能只是客户端的UI,其核心是提供服务器端业务逻辑的UI接口。因此,用户在客户端的操作都会触发服务器端业务逻辑。在本例中,服务器Java代码会维护代表购物车的对象,用户在客户端的操作都会导致服务器端对购物车进行处理。而在客户端,用户操作并不会直接作用到“购物车”数据表格中,因为我们并不是使用ActionScript代码直接操纵代表购物车的数据表格。“购物车”数据表格应该真实反映服务器端的“购物车”对象。 图19-5说明了我们将要构建的应用中客户端UI与服务器端Java代码之间的关系。 图19-5  购物车示例应用说明 1.     应用初始化时,会调用服务器端storeService.getProductList获取产品列表。 2.     用户点击“购买”按钮后,会调用cartService.purchaseProductItem,该服务器端代码处理购买操作,并返回更新后的“购物车”实例,从而在客户端更新“购物车”数据表格。 3.     “取消”按钮则会调用cartService.deleteProductItem方法,在服务器购物车对象中删除该货品,然后返回并更新购物车。 4.     “清空”按钮会调用服务器端Java代码cartService.clearAll来清空购物车。 19.4.2  实现购物车远程Java代码 我们从构建远程服务开始构建一个简单的调用远程对象的Flex应用。 实现商品类Product.java Product.java是代表商品的Value Object类,位于com.mark.shoppingCart.Entity包中。该类的实例将被填充到“购物车”中。在客户端,我们也将实现对应的ActionScript类:Product.as,见代码19-1。 代码19-1:Product.java 示例 package com.mark.shoppingCart.Entity; public class Product {     private String productName; //商品名称     private int productID;      //商品ID     private int quantityPurchased; //购买数量         public String getProductName(){         return productName;     }         public void setProductName(String val){         productName=val;     }         public int getProductID(){         return productID;     }         public void setProductID(int val){         productID=val;     }         public int getQuantityPurchased(){         return quantityPurchased;     }         public void setQuantityPurchased(int val){         quantityPurchased=val;     }         //增加商品数量     public void addItem(int val){         quantityPurchased+=val;     }     } 实现管理库存商品的Java类 StoreService.java负责维护可订购商品列表,它位于com.mark.pojoService包下。本例中,storeService方法返回Collection类型的可订购商品列表,列表中商品实例为Product类型。 为了让示例简单点,我们使用硬编码方式在列表中加入了TV、DC和DV三种产品,见代码19-2。 代码19-2:StoreService.java 示例 package com.mark.pojoService;   import com.mark.shoppingCart.Entity.Product; import java.util.ArrayList; import java.util.Collection;;   public class StoreService {     public Collection getProductList(){         Collection lstProducts=new ArrayList();                Product tmpProd=new Product();         tmpProd.setProductID(1000);         tmpProd.setProductName("TV");         tmpProd.setQuantityPurchased(0);         lstProducts.add(tmpProd);         tmpProd=new Product();         tmpProd.setProductID(1001);         tmpProd.setProductName("DC");         tmpProd.setQuantityPurchased(0);         lstProducts.add(tmpProd);         tmpProd=new Product();         tmpProd.setProductID(1002);         tmpProd.setProductName("DV");         tmpProd.setQuantityPurchased(0);         lstProducts.add(tmpProd);         System.out.println(lstProducts.size());         return lstProducts;     } }   当Flex客户端应用启动时,将会调用StoreService类的getProductList方法。LiveCycle Data Service将会在服务器端创建该类的实例,并执行getProductList方法返回商品列表。 实现管理购物车的Java类 CartService.java负责在服务器端管理购物车。服务器端的“购物车”非常简单,ArrayList类型的变量cart就是“购物车”,Product类实例将被填充在该列表中,表示已放置在购物车中的商品。CartService.java实现了三个方法用以操纵代表购物车的cart变量,分别为: ・       处理购买商品业务的方法 public List purchaseProductItem(Product prod,int quantity) ・       处理从购物车中删除已购商品业务的方法 public List deleteProductItem(Product prod) ・       清空购物车的方法 public List clearAll() 具体实现代码见代码19-3。 代码19-3:CartService.java 示例 package com.mark.pojoService; import java.util.List; import java.util.ArrayList;   import com.mark.shoppingCart.Entity.Product;   public class CartService {     //购物车商品列表     private ArrayList cart=new ArrayList();         //购买商品     public List purchaseProductItem(Product prod,int quantity){         boolean isPurchased=false;         Product tmpProd=new Product();         //如已经购买该产品,则增加购买数量         for(int i=0;i<cart.size();i++ ){             tmpProd=(Product)cart.get(i);             if(tmpProd.getProductID()==prod.getProductID()){                 tmpProd.addItem(quantity);                 isPurchased=true;             }         }         //如尚未购买该产品,则在购物车中添加新产品         if(!isPurchased){             prod.setQuantityPurchased(quantity);             cart.add(prod);         }         return cart;     }         //删除商品     public List deleteProductItem(Product prod){         Product tmpProd=new Product();         for(int i=0;i<cart.size();i++ ){             tmpProd=(Product)cart.get(i);             if(tmpProd.getProductID()==prod.getProductID()){                 cart.remove(i);                 break;             }         }         return cart;     }         //清空购物车     public List clearAll(){         cart.clear();         return cart;     }     } 19.4.3  部署服务器端代码 我们把刚刚完成的服务器端代码部署在LiveCycle Data Service服务器上,使用lcds目录作为本例的服务器端应用,如图19-6所示。 图19-6  部署在LiveCycle Data Service上的远程Java类 在lcds-root/webapps/ lcds/WEB-INF下创建classes目录,分别复制编译后的全路径class文件(CartService.class,StoreService.class,Product.class)到lcds-root/webapps/ lcds/WEB-INF/classes中。   19.4.4  配置远程目标 创建使用服务器技术的Flex项目ShoppintCartDemo,并为该项目链接LiveCycle Data Service配置文件。(具体步骤请参见第17章的17.5节“创建使用LiveCylce Data Service服务的Flex应用”(见第416页))。 使用Flex Builder,编辑WEB-INF/flex/remoting-config.xml文件,配置远程服务目标,见代码19-4: 代码19-4:remoting-config.xml文件 <?xml version="1.0" encoding="UTF-8"?> <service id="remoting-service"     class="flex.messaging.services.RemotingService">     <adapters>         <adapter-definition id="java-object" class="flex.messaging.services.         remoting.adapters.JavaAdapter" default="true"/>     </adapters>     <default-channels>         <channel ref="my-amf"/> </default-channels> <!--远程服务目标,购物车服务-->     <destination id="shoppingCart">         <properties>             <source>com.mark.pojoService.CartService</source>             <scope>request</scope>         </properties>     </destination> <!--远程服务目标,库存商品服务-->     <destination id="storeCart">         <properties>             <source>com.mark.pojoService.StoreService</source>             <scope>session</scope>         </properties>     </destination>    </service> 在该文件中,我们配置了两个远程服务目标:shoppingCart和storeCart,前者对应着Java类CartService,后者对应Java类StoreService。 19.4.5  实现客户端Flex应用 商品类Product.as 与服务器端Product.java类对应,我们实现Flex客户端的商品类。该类位于valueObject包内,见代码19-5。 代码19-5:Product.as文件 package valueObject {     [RemoteClass(alias="com.mark.shoppingCart.Entity.Product")]     [Bindable]     public class Product     {         public var productID:int;         public var productName:String;         public var quantityPurchased:int;     } } 在代码中,我们使用[RemoteClass]标识该类与服务器端com.mark.shoppingCart.Entity. Product的相互映射。其中的属性名称与Product.java一致。 主应用ShoppingCart.mxml 主应用ShoppingCart.mxml完整代码见代码19-6,具体代码说明请参见其中注释。 代码19-6:ShoppingCart.mxml文件 <?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"     layout="vertical" backgroundColor="white" creationComplete="initApp()">     <mx:Script>         <![CDATA[             import valueObject.Product;             import mx.controls.Alert;             import mx.collections.ArrayCollection;             import mx.rpc.events.FaultEvent;             import mx.rpc.events.ResultEvent;             //购物车             [Bindable]             private var acCart:ArrayCollection=new ArrayCollection();              [Bindable] //可购买商品列表             private var acStore:ArrayCollection=new ArrayCollection();                                  private function initApp():void{                 roStore.getProductList();             }                         //RemoteObject对象delete方法的result事件侦听器方法             private function deleteROResultHandler(event:ResultEvent):void{                 //处理远程服务响应                    acCart=roCart.deleteProductItem.lastResult;             }                 //RemoteObject对象clear方法的result事件侦听器方法             private function clearROResultHandler(event:ResultEvent):void{                 //处理远程服务响应                    acCart=roCart.clearAll.lastResult;             }                         //RemoteObject对象purchase方法的result事件侦听器方法             private function purchaseROResultHandler(event:ResultEvent):void{                 //处理远程服务响应                    acCart=roCart.purchaseProductItem.lastResult;             }                         private function storeROResultHandler(event:ResultEvent):void{                 //处理远程服务响应                    acStore=event.result as ArrayCollection;             }                        //RemoteObject对象fault事件侦听器方法             private function roErrorHandler(event:FaultEvent):void{                 //处理远程服务调用异常                    mx.controls.Alert.show(event.fault.message);             }         ]]>     </mx:Script>         <!--实例化远程对象组件shoppingCart-->     <mx:RemoteObject id="roCart" destination="shoppingCart"         fault="roErrorHandler(event)">         <mx:method name="clearAll" result="clearROResultHandler(event);"/>         <mx:method name="deleteProductItem" result=         "deleteROResultHandler(event);">             <mx:arguments>                 <item>{dgCart.selectedItem as Product}</item>             </mx:arguments>         </mx:method>         <mx:method name="purchaseProductItem" result= "purchaseROResultHandler(event);">             <mx:arguments>                 <item>{comboItem.selectedItem as Product}</item>                 <qty>{txtQuantity.text}</qty>             </mx:arguments>                    </mx:method>     </mx:RemoteObject> <!--实例化远程对象组件storeCart-->     <mx:RemoteObject id="roStore" destination="storeCart"          fault="roErrorHandler(event)" result="storeROResultHandler(event)"/> <!--应用UI--> <!--工具栏-->     <mx:ApplicationControlBar width="{dgCart.width}">         <mx:ComboBox id="comboItem" dataProvider="{acStore}" labelField= "productName"  />         <mx:Label text="数量"/>         <mx:TextInput id="txtQuantity" width="50"/>                <mx:VRule height="15" />         <mx:Spacer width="100%"/>         <!--调用远程对象组件-->         <mx:Button label="购买" click="roCart.purchaseProductItem.send()"/>         <mx:Button label="取消该商品" click="roCart.deleteProductItem.send()"/>         <mx:Button label="清空购物车" click="roCart.clearAll.send()"/>     </mx:ApplicationControlBar>    <!--购物车数据表格,数据绑定于acCart-->     <mx:DataGrid id="dgCart" dataProvider="{acCart}" width="80%">         <mx:columns>             <mx:DataGridColumn dataField="productID" headerText="产品ID" />             <mx:DataGridColumn dataField="productName" headerText="产品名称"/>             <mx:DataGridColumn dataField="quantityPurchased"headerText="购买数量"/>         </mx:columns>     </mx:DataGrid> </mx:Application> 最后,我们对ShoppingCart.mxml主应用中的关键点做如下说明: 1.    应用中重要的变量 我们定义了ArrayCollection类型的两个变量acCart和acStore。 acCart用于管理客户端的“购物车”,该变量被绑定在数据表格dgCart的属性dataProvider上,作为其数据源。acCart负责存储远程对象roCart调用远程方法后的响应数据。如果你还记得在CartService.java中的ArrayList类型变量cart,那么可以说acCart就是cart在客户端的“化身”。 acStore则管理客户端的“可订购商品列表”,其唯一的目的就是用于填充复合文本框comboItem(acStore作为数据源,绑定在comboItem的dataProvider属性上),供用户在其中选择订购商品。当应用初始化时,roStore远程对象组件会调用远程方法来填充acStore,与acCart类似,acStore可以被视为服务器端StoreService.java中lstProducts的“化身”。 2.    实例化远程对象组件 在本例中,使用MXML创建了两个远程对象组件实例:roCart和roStore。 roCart的destination设为“shoppingCart”。在remoting-config.xml中,id为shoppingCart的远程对象映射为Java类com.mark.pojoService.CartService.roCart使用<mx:method>标签定义了要调用的方法和需要传递的参数。 roStore的destination设为“storeCart”。在remoting-config.xml中,id为storeCart的远程对象映射为Java类com.mark.pojoService.StoreService。在应用初始化完成后(creationComplete事件),会调用事件侦听器方法initApp,该方法中直接通过remoteObj.methodName(parm1,parm2,…)的方式调用了远程对象StoreService类的getProductList方法,即roStore.getProductList()。 19.4.6  运行示例 启动LiveCycle Data Service,在Flex Builder中运行ShoppingCart.mxml,我们可以看到如下应用,如图19-7所示。 图19-7  ShoppingCart应用 在客户端进行操作的同时,我们可以在服务器端控制台上监控远程服务对象的调用情况。图19-8是当用户购买25台DC商品时,调用cartService.purchaseProductItem的情况。 图19-8  服务器端控制台信息 19.4.7  作用域session、request和application选项的最后讨论 在本章“服务器端:配置remote-config.xml”一节中,我们曾解释过,配置destination时可以为每个远程对象指定其作用域,可选值分别为session、request和application。在本章的结尾,我想结合shoppingCart这个例子对此做最后的讨论。 在本例中,远程对象服务目标的配置如下。     <destination id="shoppingCart">         <properties>             <source>com.mark.pojoService.CartService</source>            <scope>request</scope>         </properties>     </destination>     <destination id="storeCart">         <properties>             <source>com.mark.pojoService.StoreService</source>             <scope>session</scope>         </properties>     </destination>    其中作用域分别设置为request和session。这里作用域的选择并不是随意之举,而是有意为之。 与其说scope定义了远程对象的“作用域”,不如说设定了它的“存活期”。我们以设置StoreService的作用域为例。StoreService的目的是提供“可订货商品列表”。回顾StoreService.java的代码,我们在该类的purchaseProductItem方法中硬编码填充了列表lstProducts。设置该远程对象作用域为“request”,意味着每一次客户端调用该方法(本例中即为每一次启动程序或每一次刷新应用),服务器端都会重新为其生成新的实例。如果我们设置其作用域为“session”,则LiveCycle Data Service会为同一个浏览器会话维持同一个StoreService实例。每一次用户刷新浏览器时,客户端都会调用同一个实例。在本例StoreService的实现中,“session”设置将会导致在当前实例的lstProducts中重复填充商品,此时我们将会看到图19-9中所显示的重复累加的商品列表。 如果我们设置作用域为“application”,那么其他用户的访问也会导致该列表被多次重复填充,复合文本框中的重复数据将会不断增加。 由于每一次请求都要创建新的类实例,从服务器端性能的角度来说,设置作用域为“request”不是一个经济的做法。在真实项目中,可用商品列表通常取自于数据库或ERP服务,而且对于所有客户端都应该是同样的,所以完全可以使用“application”设置,以便在服务器端只维持一个实例。 图19-9  session作用域设置下导致重复的商品列表 而在本例中,我们可以修改StoreService类的purchaseProductItem代码,在填充列表前执行清空操作。此时,如果设置作用域为“session”或“application”,也能够避免重复填充商品列表的问题。

展开全文


推荐文章

猜你喜欢

附近的人在看

推荐阅读

拓展阅读

《Adobe Flex 大师之路》其他试读目录

• 第2章 Flex全记录
• 第6章 事件驱动编程
• 第13章 数据基础
• 第19章 访问远程服务 [当前]
  • 大家都在看
  • 小编推荐
  • 猜你喜欢
  •