Wednesday, February 4, 2009

enums

Back in the FDS (Flex Data Services) days we were stuck supporting Java 1.4.x. Hence, no enum support because they weren't available in the language until 1.5. But things changed awhile back when BlazeDS and LCDS up'ed their base supported JVM to 1.5.

However, just because we could now support enums in the JVM at the server didn't mean anything to the AVM (ActionScript Virtual Machine) at the client.

ActionScript (and ECMAScript) doesn't provide a native enum type. To complicate matters futher, the flash.utils.IExternalizable interface doesn't provide the writeReplace() and readResolve() hooks that the java.io.Externalizable interface defines. When you're deserializing a raw byte stream into an object graph, you need a readResolve() in order to handle enums (singletons).

Java enum serialization can't be overridden at the server, which is good! It's good because this means that enums are always serialized and deserialized according to a consistent recipe. When they're serialized in Java-land (ignoring AMF for a moment), their String name value (as returned by the name() method) is all that's written to the byte stream. When they're deserialized, a single String value is read from the byte stream and passed to the enum's valueOf() method. The returned singleton instance is then readReplace()'ed into the resulting object graph.

Based on this behavior, BlazeDS and LCDS send Java enums to your Flex app as Strings. If the client needs to send an "enum" to the server, send the String value for the enum, and it'll be unpacked as the desired Java enum instance at the server.

But some folks want more than a simple String at the client. As I mentioned earlier, lack of native enum types in ActionScript coupled with lack of a readResolve() hook on flash.utils.IExternalizable makes this impossible. On top of that, when the Java enum instance is written to the AMF byte stream at the server, the AMF type prefix is for the native String type. That means the server-side type name for the enum isn't included in the byte stream so attempting to use registerClassAlias() or [RemoteClass(alias="...")] is futile (and you wouldn't want that anyways without a readResolve() hook at the client).

If you're deadset on representing these values as typed singleton instances at the client, you'd have to do something wacky like manually traversing the data returned from the server (i.e.: a RemoteObject result) and replacing your "enum" Strings or some other marker in the result with the corresponding client-side singleton type instance of your own making before anything else got its hands on the data. That's a lot of brittle data munging for very questionable payoff...

Perhaps at some happy point in the future we'll have a native enum type to work with at the client, but until then my recommendation is to take a deep breath, relax, and just work with Strings (along with defining matching String constants that you can use for data validation, assignments, and equality checks where it makes sense).

2 comments:

Vijay K Ganesan said...

Too bad ActionScript does not have a native Enum construct as you point out. When working with a large object model as I do, this is a real pain. I find using this pattern comes closest to true enums and seems to work ok:
http://meteorite-13.blogspot.com/2008/01/introduction-of-enums-in-java-1.html

Vijay

M said...

The lack of enumeration in Flex helped me to decide to write my own Java Flex code generator where this data type mapping issue could be solved. Besides enumeration there are more problems with Java/Flex type mapping (i.e. the date datatype). At this moment I'm still working on the generator, but already the Paddle generator can be used freely at http://paddle.devoorkant.nl.
Further there are plans to extend the code generation with Hibernate/EJB3 at the back end and MVC and validation extensions at the front end.

- Martijn