2011年5月21日 星期六

Flex Remote mapping value Object (Flex 值物件映射)


Mapping Value Object
上次我們在Linux中測試過BlazeDS AMF的remote功能,現在我們來試試看在BlazeDS中的Remote Server回傳的Value Object 來映射 Flex端的Value Object。

ActionScript可使用flash.net.registerClassAlias來做Mapping Value Object:
  • registerClassAlias( "remote端 value object Class path" ,  flash端 value object Class path);
Flex中可使用MetaData tags(後設標籤) 來做Mapping Value Object:
  • [RemoteClass(alias="value object Class path")]
Remote端配置:
Setp1.撰寫Remote端的值物件SongData

//JAVA SongData (值物件)
package com.jt.valueObject;
public class SongData 
{ 
 public SongData()
 {
  super();
 }

 private String _name;
 public String getName() 
 {
  return _name;
 }
 public void setName(String name) 
 {
  _name = name;
 }

 private String _vocalist; 
 public String getVocalist() 
 {
  return _vocalist;
 }
 public void setVocalist(String vocalist) 
 {
  _vocalist = vocalist;
 }

 private String _type; 
 public String getType() 
 {
  return _type;
 }
 public void setType(String type) 
 {
  _type = type;
 }

 private int _length;
 public int getLength() 
 {
  return _length;
 }
 public void setLength(int length) 
 {
  _length = length;
 } 
}


Step2.撰寫Remote Service的類別SongService
//JAVA SongService (服務呼叫API)
package com.jt.remote;
import com.jt.valueObject.SongData;
public class SongService 
{
 public SongService()
 {
  super();
 }
 
 public SongData getSongData()
 {
  SongData data = new SongData();
  data.setName("誰公平");
  data.setVocalist("李威");
  data.setType("Top");
  data.setLength(30000);
  return data;  
 }
}


檔案結構如下圖:










Step3.編譯SongService.java與SongData.java
  • 在你使用Eclipse會幫你編譯這兩隻檔案,你也可以使用命令模式java-d編譯檔案。
  • 命令模式在所在路徑在src時 :
    • javac -d ../classes com/jt/remote/SongService.java
    • javac -d ../classes com/jt/valueObject/SongData.java
Step4.將SongData.class與SongService.class配置到BlazeDS的位置上

  • 將SongService.class放至/opt/tomcat6/webapps/blazedsAMF/WEB-INF/classes/com/jt/remote
  • 將SongData.class放至/opt/tomcat6/webapps/blazedsAMF/WEB-INF/classes/com/jt/valueObject
PS.這是照之前Linux上配置BlazeDS的設定,因此路徑會是這樣,實際請依你的配置路徑,在WEB-INF/classes底下的路徑就是你JAVA的package設定。


Step5.設定BlazeDS的配置檔remoting-config.xml
  • 如果你是跟著我Linux配置BlazeDS文章的話路徑會在opt/tomcat6/webapps/blazedsAMF/WEB-INF/flex/remoting-config.xml
  • sudo gedit opt/tomcat6/webapps/blazedsAMF/WEB-INF/flex/remoting-config.xml
在檔案內容中加入:
<destination id="Remote_GetSong">
  <properties>  
     <source>com.jt.remote.SongService</source>
  </properties>
</destination>

Step6.撰寫Flex程式來測試Mapping
//Flex的值物件SongData ,請參考JAVA中的SongData做比較
package com.jt.valueObject
{ 
 //mapping,alias是指java中valueObject Class的所在路徑。
        [RemoteClass(alias="com.jt.valueObject.SongData")]
 public class SongData
 {
  public function SongData()
  {
   super();
  }
  
  private var _name:String;
  public function get name():String
  {
   return _name;
  }
  public function set name(value:String):void
  {
   if( _name != value)
   {
    _name = value;
   }
  }
  
  private var _vocalist:String;
  public function get vocalist():String
  {
   return _vocalist;
  }
  public function set vocalist(value:String):void
  {
   if( _vocalist != value)
   {
    _vocalist = value;
   }
  }
  
  private var _type:String;
  public function get type():String
  {
   return _type;
  }
  public function set type(value:String):void
  {
   if( _type != value)
   {
    _type = value;
   }
  }
  
  private var _length:int;
  public function get length():int
  {
   return _length;
  }
  public function set length(value:int):void
  {
   if( _length != value)
   {
    _length = value;
   }
  }  
 }
}

