Introducing JMF (Java Messaging Format)
August 22nd, 2013
Annoucements, GraniteDS, JavaFX
GraniteDS release 3.0.0.M2 introduced a new serialization format for Java clients which is largely inspired by AMF3 (Action Message Format version 3). It is called JMF, which stands for Java Messaging Format, and it is now used, starting with GraniteDS 3.0.0.M3, as the standard serialization format between Java / JavaFX / Android client applications and GraniteDS servers.
Basically because we love AMF but AMF isn’t good for Java client applications: with AMF, for example, all Java numeric types (byte, short, int, long, float, double, etc.) are serialized either as ActionScript int or Number, which leads to useless conversions in the context of a Java to Java data exchange and can even lead to a loss of precision (large long values cannot be represented as Numbers without truncation). Furthermore, all collections (java.util.*) are normalized to a single ArrayCollection type, BigDecimals are converted to Numbers, enum values to nothing standard (ActionScript has no enum types), etc.
Using AMF with Java clients is certainly possible, and we do have AMF support for Java clients in GraniteDS, but it is pointlessly complicated and, as a result, requires a full bunch of ugly workarounds.
That said, JMF is largely inspired by AMF and designed with the following goals in mind:
- Compactness: serialized data must be as small as possible (even more than with AMF).
- Completeness: circular references must be correctly handled, everything that should be serialized must be serialized.
- Accuracy: no trans-typing, no pointless conversions, unless explicitly wanted.
- Extensibility: it must be possible to plug dedicated codecs for specific data types.
- Observability: data flow must be understandable by programs that have no knowledge of what is serialized.
- Security: only data that are meant to be serialized must be serialized.
- Speed: serialization must be as fast as possible (but without breaking the previous goals).
How to Use JMF?
You basically need to do nothing to use JMF: HTTP headers of serialized data, unless you have configured your Java client application to do otherwise, use a specific mime-type which tells the GraniteDS backend to switch to JMF instead of AMF serialization for the incoming request and corresponding response.
The JMF mime-type is currently “application/x-jmf+amf”, the “+amf” part stating that the messages inside the request are instances of flex.messaging.messages.Message (possibly enclosed in an AMF0 envelop). We are considering to use our own message envelops when using JMF and the mime-type is likely going to be “application/x-jmf” only in later versions.
The good thing with this mime-type switch mechanism is that you can have Java (or even JavaFX / Android) client applications using the exact same backend used by Flex client applications: there is nothing specific to configure on the server-side.
How does JMF Compare to AMF and Standard Java Serialization in Terms of Size?
With small messages such as a ping message (which purpose is to make sure that the server is alive and to negotiate serialization options), JMF vs. AMF vs. Standard Java Serialization (ie. java.io.ObjectOutputStream) gives these results:
- JMF: 113 bytes.
- AMF: 261 bytes.
- Java Serialization: 894 bytes.
With bigger messages (a list of person with collections of contacts), you can typically get these results:
- JMF: 2115 bytes.
- AMF: 2749 bytes.
- Java Serialization: 5278 bytes.
As a general result, but without conducting an extensive benchmark, we have found that:
- The size of JMF encoded data can be up to 1/3 of the size of AMF encoded data.
- The size of JMF encoded data can be up to 1/8 of the size of Java serialized data.
- JMF encoding is always smaller than AMF or standard Java serialization encoding.
What about Security?
AMF serialization doesn’t state anything about what is (de)serializable or not: this lack of control can lead to a security breach that was discovered by Wouter Coekaerts and reported here. Granite Data Services fixed this issue by adding a AMF3DeserializerSecurizer that controls which class can be instantiated at deserialization time (see this post about GraniteDS 2.2.1.GA).
Unlike AMF but just like the standard Java serialization, JMF only encodes primitive types and classes that implement java.io.Serializable. As a result, the above vulnerability doesn’t affect JMF, unless classes that shouldn’t be serialized implement (which is a design bug) java.io.Serializable.
Is there a public JMF specification?
Not yet. However, you are free to have look at the implementation, it’s fully open-source, just like the rest of the GraniteDS platform.