2010년 12월 15일 수요일

IExternalizable 인터페이스를 이용한 AMF 커스텀 직렬화 활용 [출처] IExternalizable 인터페이스를 이용한 AMF 커스텀 직렬화 활용|작성자 네모

LCDS, BlazeDS, ZendAMF등을 이용해 AMF(ActionScript Message Format)으로 데이터를 주고받는 형태는 이미 많은 예제들과 문서들이 있다. AMF는 데이터 통신을 위한 일종의 약속된 규약이고 이 데이터를 주고 받고 하는 과정에서 서로의 언어에 맞게 직렬화(Serialization)하는 것은 각 언어에서 지원해주는 라이브러리를 사용하면 된다. ActionScript 3.0은 기본 API에서 지원해준다. 자바의 경우는 BlazeDS나 LCDS를 사용하면 된다. PHP의 경우에는 ZendAMF를 사용하면 된다. 이들을 이용하면 가령, 자바와 java.lang.Interger는 ActionScript의 int나 uint로... java.lang.Double은 ActionScript의 Number형과 직렬화된다. 이는 일종의 기본적으로 지원되는 직렬화이다. 

다음은 BlazeDS에서 기본적으로 지원되는 직렬화이다.
Serializing between ActionScript and Java

하지만 이런 기본 직렬화과정을 사용하지 않고 직렬화 자체를 커스터마이징(customizing)할 수 있다. 가령 클라이언트(예,Flash 애플리케이션)에서는 아이디, 이름, 속성, 가격의 정보가 중요하지만 서버(예,자바)에서는 재고(inventory)가 중요한 경우가 있다. 이런 경우에는 Flex와 Java쪽의 직렬화하는 클래스를 다음과 같이 디자인 할 수 있겠다.


01.// Product.as
02.packagesamples.externalizable {
03.  
04.importflash.utils.IExternalizable;
05.importflash.utils.IDataInput;
06.importflash.utils.IDataOutput;
07.  
08.[RemoteClass(alias="samples.externalizable.Product")]
09.publicclassProduct implementsIExternalizable {
10.    publicfunctionProduct(name:String=null) {
11.        this.name = name;
12.    }
13.  
14.    publicvarid:int;
15.    publicvarname:String;
16.    publicvarproperties:Object;
17.    publicvarprice:Number;
18.  
19.    publicfunctionreadExternal(input:IDataInput):void{
20.        name = input.readObject() asString;
21.        properties = input.readObject();
22.        price = input.readFloat();
23.    }
24.  
25.    publicfunctionwriteExternal(output:IDataOutput):void{
26.        output.writeObject(name);
27.        output.writeObject(properties);
28.        output.writeFloat(price);
29.    }
30.}
31.}


