/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.serialization;
import org.openmrs.ImplementationId;
import org.openmrs.Patient;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.converters.extended.DynamicProxyConverter;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
/**
* This serializer uses the xstream library to serialize and deserialize objects.
* <br>
* All classes are automatically aliased. So a serialization of the {@link Patient} class
* will not be:
* <code>
* <org.openmrs.Patient ...>
* <element
* ...
* </org.openmrs.Patient>
* </code>
* but instead will be:
* <code>
* <patient ...>
* <element
* ...
* </patient>
* </code>
*
*/
public class SimpleXStreamSerializer implements OpenmrsSerializer {
// cached xstream object
public XStream xstream = null;
/**
* Default Constructor
*
* @throws SerializationException
*/
public SimpleXStreamSerializer() throws SerializationException {
this(null);
}
/**
* Constructor that takes a custom XStream object
* @param customXstream
* @throws SerializationException
*/
public SimpleXStreamSerializer(XStream customXstream) throws SerializationException {
if (customXstream == null) {
xstream = new XStream();
} else {
this.xstream = customXstream;
}
xstream.registerConverter(new OpenmrsDynamicProxyConverter(), XStream.PRIORITY_VERY_HIGH);
//this is added to read the prior simpleframework-serialized values.
// TODO find a better way to do this.
this.xstream.useAttributeFor(ImplementationId.class, "implementationId");
}
/**
* Expose the xstream object, so that module can config with xstream as need
*
* @return xstream can be configured by module
*/
public XStream getXstream() {
return xstream;
}
/**
* @see OpenmrsSerializer#serialize(java.lang.Object)
* @should not serialize proxies
*/
@Override
public String serialize(Object o) throws SerializationException {
return xstream.toXML(o);
}
/**
* @see OpenmrsSerializer#deserialize(String, Class)
* @should not deserialize proxies
* @should ignore entities
*/
@Override
@SuppressWarnings("unchecked")
public <T extends Object> T deserialize(String serializedObject, Class<? extends T> clazz) throws SerializationException {
try {
return (T) xstream.fromXML(serializedObject);
}
catch (XStreamException e) {
throw new SerializationException("Unable to deserialize class: " + clazz.getName(), e);
}
}
/**
* An instance of this converter needs to be registered with a higher priority than the rest so
* that it's called early in the converter chain. This way, we can make sure we never get to
* xstream's DynamicProxyConverter that can deserialize proxies.
*
* @see <a href="http://tinyurl.com/ord2rry">this blog</a>
*/
private class OpenmrsDynamicProxyConverter extends DynamicProxyConverter {
OpenmrsDynamicProxyConverter() {
super(null);
}
@Override
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
throw new XStreamException("Can't serialize proxies");
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
throw new XStreamException("Can't deserialize proxies");
}
}
}