BlazeDS Developer Guide

Explicitly mapping ActionScript and Java objects

View comments | RSS feed

For Java objects that BlazeDS does not handle implicitly, values found in public bean properties with get/set methods and public variables are sent to the client as properties on an Object. Private properties, constants, static properties, and read-only properties, and so on, are not serialized. For ActionScript objects, public properties defined with the get/set accessors and public variables are sent to the server.

BlazeDS uses the standard Java class, java.beans.Introspector, to get property descriptors for a Java bean class. It also uses reflection to gather public fields on a class. It uses bean properties in preference to fields. The Java and ActionScript property names should match. Native Flash Player code determines how ActionScript classes are introspected on the client.

In the ActionScript class, you use the [RemoteClass(alias=" ")] metadata tag to create an ActionScript object that maps directly to the Java object. The ActionScript class to which data is converted must be used or referenced in the MXML file for it to be linked into the SWF file and available at run time. A good way to do this is by casting the result object, as the following example shows:

var result:MyClass = MyClass(event.result);

The class itself should use strongly typed references so that its dependencies are also linked.

The following example shows the source code for an ActionScript class that uses the [RemoteClass(alias=" ")] metadata tag:

package samples.contact {
    [Bindable]
    [RemoteClass(alias="samples.contact.Contact")]
    public class Contact {
        public var contactId:int;

        public var firstName:String;

        public var lastName:String;

        public var address:String;

        public var city:String;

        public var state:String;

        public var zip:String;
    }
}

You can use the [RemoteClass] metadata tag without an alias if you do not map to a Java object on the server, but you do send back your object type from the server. Your ActionScript object is serialized to a special Map object when it is sent to the server, but the object returned from the server to the clients is your original ActionScript type.

To restrict a specific property from being sent to the server from an ActionScript class, use the [Transient] metadata tag above the declaration of that property in the ActionScript class.

Converting data from Java to ActionScript

An object returned from a Java method is converted from Java to ActionScript. BlazeDS also handles objects found within objects. BlazeDS implicitly handles the Java data types in the following table.

Java type

ActionScript type (AMF 3)

enum (JDK 1.5)

String

java.lang.String

String

java.lang.Boolean, boolean

Boolean

java.lang.Integer, int

int

If value < 0xF0000000 || value > 0x0FFFFFFF, the value is promoted to Number due to AMF encoding requirements.

java.lang.Short, short

int

If i < 0xF0000000 || i > 0x0FFFFFFF, the value is promoted to Number.

java.lang.Byte, byte[]

int

If i < 0xF0000000 || i > 0x0FFFFFFF, the value is promoted to Number.

java.lang.Byte[]

flash.utils.ByteArray

java.lang.Double, double

Number

java.lang.Long, long

Number

java.lang.Float, float

Number

java.lang.Character, char

String

java.lang.Character[], char[]

String

java. math.BigInteger

String

java.math.BigDecimal

String

java.util.Calendar

Date

Dates are sent in the Coordinated Universal Time (UTC) time zone. Clients and servers must adjust time accordingly for time zones.

java.util.Date

Date

Dates are sent in the UTC time zone. Clients and servers must adjust time accordingly for time zones.

java.util.Collection (for example, java.util.ArrayList)

mx.collections.ArrayCollection

java.lang.Object[]

Array

java.util.Map

Object (untyped). For example, a java.util.Map[] is converted to an Array (of Objects).

java.util.Dictionary

Object (untyped)

org.w3c.dom.Document

XML object

null

null

java.lang.Object (other than previously listed types)

Typed Object

Objects are serialized using Java bean introspection rules and also include public fields. Fields that are static, transient, or nonpublic, as well as bean properties that are nonpublic or static, are excluded.

Note: You can enable legacy XML support for the flash.xml.XMLDocument type on any channel that is defined in the services-config.xml file.

Note: In Flex 1.5, java.util.Map was sent as an associative or ECMA Array. This is no longer a recommended practice. You can enable legacy Map support to associative Arrays, but Adobe recommends against doing this.

Configuring AMF serialization on a channel

