Using Custom Type Marshallers in BlazeDS

In my previous post I pointed to the BlazeDS classes that need to be replaced in order to prevent ActionScript Number.NaN from turning into Long or Integer zeroes on the MessageBroker side. The recommendation boiled down to re-jaring flex-messaging-core.jar or placing the modified classes somewhere earlier in the application server’s classpath. If neither option is allowed, you may configure your endpoint with the custom type marshaller, like the one below:


        <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
           <endpoint url="http://{server.name}:{server.port}/{context.root}/messagebroker/amf" 
           class="flex.messaging.endpoints.AMFEndpoint">
           </endpoint>
           <properties>    
               <serialization>
                   <type-marshaller>clear.messaging.io.amf.translator.NullkeepingASTranslator</type-marshaller>
               </serialization>
           </properties>    
       </channel-definition>

In BlazeDS 4 you will base your type marshaller on flex.messaging.io.ASTranslator:
public class NullkeepingASTranslator extends
    flex.messaging.io.amf.translator.ASTranslator
{
    private static final NumberDecoder numberDecoder = new NumberDecoder();  
    public Object convert(Object source, Class desiredClass)
    {
      if ( DecoderFactory.isNumber(desiredClass)){
        return numberDecoder.decodeObject(source, desiredClass);
      }else{
        return super.convert(source, desiredClass);
      }
    }
}

This route does not require neither server-level deployment nor modification of the flex-messaging-core.jar.

I’d like to use this opportunity and invite Flex developers living in Europe to attend our Advanced Flex Master Class in Brussels, Belgium on March 1 and 2, 2010.

Victor Rasputnis

Prevent Flex numeric nulls from turning to zeros in BlazeDS

This question we get on almost every new client project: “We’re struggling with handling of null values for numeric data types in Flex/Java projects. Every time there’s an update, we end up replacing the original nulls with zeros when the user didn’t actually change that value.  Have you guys come up with a silver bullet for handling numeric nulls?”

Consider a Java method with a Double parameter. You pass uninitialized ActionScript Number, that is, Number.NaN. What will BlazeDS deserialize (unmarshal)? Double.NaN. At this point your Java code may use something like (value==Double.NaN)?null:value, capitalizing on the fact that information about special value of NaN had been delivered from your client code to your server code. Now, let’s assume you change the signature of the Java method to accept Long instead of Double. You pass NaN and you get … 0! The same happens with marshaling ActionScript object that carries Number.NaN properties: they turn into 0, if, god forbid, their Java counterparties are declared as Long or Integer.

The reason is that while Java has Double.NaN it does not support either Long.NaN or Integer.NaN. Looking at the following snippet of code you can see how differently Double and Long variables get treated by Java:


     Double dbl = new Double(Double.NaN); // We emulate incoming numeric null
     System.out.println(dbl); //prints NaN, cause dbl "knows" it came from null
     long l = new Long(dbl.longValue(new Double(Double.NaN)));
     System.out.println(l); // Oops, prints 0!

Unfortunately, native BlazeDS flex.messaging.io.amf.translator.NumberDecoder falls into this trap. So, what is to be done? Luckily, BlazeDS is an open source product and this class has to be slightly modified.

Figure 1 illustrates the changes required to protect null-ability of your Long, Integer, etc values, except Double (we explain what to do with Double-s a bit later):

Modified NumberDecoder class  under Deltawalker

Figure 1. Modified NumberDecoder. Farata modification is on the left, canonical BlazeDS class is on the right.

Now, after you make the changes and compile the class against the rest of the flex-messaging-*** jars you can re-jar your own flex-messaging-core.jar.
Better yer, place this class in the common server folder so that it positively affects classpath of all applications on the server.
From now on, Number.NaN will come as Long null, or Integer null – whatever you decide on the Java side.

It this sounds like to big of a deal to you, keep using Double values and convert Double.NaN to null yourself, when appropriate.


Finally, if you would also like to see Double.NaN automatically converted to null, you will have to substitute one more BlazeDS class – flex.messaging.io.amf.translator.NativeDecoder. Explanation: it’s just so happens that BlazeDS marshalling ignores the NumberDecoder when the source (Number, aka Double) and target (Double) types are the same. Here we come and force BlazeDS to use NumberDecoder with numbers no matter what:

    public class NativeDecoder extends ActionScriptDecoder
{
    public Object decodeObject(Object shell, Object encodedObject, Class desiredClass)
    {
       if ( DecoderFactory.isNumber(desiredClass)){
         NumberDecoder numberDecoder = new NumberDecoder();
            return numberDecoder.decodeObject(encodedObject, desiredClass);
       } else
         return encodedObject;      // the sole original BlazeDS line
    }
}

Source code:
NumberDecoder.java modified by Farata
NativeDecoder.java modified by Farata

I’d like to use this opportunity and invite Flex developers living in Europe to attend our Advanced Flex Master Class in Brussels, Belgium on March 1 and 2, 2010.

Victor Rasputnis