Friday, September 14, 2012

BlazeDS, LCDS and Flex-Java Interaction (Part 2)

Question: What do we mean by serialization/deserialization and Marshalling/UnMarshalling?
Answer: We can use remoting in blazeDS/LCDS to make use of server-side logic and  get the result back in Flex Client. Whenever we call any function on server side we generally pass some value-objects/primitives to server and there this marshalling (or serialization) comes into picture.

The conversion between data types in actionscipt to corresponding data types to Java (or any other supported server side laguage) is main idea of marshalling or serialization. While we receive data back from server side it is referred to as deserialization or un-marshalling. We can also use instances of our own custom (value objects) and then provide complete path of the corresponding value-object (in remote object tag)on server side to help blazeDS and FVM (Flash Virtual Machine) to cooperate in serialization/de-serialization.
BlazeDS uses the AMF binary protocol to serialize and deserialize Java classes, and the Flash Player does the same for the Flex classes on the client side.The use of a AMF binary data transfer format increases performance, allowing applications to load data up to 10 times faster than with text-based formats such as XML or SOAP.

Question: What is AMF protocol and where is it used?
Answer: AMF stands for Action Message Format. It is binary data prorocol supported by Flash Virtual Machine. It can be best described as SOAP-RPC hybrid, in which data is transferred via Remote Procedure Calls. This protocol is open source now.
Advantages of AMF include: rapid data transfer, automatic marshalling and unmarshalling of data objects by the FVM, support across the entire spectrum of server side languages including .NET, PHP, Java, Ruby, and Python.
Disadvantages of AMF include: proprietary protocol that is not compatible with other client-side RIA development platforms such as DOJO, Scriptaculous, and SilverLight.  However, JavaScript communication via AMF with a server language is made possible through the Adobe AIR platform.


Question: Can we achieve custom serialization/de-serialization?
Answer: Yes. The solution is to use flash.utils.IExternalizable interface in ActionScript which is compatible with java.io.IExternalizable API. We need to implement these interfaces on the client side and server side value objects respectively to take control of their serialization The API requires two methods readExternal() writeExternal() which take flash.utils.IDataInput and flash.utils.IDataOutput streams respectively. The implementations of these methods mirror the server Java class, which implements java.io.Externalizable – also with two methods readExternal() and writeExternal() taking java.io.ObjectInput and java.io.ObjectOutput streams respectively.


 public void writeExternal(ObjectOutput out) throws IOException 

 {    

    out.writeObject(id);    

    out.writeObject(name);   

    out.writeObject(description);   

    out.writeInt(price);  

 }  

 …mirrors the client readExternal method in ActionScript:  

 public function readExternal(input:IDataInput):void{  

    _id = input.readObject() as String;    

   name = input.readObject() as String;    

   description = input.readObject() as String;    

   price = input.readInt();  

 }  
A similar relationship exists for the reverse situation for sending instances back from the client to the server.


Question: Now we know that we can write custom serialization/de-serialization routines, but sometimes these are invoked for Flex->Server (BlazeDS) direction and not for Server->Flex Direction. What can be done to solve this problem?
Answer:If we encounter any such problem then the solution is to write own AMFEndpoint class and relevant serialization class.

Channel-Definition


<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
        <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" class="ch.hedgesphere.core.blazeds.endpoint.AMFEndpoint"/>

         <properties>
            <serialization>
                <type-marshaller>ch.hedgesphere.core.blazeds.translator.HedgesphereASTranslatortype-marshaller>
            serialization>
        properties>

    channel-definition>

Custom AMF Endpoint
 
package ch.hedgesphere.core.blazeds.endpoint;


import ch.hedgesphere.core.blazeds.serialization.Serializer;

    public class AMFEndpoint extends flex.messaging.endpoints.AMFEndpoint {

    @Override
    protected String getSerializerClassName() {
        return Serializer.class.getName();
        }

    }

Custom Serializer

 package ch.hedgesphere.core.blazeds.serialization;

