XML Serialization
Overview
Platform provides facilities to save java objects in xml format. Most of the platform objects use this approach (reports, report queries, search queries, import/export configurations and mappings, assortment, value providers, job framework objects). The main purpose of xml serializion is to provide JMV independent, 'human readable' and controllable format to store java objects. If you need to persist service objects and you don't need to perform search or match queries on a set of persisted objects then HPM xml serialization is a correct approach to use. A typical example of xml serialization is a user specific configuration object which should be saved between user login sessions.
What xml serialization is not:
It is not a domain objects persistence
It is not the HPM communication framework marshaling
For JEE folks: HPM XML Serialization has the same purposes as Java Architecture for XML Binding (JAXB) technology.
Services and Interfaces
All necessary services and interfaces can be found in the com.heiler.ppm.xml bundle.
This bundle also contains xml 3rd party libraries which are required in order xml serialization to function properly. Please, don't change or upgrade these libraries because other versions (or spec implementaions) will most likely not work and cause hard-to-diagnose bugs (as of 5.3). These libraries are located in the /endorsed folder of the bundle which is configured as a java.endorsed.dirs java setting. This setting effectively redefines system xml-apis implementions.
Creating xml serializable classes
As of HPM 5.3 the correct way to use xml serialization is to implement DomPersistalbe2 interface (for serialization) and DomPersistalbe2 interface (for deserializazion). DomPersistableFactory implementations shell be contributed using com.heiler.ppm.xml.domPersistableFactories. We recommend to use internal classes for that.
In DomPersistable2#writeToDom( DomPersistWriteContext context, Element anchorElement ) implementation you can decide which fields will be serialized and in which form: as xml attribute or as a xml sub element. If any of the fields types you want to serialize already implements DomPersistable2 then the xml serializer will recognize it and will delegate serialization of this type to its writeToDom(...) method. Such approach helps to serialize complex object trees where every object is responsible only for serialization of its own data only. This feature is quite helpfull when your java objects contain references to platform objects which already support xml serialization (for example SearchParameters). During serialization platform is able to recognize same object instances and serialize them only once (this will save space and time). Xml representations of the duplicate objects will only contain references to the master object (in xml string this is called sys-id).
To serialize and deserialize java object you can use DomPersistUtils.toXml(Object,String) and DomPersistUtils.fromXml(String) methods.
public
class
MyConfig
implements
DomPersistable2
{
private
static
final
String XML_MY_NAME =
"myName"
;
private
static
final
String XML_ABOUT_ME =
"about"
;
private
static
final
String XML_FRIENDS =
"myFriendsSearch"
;
private
String myName;
private
String aboutMe;
private
SearchParameters myFriendsSearchParams;
public
MyConfig( String myName, String aboutMe, SearchParameters myFriendsSearchParams )
{
this
.myName = myName;
this
.aboutMe = aboutMe;
this
.myFriendsSearchParams = myFriendsSearchParams;
}
@Override
public
String getFactoryId()
{
DomPersistUtils.warnUnregistered( Factory.IDENTIFIER, Factory.
class
);
return
Factory.IDENTIFIER;
}
@Override
public
void
writeToDom( DomPersistWriteContext context, Element anchorElement )
throws
Exception
{
//same name as xml attribute
anchorElement.addAttribute( XML_MY_NAME,
this
.myName );
//save long text as a body of <about>...</about>
context.writeToDom(
this
.aboutMe, anchorElement.addElement( XML_ABOUT_ME ) );
//save search params under <myFriendsSearch>...</myFriendsSearch>
context.writeToDom(
this
.myFriendsSearchParams, anchorElement.addElement( XML_FRIENDS ) );
}
public
static
final
class
Factory
implements
DomPersistableFactory
{
public
static
final
String IDENTIFIER =
"hlr.me.MyConfig"
;
//$NON-NLS-1$
@Override
public
Object createFromDom( DomPersistReadContext context, Element anchorElement )
throws
Exception
{
String myName = DomPersistUtils.getAttributeValue( anchorElement, XML_MY_NAME,
true
,
null
);
Element aboutMeXmlElement = anchorElement.element( XML_ABOUT_ME );
String aboutMe = context.createFromDom( aboutMeXmlElement );
Element fredsSearchXmlElement = anchorElement.element( XML_FRIENDS );
SearchParameters searchParams = context.createFromDom( fredsSearchXmlElement );
return
new
MyConfig( myName, aboutMe, searchParams );
}
}
}
//contribution in plugin.xml
<extension
point=
"com.heiler.ppm.xml.domPersistableFactories"
>
<factory
class
=
"com.heiler.ppm.me.MyConfig$Factory"
id=
"hlr.me.MyConfig"
>
</factory>
</extension>
Serialize objects without DomPersistable2 interface.
You can serilize objects which do not implement DomPersistable2 interface by implementing DomPersister interface in a separate class and contributing it using persister element in the com.heiler.ppm.xml.domPersistableFactories extension point.
Deprecated interfaces
HPM platform also still contains two deprecated xml searilizazion machanisms. DomPersistable and PersistableElement interfaces. As well as corresponding extension point com.heiler.ppm.xml.elementFactories. You should not use these mechanisms, but you can still serialize these objects in a context of DomPersistable2 implementaions. For example you can simply call
public
void
writeToDom( DomPersistWriteContext context, Element anchorElement )
throws
Exception
{
...
Element e = anchorElement.addElement( XML_LEGACY );
context.writeToDom(
this
.oldDomPersistable, e );
...
}
add serlialization framework will delegate xml serializaion of this object to the deprecated (but still) working algorithm. So you can mix different xml serilization approaches in the same object tree. However this is highly unrecommended.
The main disadvatage of DomPersistable and PerstableElement aproaches is that they store full qualified class names. So if the class name has been changed between versions then deserialization of the old xml structures will be impossible because the class name is used by refelection on deserialization.
Migration to DomPersistable2
As a general rule you have to migrate your old DomPersistable implementaions to DomPersistable2 approach. Because you also need to support HPM versions migrations you need to follow some rules:
New DomPersistable2 implementaion of your class must use the same xml structure as the original DomPersistable implemention.
New DomPersistable2 implementaion must have the same full qualified name as the original DomPersistable implemention.
New DomPersistable2 implementaion must have an empty argument public constructor.
Data migration will happen automatically as soon as a cycle of read_domPersistable_format - save_in_domPersistable2_format will be sucessfully finished.
It is important to test new DomPersistable2 with old data from DomPersistable serialization. With tests you can be sure that migration with live data will not go wrong in production environment. For that you need to build test cases which take the old XML string and read it using new DomPersistable2 implementaion. Consider this as a required junit test case when you make refactoring of DomPersistable to DomPersistable2.