You can support legacy AMF type serialization used in earlier versions of Flex and configure other serialization properties in channel definitions in the services-config.xml file.

The following table describes the properties that you can set in the <serialization> element of a channel definition:

Property

Description

enable-small-messages

Default value is true. If enabled, messages are sent using an alternative smaller form if one is available and the endpoint supports it.

ignore-property-errors

Default value is true. Determines if the endpoint should throw an error when an incoming client object has unexpected properties that cannot be set on the server object.

log-property-errors

Default value is false. When true, unexpected property errors are logged.

legacy-collection

Default value is false. When true, instances of java.util.Collection are returned as ActionScript Arrays. When false, instance of java.util.Collection are returned as mx.collections.ArrayCollection.

legacy-map

Default value is false. When true, java.util.Map instances are serialized as an ECMA Array or associative array instead of an anonymous Object.

legacy-xml

Default value is false. When true, org.w3c.dom.Document instances are serialized as flash.xml.XMLDocument instances instead of intrinsic XML (E4X capable) instances.

legacy-throwable

Default value is false. When true, java.lang.Throwable instances are serialized as AMF status-info objects (instead of normal bean serialization, including read-only properties).

legacy-externalizable

Default value is false. When true, java.io.Externalizable types (that extend standard Java classes like Date, Number, String) are not serialized as custom objects (for example, MyDate is serialized as Date instead of MyDate). Note that this setting overwrites any other legacy settings. For example, if legacy-collection is true but the collection implements java.io.Externalizable, the collection is returned as custom object without taking the legacy-collection value into account.

type-marshaller

Specifies an implementation of flex.messaging.io.TypeMarshaller that translates an object into an instance of a desired class. Used when invoking a Java method or populating a Java instance and the type of the input object from deserialization (for example, an ActionScript anonymous Object is always deserialized as a java.util.HashMap) doesn't match the destination API (for example, java.util.SortedMap). Thus, the type can be marshalled into the desired type.

restore-references

Default value is false. An advanced switch to make the deserializer keep track of object references when a type translation has to be made; for example, when an anonymous Object is sent for a property of type java.util.SortedMap, the Object is first deserialized to a java.util.Map as normal, and then translated to a suitable implementation of SortedMap (such as java.util.TreeMap). If other objects pointed to the same anonymous Object in an object graph, this setting restores those references instead of creating SortedMap implementations everywhere. Notice that setting this property to true can slow down performance significantly for large amounts of data.

instantiate-types

Default value is true. Advanced switch that when set to false stops the deserializer from creating instances of strongly typed objects and instead retains the type information and deserializes the raw properties in a Map implementation, specifically flex.messaging.io.ASObject. Notice that any classes under flex.* package are always instantiated.

Using custom serialization between ActionScript and Java

If the standard mechanisms for serializing and deserializing data between ActionScript on the client and Java on the server do not meet your needs, you can write your own serialization scheme. You implement the ActionScript-based flash.utils.IExternalizable interface on the client and the corresponding Java-based java.io.Externalizable interface on the server.

A typical reason to use custom serialization is to avoid passing all of the properties of either the client-side or server-side representation of an object across the network tier. When you implement custom serialization, you can code your classes so that specific properties that are client-only or server-only are not passed over the wire. When you use the standard serialization scheme, all public properties are passed back and forth between the client and the server.

On the client side, the identity of a class that implements the flash.utils.IExternalizable interface is written in the serialization stream. The class serializes and reconstructs the state of its instances. The class implements the writeExternal() and readExternal() methods of the IExternalizable interface to get control over the contents and format of the serialization stream, but not the class name or type, for an object and its supertypes. These methods supersede the native AMF serialization behavior. These methods must be symmetrical with their remote counterpart to save the class's state.

On the server side, a Java class that implements the java.io.Externalizable interface performs functionality that is analogous to an ActionScript class that implements the flash.utils.IExternalizable interface.

Note: You should not use types that implement the IExternalizable interface with the HTTPChannel if precise by-reference serialization is required. When you do this, references between recurring objects are lost and appear to be cloned at the endpoint.

