Just Another Multithread Marshaller Engine
Jamme is a highly customizable multi-thread engine that allows to bidirectionally transform a java object in its XML representation (marshall) and viceversa (unmarshall). It is designed to be easily integrated in a dependency injection framework like Spring.
The main purpose of this library is to allow to serialize and deserialize complex (and even recursive) graphs of java objects in an XML format instead of using the native java serialization mechanism. Indeed XML serialization has the following advantages:
- It produces human readable files instead of raw byte streams.
- The resulting XML can be transformed in something useful for other applications through an XSL transformation.
- It is easier customizable, in an almost declarative way.
- It is far more robust to data model changes: if the serialized java class slightly changes it is not possible to desrialize it again, while the XML marshall is still unmarshallable in the modified classes with no or few and simple modifications
How jamme works
To achieve this results jamme by default introspects the objects to be marshalled not by accessing all their getter methods (as for example betwixt does), but by reading the objects fields skipping the static and the transient ones.
This choice has been made because the actual meaningful state of a java object is in its fields values, while reading and writing those values through the standard java bean accessor methods can easily drive to marshall useless values (for example calculated ones) and ignore the ones that actually represents the object state.
Why jamme is multithreaded
In the last years multithreaded software is gaining more and more importance even because the race toward always higher CPU frequency and number of transistors integrated on a single core is going to finish while CPU makers tend to increase the performances of their devices integrating on them 2 or more cores.
For this reason jamme allows to (optionally) split the potentially time-consuming marshall of huge objects graph and unmarshall of long XML strings on different threads. Jamme achieves this result with a single-producer/multiple-consumers pattern. In more details the producer thread splits the objects graph or the XML string to be processed in more parts pushing each part on a queue while the consumer threads pop these parts from the queue and parse them in parallel.
Start using jamme
To transform an object in its XML representation and vice versa with jamme is as easy as to invoke its Marshaller interface. The default implementation of this interface (that has a very straightforward API) provided by jamme is the MarshallerImpl class. So by instancing this class it is possible to serialize the state of an object in XML format:
Marshaller marshaller = new MarshallerImpl(); String xml = marshaller.marshall(object);
and conversely to do the opposite reconverting the resulting XML string in an object that is a complete deep clone of the former one:
Object object = marshaller.unmarshall(xml);
Jamme configuration and customization
Jamme is highly and easily configurable by replacing its strategy classes that defines its behavior. In other words you can customize how jamme converts objects in XML and vice versa by providing different strategies. For each of these pluggable aspects, jamme comes with a meaningful default strategy that provides a simple, but complete and coherent, behavior that in turn allows to use jamme "as it is" out-of-the-box.
Moreover all of these strategies can be plugged both in the marshaller engine itself (possibly via dependency injection with a framework like Spring or similar) in order to permanently modify its behavior, and in a configuration object that can be passed to a given marshall request to provide a one-shot definition of how that specific request is processed.
It's possible to redefine a given strategy both by providing a new implementation of its specific interface or by extending the jamme default one. Remember that, if you are going to use jamme in a multithreaded environment or even if you want to use its multithreaded marshaller engine, the provided strategy classes need to be thread safe as well. In more detail, the description of all the available strategies through which you can customize jamme follows:
- Class Normalizer - It defines the actual class that needs to be introspected when a given object is going to be marshalled. This strategy is useful when you need to marshall proxy objects for example because they have been fetched with an O/R mapping tool like Hibernate.
- Field Binding Strategy - It allows to decide if a given object property should be mapped as an attribute of the xml element describing the containing object or as another element inside it. Be aware that if you map a field as an attribute, jamme assumes that it is a primitive value and it writes the field value using the provided object conversion strategy instead of further introspect it.
- Field Naming Strategy - It defines how a field name should be translated in the corresponding name marshalled into the xml and vice versa. It can be useful to make the produced XML more readable allowing to use a naming convention that better matches the names of a specific domain. It is also possible to use it when the name of a given field get changed after a refactor, in order to not lose the backward compatibility of an XML produced before the refactor itself.
- Field Primitive Strategy - It defines if a given field should be treated as a primitive (a single plain value) field or as a complex one that needs to be recursively introspected in its own fields.
- Field Suppression Strategy - It defines if a given field should be ignored during tha marshall and then omitted from the produced XML.
- Marshall Style - Defines the style (i.e. the verbosity and the kind of the informations added to the xml) while marshalling an objects' graph. By default it is set to BIDIRECTIONAL that is the most verbose but also the only one that assures that the resulting XML could be unmarshalled back in the graph from which it has been generated.
- Marshaller Thread Strategy - It defines how many parallel threads should be used for a given marshall process.
- Naming Convention - It defines the naming conventions used by jamme while marshalling an object in xml. By overriding it you are allowed to change the name of some xml elements like the root one, the items of a list or of an array, or the keys and value of a map.
- Object Creator - It defines how to create an empty object that fits a given field type and will be used to accomodate the data contained in a given XML element.
- Object Introspector - It defines how to introspect an object in order to transforming its properties in a collection of name/value pairs. As stated, by default jamme does this job by using the names and the corresponding values of all the non transient and non static fields of the given object.
- Object To String Convertor - It defines how to convert an object in its String representation and conversely a String in the Object that it represents given its Class.
Integrating Jamme with Spring
Jamme has been designed to be easily integrated in a Spring application. Configure and instance Jamme as a normal bean's Spring is straightforward as in the following XML snippet where a class normalizer, a field suppression strategy and an object creator have been added in order to customize the default Jamme behavior.
<bean id="marshaller" class="ch.jamme.impl.MarshallerImpl"> <property name="classNormalizer"><bean class="mypackage.MyClassNormalizer"/></property> <property name="fieldSuppressionStrategy"><bean class="mypackage.MyFieldSuppression"/></property> <property name="objectCreator"><bean class="mypackage.MyObjectCreator"/></property> </bean>