//Flex的應用程式
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
      xmlns:s="library://ns.adobe.com/flex/spark" 
      xmlns:mx="library://ns.adobe.com/flex/mx" 
      minWidth="955" minHeight="600"
      applicationComplete="completeHandler(event)"
      >
 <s:layout>
  <s:HorizontalLayout/>
 </s:layout>
 <fx:Script>
  <![CDATA[
   import com.jt.valueObject.SongData;
   
   import flash.net.registerClassAlias;
   
   import mx.events.FlexEvent;
   import mx.messaging.ChannelSet;
   import mx.messaging.channels.AMFChannel;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   import mx.rpc.remoting.Operation;
   import mx.rpc.remoting.RemoteObject;
   
   //Remote Operation
   private var _channelSet:ChannelSet;
   private var _amfChannel:AMFChannel;
   
   private var _remoteObject:RemoteObject;  
   private var _operation:Operation;  
   private var _data:Object;   
   
   protected function completeHandler(event:FlexEvent):void
   { 
    
    //as3方式設定mapping (java值物件路徑 , as3值物件類)
    //registerClassAlias("com.jt.valueObject.SongData", SongData);
    initRemoteData();
    initRemoteOpertion();   
    initBtnListener();  
   }   
   private function initRemoteData():void
   {
    _data  = createRemoteData();    
   }   
   protected function createRemoteData():Object
   {    
    var data:Object = new Object();  
    //blazeds的AMF Server Service服務位址
    data.uri = "http://127.0.0.1:8400/blazedsAMF/messagebroker/amf";            
    data.channelSetID = "my-amf"; 
    //服務程式來源path(由classes之後算起) 
    data.source = "com.jt.remote.SongService"; 
    //操作method名稱 
    data.name = "getSongData";  
    data.destination = "Remote_GetSong"; 
    return data;
   }   
   
   private function initRemoteOpertion():void
   {
    initChannelSet();
    initAMFChannel();
    addChannel();
    initRemoteObject();
    initOperation();
    afterInitOPeration();
   }   
   private function initChannelSet():void
   {    
    _channelSet = new ChannelSet(); 
   }
   private function initAMFChannel():void
   {
    _amfChannel = new AMFChannel(_data.channelSetID , _data.uri); 
   }
   private function addChannel():void
   {
    _channelSet.addChannel(_amfChannel); 
   }
   private function initRemoteObject():void
   {    
    _remoteObject = new RemoteObject();
    _remoteObject.destination = _data.destination;
    _remoteObject.channelSet = _channelSet; 
    _remoteObject.source  = _data.source;    
   }
   
   private function initOperation():void
   {    
    _operation = new Operation();
    _operation.name = _data.name;      
   }
   private function afterInitOPeration():void
   {
    _remoteObject.operations  = {getSongData:_operation};    
    _remoteObject.addEventListener(ResultEvent.RESULT , onResult);
    _remoteObject.addEventListener(FaultEvent.FAULT , onFault);      
   }    
   
   protected function onResult(e:ResultEvent):void
   { 
    var v:SongData; //有這行 ,後設標籤的mapping就會成功, 如果沒有這行 就沒辦法mapping,很怪異。    
    output.text = e.result.toString();
   }
   
   protected function onFault(e:FaultEvent):void
   { 
    output.text = "Remote失敗";
   }   
   
   private function initBtnListener():void
   {
    btn.addEventListener(MouseEvent.MOUSE_DOWN , callGetSongData);    
   }   
   protected function callGetSongData(event:MouseEvent):void
   { 
    _remoteObject.getSongData();    
   }     
  ]]>
 </fx:Script> 
 <s:Panel width="300" height="200" title="ValueTest">
  <s:layout>
   <s:VerticalLayout/>
  </s:layout>  
   <s:TextArea id="output" width="100%" height="100%"/>        
 </s:Panel>
 <s:Button id="btn" label="call getSongData"/> 
</s:Application>


  • 執行應用程式前記得,要把tomcat運作起來
  • 我們在onResult(e:ResultEvent)中下斷點以便觀察e.result
  • 執行應用程式
  • 點擊瀏覽畫面上的Button
我們可以看到,e.result為一個SongData,代表mapping成功









然後我們將onResult(e:ResultEvent)中的var v:SongData;給註解掉,然後再執行一次應用程式,你會發現e.result竟然變成ObjectProxy了,代表mapping失敗。





至於為什麼會這樣,目前還不知道,但若使用flash.net.registerClassAlias來做mapping則沒有這樣的問題。

registerClassAlias的用法:
  • registerClassAlias("com.jt.valueObject.SongData", SongData);
第一個參數是Remote端中,值物件的所在路徑。
第二個參數是Flex端中,值物件的類。

registerClassAlias方式是flashPlayer API因此flash、Flex都可使用。

你可以將應用程式中的registerClassAlias("com.jt.valueObject.SongData", SongData);拿掉註解,然後將SongData的[RemoteClass(alias="com.jt.valueObject.SongData")]註解掉,測試一下。

Code Block 模板



code style