The following example shows the complete source code for the client (ActionScript) version of a Product class that maps to a Java-based Product class on the server. The client Product implements the IExternalizable interface and the server Product implements the Externalizable interface.

// Product.as
package samples.externalizable {

import flash.utils.IExternalizable;
import flash.utils.IDataInput;
import flash.utils.IDataOutput;

[RemoteClass(alias="samples.externalizable.Product")]
public class Product implements IExternalizable {
    public function Product(name:String=null) {
        this.name = name;
    }

    public var id:int;
    public var name:String;
    public var properties:Object;
    public var price:Number;

    public function readExternal(input:IDataInput):void {
        name = input.readObject() as String;
        properties = input.readObject();
        price = input.readFloat();
    }

    public function writeExternal(output:IDataOutput):void {
        output.writeObject(name);
        output.writeObject(properties);
        output.writeFloat(price);
    }
}
}

The client Product uses two kinds of serialization. It uses the standard serialization that is compatible with the java.io.Externalizable interface and AMF 3 serialization. The following example shows the writeExternal() method of the client Product, which uses both types of serialization:

public function writeExternal(output:IDataOutput):void {
    output.writeObject(name);
    output.writeObject(properties);
    output.writeFloat(price);
}

As the following example shows, the writeExternal() method of the server Product is almost identical to the client version of this method:

public void writeExternal(ObjectOutput out) throws IOException {
    out.writeObject(name);
    out.writeObject(properties);
    out.writeFloat(price);
}

In the client Product's writeExternal() method, the flash.utils.IDataOutput.writeFloat() method is an example of standard serialization methods that meet the specifications for the Java java.io.DataInput.readFloat() methods for working with primitive types. This method sends the price property, which is a Float, to the server Product.

The examples of AMF 3 serialization in the client Product's writeExternal() method is the call to the flash.utils.IDataOutput.writeObject() method, which maps to the java.io.ObjectInput.readObject() method call in the server Product's readExternal() method. The flash.utils.IDataOutput.writeObject() method sends the properties property, which is an Object, and the name property, which is a String, to the server Product. This is possible because the AMFChannel endpoint has an implementation of the java.io.ObjectInput interface that expects data sent from the writeObject() method to be formatted as AMF 3.

In turn, when the readObject() method is called in the server Product's readExternal() method, it uses AMF 3 deserialization; this is why the ActionScript version of the properties value is assumed to be of type Map and name is assumed to be of type String.

The following example shows the complete source of the server Product class:

// Product.java
package samples.externalizable;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Map;

/**
* This Externalizable class requires that clients sending and 
* receiving instances of this type adhere to the data format
* required for serialization.
*/
public class Product implements Externalizable {
    private String inventoryId;
    public String name;
    public Map properties;
    public float price;

    public Product()
    {
    }

        /**
        * Local identity used to track third party inventory. This property is
        * not sent to the client because it is server-specific.
        * The identity must start with an 'X'.
        */
        public String getInventoryId() {
            return inventoryId;
        }

        public void setInventoryId(String inventoryId) {
            if (inventoryId != null && inventoryId.startsWith("X"))
            {
                this.inventoryId = inventoryId;
            }
            else
            {
                throw new IllegalArgumentException("3rd party product
                inventory identities must start with 'X'");
            }
        }

        /**
         * Deserializes the client state of an instance of ThirdPartyProxy
         * by reading in String for the name, a Map of properties
         * for the description, and 
         * a floating point integer (single precision) for the price. 
         */
        public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
            // Read in the server properties from the client representation.
            name = (String)in.readObject();
            properties = (Map)in.readObject();
            price = in.readFloat();
            setInventoryId(lookupInventoryId(name, price));
        }
        /**
         * Serializes the server state of an instance of ThirdPartyProxy
         * by sending a String for the name, a Map of properties
         * String for the description, and a floating point
         * integer (single precision) for the price. Notice that the inventory 
         * identifier is not sent to external clients.
         */
        public void writeExternal(ObjectOutput out) throws IOException {
            // Write out the client properties from the server representation
            out.writeObject(name);
            out.writeObject(properties);
            out.writeFloat(price);
        }
        
