A blog about software development and other software related matters

Blog Archive

Saturday, August 30, 2008

AMF serialization from Java to Flex and back

There are some scenarios in which you want to pass objects to and from a Flex application to a Java back end, if your using an Adobe framework (LCDS or BlazeDS) than you get this functionality out of the box however there are some cases that you wish to escape Adobe's grip and uses some third party technology.
Some solutions that may come to mind are JSon or XML, the problem with these solutions is that they may require a lot of bandwidth and even lots of computing power for complex objects graphs, AMF to the rescue.
AMF is a binary protocol which is used natively by the flash player, with the release of BlazeDS it was made accessible to any Java client that wishes to use it, in order to use AMF we need to serialize an object into bytes and pass it as the payload of any protocol that we choose, the easiest way to do so is to encode the resulting AMF bytes into BASE64 encoding (a format that transforms byte arrays into a readable string form) and append it to the existing payload.
Ill start with the Java side first:


import com.google.inject.Inject;
import flex.messaging.endpoints.BaseHTTPEndpoint;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Input;
import flex.messaging.io.amf.Amf3Output;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

/**
*
* @author ronen
*/
public class AmfSerializer {

@Inject
private SerializationContext context;

public <T> String toAmf(final T source) throws IOException {
final StringBuffer buffer = new StringBuffer();
final ByteArrayOutputStream bout = new ByteArrayOutputStream();
final Amf3Output amf3Output = new Amf3Output(context);
amf3Output.setOutputStream(bout);
amf3Output.writeObject(source);
amf3Output.flush();
amf3Output.close();
final BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(bout.toByteArray());
}

public <T> T fromAmf(final String amf) throws ClassNotFoundException, IOException {
final BASE64Decoder decoder = new BASE64Decoder();
byte[] input = decoder.decodeBuffer(amf);
InputStream bIn = new ByteArrayInputStream(input);
Amf3Input amf3Input = new Amf3Input(context);
amf3Input.setInputStream(bIn);
return (T) amf3Input.readObject();
}
}


This code uses BlazeDS API (the messaging-core, messaging-common and messaging-remoting jars) in order to serialize an object of type T into AMF byte array the bytes are encoded to a Base64 string, encoding result of an instance of the following VO:


package com.jdftm.vo;

import java.util.Date;

public class CurrentDayVO {

private Date now;

public CurrentDayVO() {
}

public void setNow(Date now) {
this.now = now;
}

public Date getNow() {
return now;
}
}


might look something like ChMzY29tLmpkZnRtLnZvLkN1cnJlbnREYXlWTwdub3cIAUJrjUw54AAA.

Now lets turn to the Flex side:


package com.jdftm.stomp.interop {
import flash.utils.ByteArray;
import mx.utils.Base64Encoder;
import mx.utils.Base64Decoder;

public class AMFSerializer {
public function serializeToString(value:Object):String{
if(value==null){
throw new Error("null isn't a legal serialization candidate");
}
var bytes:ByteArray = new ByteArray();
bytes.writeObject(value);
bytes.position = 0;
var be:Base64Encoder = new Base64Encoder();
be.encodeBytes(bytes);
var res:String = be.toString();
be.reset();
return res;
}

public function readObjectFromStringBytes(value:String):Object{
var dec:Base64Decoder=new Base64Decoder();
dec.decode(value);
var result:ByteArray=dec.drain();
result.position=0;
return result.readObject();
}
}
}


The logic is quite similar to the Java side code however when using the AMFSerializer we must register the serialized classes prior to serializing them:


registerClassAlias("com.jdftm.vo.CurrentDayVO", CurrentDayVO);


Failing to so will cause the de-serialization process (on both sides) to fail since it uses the alias in order to create the resulting instance object, its also required that the serialized classes on both ends to be in the same package as implemented in the CurrentDayVO Flex class:


package com.jdftm.vo{
[Bindable]
[RemoteClass(alias="com.jdftm.vo.CurrentDayVO")]
public class CurrentDayVO{
private var _now:Date;

public function get now():Date{
return _now;
}

public function set now(value:Date):void{
_now=value;
}
}
}


As youv seen AMF isn't to hard to use and may prove to be a powerful contender in the crowded integration protocols market.

Updated 2/09/08
Here is the SerializationContext implementation which is used in the Java serialization class


import com.google.inject.Provider;
import flex.messaging.io.SerializationContext;