01.// Product.java
02.packagesamples.externalizable;
03.  
04.importjava.io.Externalizable;
05.importjava.io.IOException;
06.importjava.io.ObjectInput;
07.importjava.io.ObjectOutput;
08.importjava.util.Map;
09.  
10./**
11.* This Externalizable class requires that clients sending and 
12.* receiving instances of this type adhere to the data format
13.* required for serialization.
14.*/
15.publicclassProduct implementsExternalizable {
16.    privateString inventoryId;
17.    publicString name;
18.    publicMap properties;
19.    publicfloatprice;
20.  
21.    publicProduct()
22.    {
23.    }
24.  
25.        /**
26.        * Local identity used to track third party inventory. This property is
27.        * not sent to the client because it is server-specific.
28.        * The identity must start with an 'X'.
29.        */
30.        publicString getInventoryId() {
31.            returninventoryId;
32.        }
33.  
34.        publicvoidsetInventoryId(String inventoryId) {
35.            if(inventoryId != null&& inventoryId.startsWith("X"))
36.            {
37.                this.inventoryId = inventoryId;
38.            }
39.            else
40.            {
41.                thrownewIllegalArgumentException("3rd party product
42.                inventory identities must start with 'X'");
43.            }
44.        }
45.  
46.        /**
47.         * Deserializes the client state of an instance of ThirdPartyProxy
48.         * by reading in String for the name, a Map of properties
49.         * for the description, and 
50.         * a floating point integer (single precision) for the price. 
51.         */
52.        publicvoidreadExternal(ObjectInput in) throwsIOException,
53.            ClassNotFoundException {
54.            // Read in the server properties from the client representation.
55.            name = (String)in.readObject();
56.            properties = (Map)in.readObject();
57.            price = in.readFloat();
58.            setInventoryId(lookupInventoryId(name, price));
59.        }
60.        /**
61.         * Serializes the server state of an instance of ThirdPartyProxy
62.         * by sending a String for the name, a Map of properties
63.         * String for the description, and a floating point
64.         * integer (single precision) for the price. Notice that the inventory 
65.         * identifier is not sent to external clients.
66.         */
67.        publicvoidwriteExternal(ObjectOutput out) throwsIOException {
68.            // Write out the client properties from the server representation
69.            out.writeObject(name);
70.            out.writeObject(properties);
71.            out.writeFloat(price);
72.        }
73.          
74.        privatestaticString lookupInventoryId(String name,floatprice) {
75.            String inventoryId = "X"+ name + Math.rint(price);
76.            returninventoryId;
77.        }
78.}

위 코드는 ActionScript의 flash.utils.IExternalizable 인터페이스와 Java의 java.io.Externalizable 인터페이스를 이용해 기본직렬화를 무시하고 이들 인터페이스에 정의된 readExternal와 writeExternal 메소드를 호출하여 직렬화 자체를 커스터마이징 할 수 있다는 것을 의미한다. Externalizable 인터페이스를 구현한 클래스에 정의된 메소드가 기본 직렬화보다 우선순위가 높다는 것을 기억하면 되겠다.

Flex의 ArrayCollection을 다시 한번 보기 바란다. 이 클래스는 flash.utils.IExternalizable를 구현했다. 
mx.collections.ArrayCollection

꼭 이런 경우만은 아니다. 쓸데없는 데이터의 크기를 줄이기 위한 방법도 해당한다. 예를들어 ActionScript 코드를 보면 다음과 같다. 


01.classExample implementsIExternalizable {
02.    
03.      publicvarone:Boolean;
04.      publicvartwo:Boolean;
05.      publicvarthree:Boolean;
06.      publicvarfour:Boolean;
07.      publicvarfive:Boolean;
08.      publicvarsix:Boolean;
09.      publicvarseven:Boolean;
10.      publicvareight:Boolean;
11.       publicfunctionwriteExternal(output:IDataOutput) {
12.           varflag:int0;
13.           if(one) flag |= 1;
14.          if(two) flag |= 2;
15.          if(three) flag |= 4;
16.          if(four) flag |= 8;
17.          if(five) flag |= 16;
18.          if(six) flag |= 32;
19.          if(seven) flag |= 64;
20.          if(eight) flag |= 128;
21.           output.writeByte(flag);
22.      }
23.       publicfunctionreadExternal(input:IDataInput) {
24.           varflag:int= input.readByte();
25.           one = (flag & 1) != 0;
26.          two = (flag & 2) != 0;
27.          three = (flag & 4) != 0;
28.          four = (flag & 8) != 0;
29.          five = (flag & 16) != 0;
30.          six = (flag & 32) != 0;
31.          seven = (flag & 64) != 0;
32.          eight = (flag & 128) != 0;
33.      }
34. }

데이터 통신을 위해 쓸데없이 Boolean객체를 주고 받을 필요없다. 위 코드처럼 직렬화를 커스터마이징한다면 송수신 데이터 자체의 크기도 줄일 수 있다. 이는 매우 유용하다.


참고글
flash.utils.IExternalizable
커스텀 직렬화의 사용
ActionScript 3.0 데이터 유형 및 직렬화(serialization)
Serializing between ActionScript and Java
AMF 3 스팩
AS3 BitmapData AMF solution using IExternalizable
Flex and PHP: remoting with Zend AMF 

글쓴이 : 지돌스타(http://blog.jidolstar.com/644)

댓글 없음:

댓글 쓰기