        private static String lookupInventoryId(String name, float price) {
            String inventoryId = "X" + name + Math.rint(price);
            return inventoryId;
        }
}

The following example shows the server Product's readExternal() method:

public void readExternal(ObjectInput in) throws IOException,
    ClassNotFoundException {
    // Read in the server properties from the client representation.
    name = (String)in.readObject();
    properties = (Map)in.readObject();
    price = in.readFloat();
    setInventoryId(lookupInventoryId(name, price));
    }

The client Product's writeExternal() method does not send the id property to the server during serialization because it is not useful to the server version of the Product object. Similarly, the server Product's writeExternal() method does not send the inventoryId property to the client because it is a server-specific property.

Notice that the names of a Product's properties are not sent during serialization in either direction. Because the state of the class is fixed and manageable, the properties are sent in a well-defined order without their names, and the readExternal() method reads them in the appropriate order.


Comments


Ron Fulkerson said on Jun 14, 2009 at 9:32 PM :
To handle derived types of a base class in BlazeDS, an explicit AS3 class with a RemoteObject tag must be created for each derived type. A returned remote object class much then be type checked with the 'is' operator and cast to the appropriate class. See http://tr.im/opFt for more info on the state of dealing with derived remote classes...
Prakash.NET said on Dec 23, 2009 at 12:55 AM :
Hi,

Nice article.

When i tried to implement with an example, i got a error like below;

[FaultEvent fault=[RPC Fault faultString="Didn't receive an acknowledge message" faultCode="Server.Acknowledge.Failed" faultDetail="Was expecting mx.messaging.messages.AcknowledgeMessage, but received null"] messageId="49F617F8-B893-3F5F-7A02-BAB887F98F09" type="fault" bubbles=false cancelable=true eventPhase=2]

Can any one tell me where i missedout?..

Regards,
Prakash.
mpeterson said on Dec 23, 2009 at 10:31 AM :
Here is a zip of the complete app that uses these Product classes. I just set it up and ran it without issues, so take a look at the overall structure and the MXML.
http://blogs.adobe.com/pfarland/examples/externalizable.zip
No screen name said on Feb 16, 2010 at 11:36 PM :
How can i stop converting dates flex to java. If we have passed date in UTC the server time is in GMT then the date was converting according to the GMT and inserted in to the database. How can i remove this convertion at blazeds level. is there any possible to to set it in config files?
fiergra said on Mar 9, 2010 at 5:14 AM :
What about the types of objects contained in a AS ArrayCollection?

When sending an object of type Collection<MyJavaClass> from JAVA to FLEX
it is converted to an instance of type ArrayCollection on the client. The type
definition of the elements is lost!?! They are represented as simple Objects
in FLEX. Sending back that ArrayCollection to a JAVA method on the server
will result in type conversion errors, since all elements are sent as ASObject
instances!

Only workaround is to iterate over the ArrayCollection and explicitly cast all
elements to the corresponding type.
TomBaggett said on Apr 16, 2010 at 5:09 AM :
fiergra - If you set up an AS3 equivalent to the Java class and map it to the Java class using the [RemoteClass(alias="com.myjavaapp.myjavaclass")] metadata tag, your ArrayCollection members will be instances of the AS3 class instead of generic objects. If "MyJavaClass" contains member properties that are other classes, you would have to create AS3 equivalents of them as well.

Once you get everything mapped, the data will correctly make the round trip between your Java and Flex apps.

Here is a modified version of the example from the top of the documentation. Note that you don't have to use the [Bindable] tag unless you actually want it to be bindable.

package com.myflexapp {
[Bindable]
[RemoteClass(alias="com.myjavaapp.MyJavaClass")]
public class MyFlexClass {
// Would need to map this class as well or it will be a generic Object
public var myOtherClass:MyOtherFlexClass;

public var contactId:int;

public var firstName:String;

public var lastName:String;

public var address:String;

public var city:String;

public var state:String;

public var zip:String;
}
}

 

RSS feed | Send me an e-mail when comments are added to this page | Comment Report

Current page: http://livedocs.adobe.com/blazeds/1/blazeds_devguide/serialize_data_3.html