public class SerializationContextProvider implements Provider<SerializationContext> {

@Override
public SerializationContext get() {
SerializationContext serializationContext = SerializationContext.getSerializationContext();// Threadlocal SerializationContent
serializationContext.enableSmallMessages = true;
serializationContext.instantiateTypes = true;
serializationContext.supportRemoteClass = true;// use _remoteClass field
serializationContext.legacyCollection = false;// false Legacy Flex 1.5 behavior was to return a java.util.Collection for Array, New Flex 2+ behavior is to return Object[] for AS3 Array
serializationContext.legacyMap = false;// false Legacy flash.xml.XMLDocument Type
serializationContext.legacyXMLDocument = false;// true New E4X XML Type
serializationContext.legacyXMLNamespaces = false;// determines whether the constructed Document is name-space aware
serializationContext.legacyThrowable = false;
serializationContext.legacyBigNumbers = false;
serializationContext.restoreReferences = false;
serializationContext.logPropertyErrors = false;
serializationContext.ignorePropertyErrors = true;
return serializationContext;

/*
serializationContext.enableSmallMessages = serialization.getPropertyAsBoolean(ENABLE_SMALL_MESSAGES, true);
serializationContext.instantiateTypes = serialization.getPropertyAsBoolean(INSTANTIATE_TYPES, true);
serializationContext.supportRemoteClass = serialization.getPropertyAsBoolean(SUPPORT_REMOTE_CLASS, false);
serializationContext.legacyCollection = serialization.getPropertyAsBoolean(LEGACY_COLLECTION, false);
serializationContext.legacyMap = serialization.getPropertyAsBoolean(LEGACY_MAP, false);
serializationContext.legacyXMLDocument = serialization.getPropertyAsBoolean(LEGACY_XML, false);
serializationContext.legacyXMLNamespaces = serialization.getPropertyAsBoolean(LEGACY_XML_NAMESPACES, false);
serializationContext.legacyThrowable = serialization.getPropertyAsBoolean(LEGACY_THROWABLE, false);
serializationContext.legacyBigNumbers = serialization.getPropertyAsBoolean(LEGACY_BIG_NUMBERS, false);
boolean showStacktraces = serialization.getPropertyAsBoolean(SHOW_STACKTRACES, false);
if (showStacktraces && Log.isWarn())
log.warn("The " + SHOW_STACKTRACES + " configuration option is deprecated and non-functional. Please remove this from your configuration file.");
serializationContext.restoreReferences = serialization.getPropertyAsBoolean(RESTORE_REFERENCES, false);
serializationContext.logPropertyErrors = serialization.getPropertyAsBoolean(LOG_PROPERTY_ERRORS, false);
serializationContext.ignorePropertyErrors = serialization.getPropertyAsBoolean(IGNORE_PROPERTY_ERRORS, true);
*/
}
}



This is the guice provider which is used when creating such contexts.

Friday, August 22, 2008

Google collection API

If your using Java collection API you should really see the following presentation about Google collection API, this is truly a gem for any Java programmer:

The first part


The second part:

Thursday, August 21, 2008

Code is data or so they say..

Iv been writing some custom checkstyle checks, the process of doing so is laborious and annoying, checkstyle uses an ANTLR visitor to allow the programmer to validate the tokens he wishes, this approach doesn't scale too well since it makes the programmer think of the "how" and not on "what", by that i mean that instead of being declarative its imperative.
After writing a couple of those it made me realize that there must be a better tool in which i could query the code instead of running along side the it, as a matter of a fact we are using such a tool every day, the IDE.
In the IDE we search for types, usages and other rather limited queries, we overcome these limitations by traversing the code wisely however there are still much more left be desired, for example the following query:


select field from classes where class.package=com.jdftm.* and field.visibilty=protected

which finds all the protected fields within the com.jdftm package, this kind of queries can be usefully not only on build time but also on development and runtime!

The runtime usage might sound a bit awkward but it might prove to be very powerful, imagine that we wish to write a test which validates that all the classes in com.jdftm package will not accept null parameters in any of their public methods, its possible to do so with some laborious reflection code but doing with a query tool would be much cleaner and easier to follow.

It seems that im not the first one to think in this direction still i would like to find some library that can run in pure Java code (no IDE dependency).

Update:
After yet some more googeling iv found yet two more interesting tools javaML and bbq