import java.io.OutputStream;
import flex.messaging.io.MessageIOConstants;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.AmfMessageSerializer;
import flex.messaging.io.amf.AmfTrace;
public class Serializer extends AmfMessageSerializer {

    @Override
    public void initialize(SerializationContext context, OutputStream out, AmfTrace trace)
    {
        amfOut = new AMF0Output(context);
        amfOut.setOutputStream(out);
        amfOut.setAvmPlus(version >= MessageIOConstants.AMF3);

        debugTrace = trace;
        isDebug = trace != null;
        amfOut.setDebugTrace(debugTrace);
    }
}
Custom AMF0 Handling

 package ch.hedgesphere.core.blazeds.serialization;

import flex.messaging.io.SerializationContext;
public class AMF0Output extends flex.messaging.io.amf.Amf0Output {
public AMF0Output(SerializationContext context) {
    super(context);
}
@Override
    protected void createAMF3Output()
    {
        avmPlusOutput = new AMF3Output(context);
        avmPlusOutput.setOutputStream(out);
        avmPlusOutput.setDebugTrace(trace);
    }
}

Custom AMF3 Handling


package ch.hedgesphere.core.blazeds.serialization;
import java.io.IOException;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalTime;
import flex.messaging.io.SerializationContext;
public class AMF3Output extends flex.messaging.io.amf.Amf3Output {
public AMF3Output(SerializationContext context) {
    super(context);
}
@Override
public void writeObject(Object value) throws IOException {
    if(value instanceof DateTime) {
        value = convertToDate((DateTime)value);
    }
    if(value instanceof LocalDate) {
        value = convertToDate((LocalDate)value);
    }
    if(value instanceof LocalTime) {
    value = convertToDate((LocalTime)value);
    }
    super.writeObject(value);
}
private Object convertToDate(LocalTime time) {
    return time.toDateTimeToday().toDate();
}
private Object convertToDate(LocalDate date) {
    return date.toDateMidnight().toDate();
}
private Object convertToDate(DateTime dateTime) {
    return dateTime.toDate();
}   }

Custom Marshaller from Flex->Java

@SuppressWarnings({"rawtypes"})

@Override
public Object convert(Object originalValue, Class type) {
    if( type.equals(DateTime.class)) {
        return convertToDateTime(originalValue);
    }
    if( type.equals(LocalDate.class)) {
    return convertToLocalDate(originalValue); 
    }
    if( type.equals(LocalTime.class)) {
        return convertToLocalTime(originalValue);
    }

    return super.convert(originalValue, type);
}
private Object convertToLocalTime(Object originalValue) {
    return originalValue == null ? null : new LocalTime(originalValue);
}
private Object convertToLocalDate(Object originalValue) {
    return originalValue == null ? null : new LocalDate(originalValue); }
private Object convertToDateTime(Object originalValue) {
    return originalValue == null ? null : new DateTime(originalValue);
}
@SuppressWarnings({"rawtypes"})
@Override
public Object createInstance(Object source, Class type) {
    return super.createInstance(source, type);
}
}


Question:The main problem in serialization/de-serialization occurs when we are using numeric types. Actually Flex does not have null data type and when a null value comes from java side it is converted to zero. Also no datatype in Flex can hold a value of long data type in Java. In case precision matters we need to write our own custom solution. What can be done?
Answer: BlazeDS uses a BeanProxy to read and write attributes into an object (using its getters and setters), and to convert it to the right type if needed. That is an ideal place to customize. When reading values i.e. sending them to Flex (from Java) it is fine but when values move from flex to java it is problem because it will handle setting the properties of object but not handle the conversion when the number is an argument to remote call. For that to work a custom NumberDecoder is needed. As there is no mechanism to write custom decoder, we also need to replace TypeMarshaller.

Above excerpt is taken from the following link:
http://labs.bsb.com/2010/11/serialization-of-numeric-types-with-blazeds/