Chapter 31. Marshalling
31.1. Marshalling
Marshalling is the process of converting Java objects into a format that is transferable over the wire. Unmarshalling is the reversal of this process where data read from a wire format is converted into Java objects.
Red Hat JBoss Data Grid uses marshalling and unmarshalling to:
- transform data for relay to other JBoss Data Grid nodes within the cluster.
- transform data to be stored in underlying cache stores.
31.2. About the JBoss Marshalling Framework
Red Hat JBoss Data Grid uses the JBoss Marshalling Framework to marshall and unmarshall Java POJO
s. Using the JBoss Marshalling Framework offers a significant performance benefit, and is therefore used instead of Java Serialization. Additionally, the JBoss Marshalling Framework can efficiently marshall Java POJO
s, including Java classes.
The Java Marshalling Framework uses high performance java.io.ObjectOutput
and java.io.ObjectInput
implementations compared to the standard java.io.ObjectOutputStream
and java.io.ObjectInputStream
.
31.3. Support for Non-Serializable Objects
A common user concern is whether Red Hat JBoss Data Grid supports the storage of non-serializable objects. In JBoss Data Grid, marshalling is supported for non-serializable key-value objects; users can provide externalizer implementations for non-serializable objects.
If you are unable to retrofit Serializable
or Externalizable
support into your classes, you could (as an example) use XStream to convert the non-serializable objects into a String that can be stored in JBoss Data Grid.
slows down the process of storing key-value objects due to the required `XML` transformations.
31.4. Hot Rod and Marshalling
In Remote Client-Server mode, marshalling occurs both on the Red Hat JBoss Data Grid server and the client levels, but to varying degrees.
All data stored by clients on the JBoss Data Grid server are provided either as a byte array, or in a primitive format that is marshalling compatible for JBoss Data Grid.
On the server side of JBoss Data Grid, marshalling occurs where the data stored in primitive format are converted into byte array and replicated around the cluster or stored to a cache store. No marshalling configuration is required on the server side of JBoss Data Grid.
At the client level, marshalling must have a
Marshaller
configuration element specified in the RemoteCacheManager configuration in order to serialize and deserialize POJOs.Due to Hot Rod’s binary nature, it relies on marshalling to transform POJOs, specifically keys or values, into byte array.
31.5. Configuring the Marshaller using the RemoteCacheManager
A Marshaller is specified using the marshaller
configuration element in the RemoteCacheManager, the value of which must be the name of the class implementing the Marshaller interface. The default value for this property is org.infinispan.commons.marshall.jboss.GenericJBossMarshaller
.
If developing your own custom marshaller, protect it from potential injection attacks by verifying that any class names read, before instantiating, are amongst the expected/allowed class names.
The following procedure describes how to define a Marshaller to use with RemoteCacheManager.
Define a Marshaller
Create a Configuration Builder
Create a ConfigurationBuilder and configure it with the required settings.
ConfigurationBuilder builder = new ConfigurationBuilder(); //... (other configuration)
Add a Marshaller Class
Add a Marshaller class specification within the Marshaller method.
builder.marshaller(GenericJBossMarshaller.class);
Alternatively, specify a custom Marshaller instance.
builder.marshaller(new GenericJBossMarshaller());
Start the RemoteCacheManager
Build the configuration containing the Marshaller, and start a new RemoteCacheManager with it.
Configuration configuration = builder.build(); RemoteCacheManager manager = new RemoteCacheManager(configuration);
At the client level, POJOs need to be either Serializable, Externalizable, or primitive types.
The Hot Rod Java client does not support providing Externalizer instances to serialize POJOs. This is only available for JBoss Data Grid Library mode.
31.6. Restricting Deserialization to Specific Java Classes
The Red Hat JBoss Data Grid server allows deserialization only for standard Java classes and primitives in addition to the Java classes that you specify in a whitelist.
Clients, on the other hand, can deserialize objects that belong to any Java class unless you restrict deserialization to specific classes. To do this, use the addJavaSerialWhiteList
method in the org.infinispan.client.hotrod.configuration.ConfigurationBuilder
class.
For example, to restrict deserialization to only Java classes with fully qualified names that contain either Person or Employee, specify the following configuration:
import org.infinispan.client.hotrod.configuration.ConfigurationBuilder; ... ConfigurationBuilder configBuilder = ... configBuilder.addJavaSerialWhiteList(".*Person.*", ".*Employee.*");
For information on configuring the deserialization whitelist in the JBoss Data Grid server, see Configuring the Deserialization Whitelist in the Administration and Configuration Guide.
31.7. Troubleshooting
31.7.1. Marshalling Troubleshooting
In Red Hat JBoss Data Grid, the marshalling layer and JBoss Marshalling in particular, can produce errors when marshalling or unmarshalling a user object. The exception stack trace contains further information to help you debug the problem.
Exception Stack Trace
java.io.NotSerializableException: java.lang.Object at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:857) at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:407) at org.infinispan.marshall.exts.ReplicableCommandExternalizer.writeObject(ReplicableCommandExternalizer.java:54) at org.infinispan.marshall.jboss.ConstantObjectTable$ExternalizerAdapter.writeObject(ConstantObjectTable.java:267) at org.jboss.marshalling.river.RiverMarshaller.doWriteObject(RiverMarshaller.java:143) at org.jboss.marshalling.AbstractMarshaller.writeObject(AbstractMarshaller.java:407) at org.infinispan.marshall.jboss.JBossMarshaller.objectToObjectStream(JBossMarshaller.java:167) at org.infinispan.marshall.VersionAwareMarshaller.objectToBuffer(VersionAwareMarshaller.java:92) at org.infinispan.marshall.VersionAwareMarshaller.objectToByteBuffer(VersionAwareMarshaller.java:170) at org.infinispan.marshall.VersionAwareMarshallerTest.testNestedNonSerializable(VersionAwareMarshallerTest.java:415) Caused by: an exception which occurred: in object java.lang.Object@b40ec4 in object org.infinispan.commands.write.PutKeyValueCommand@df661da7 ... Removed 22 stack frames
Messages starting with in object
and stack traces are read in the same way: the highest in object
message is the innermost one and the outermost in object
message is the lowest.
The provided example indicates that a java.lang.Object
instance within an org.infinispan.commands.write.PutKeyValueCommand
instance cannot be serialized because java.lang.Object@b40ec4
is not serializable.
However, if the DEBUG
or TRACE
logging levels are enabled, marshalling exceptions will contain toString()
representations of objects in the stack trace. The following is an example that depicts such a scenario:
Exceptions with Logging Levels Enabled
java.io.NotSerializableException: java.lang.Object ... Caused by: an exception which occurred: in object java.lang.Object@b40ec4 -> toString = java.lang.Object@b40ec4 in object org.infinispan.commands.write.PutKeyValueCommand@df661da7 -> toString = PutKeyValueCommand{key=k, value=java.lang.Object@b40ec4, putIfAbsent=false, lifespanMillis=0, maxIdleTimeMillis=0}
Displaying this level of information for unmarshalling exceptions is expensive in terms of resources. However, where possible, JBoss Data Grid displays class type information. The following example depicts such levels of information on display:
Unmarshalling Exceptions
java.io.IOException: Injected failue! at org.infinispan.marshall.VersionAwareMarshallerTest$1.readExternal(VersionAwareMarshallerTest.java:426) at org.jboss.marshalling.river.RiverUnmarshaller.doReadNewObject(RiverUnmarshaller.java:1172) at org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:273) at org.jboss.marshalling.river.RiverUnmarshaller.doReadObject(RiverUnmarshaller.java:210) at org.jboss.marshalling.AbstractUnmarshaller.readObject(AbstractUnmarshaller.java:85) at org.infinispan.marshall.jboss.JBossMarshaller.objectFromObjectStream(JBossMarshaller.java:210) at org.infinispan.marshall.VersionAwareMarshaller.objectFromByteBuffer(VersionAwareMarshaller.java:104) at org.infinispan.marshall.VersionAwareMarshaller.objectFromByteBuffer(VersionAwareMarshaller.java:177) at org.infinispan.marshall.VersionAwareMarshallerTest.testErrorUnmarshalling(VersionAwareMarshallerTest.java:431) Caused by: an exception which occurred: in object of type org.infinispan.marshall.VersionAwareMarshallerTest$1
In the provided example, an IOException
was thrown when an instance of the inner class org.infinispan.marshall.VersionAwareMarshallerTest$1
is unmarshalled.
In a manner similar to marshalling exceptions, when DEBUG
or TRACE
logging levels are enabled, the class type’s classloader information is provided. An example of this classloader information is as follows:
Classloader Information
java.io.IOException: Injected failue! ... Caused by: an exception which occurred: in object of type org.infinispan.marshall.VersionAwareMarshallerTest$1 -> classloader hierarchy: -> type classloader = sun.misc.Launcher$AppClassLoader@198dfaf ->...file:/opt/eclipse/configuration/org.eclipse.osgi/bundles/285/1/.cp/eclipse-testng.jar ->...file:/opt/eclipse/configuration/org.eclipse.osgi/bundles/285/1/.cp/lib/testng-jdk15.jar ->...file:/home/galder/jboss/infinispan/code/trunk/core/target/test-classes/ ->...file:/home/galder/jboss/infinispan/code/trunk/core/target/classes/ ->...file:/home/galder/.m2/repository/org/testng/testng/5.9/testng-5.9-jdk15.jar ->...file:/home/galder/.m2/repository/net/jcip/jcip-annotations/1.0/jcip-annotations-1.0.jar ->...file:/home/galder/.m2/repository/org/easymock/easymockclassextension/2.4/easymockclassextension-2.4.jar ->...file:/home/galder/.m2/repository/org/easymock/easymock/2.4/easymock-2.4.jar ->...file:/home/galder/.m2/repository/cglib/cglib-nodep/2.1_3/cglib-nodep-2.1_3.jar ->...file:/home/galder/.m2/repository/javax/xml/bind/jaxb-api/2.1/jaxb-api-2.1.jar ->...file:/home/galder/.m2/repository/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar ->...file:/home/galder/.m2/repository/javax/activation/activation/1.1/activation-1.1.jar ->...file:/home/galder/.m2/repository/jgroups/jgroups/2.8.0.CR1/jgroups-2.8.0.CR1.jar ->...file:/home/galder/.m2/repository/org/jboss/javaee/jboss-transaction-api/1.0.1.GA/jboss-transaction-api-1.0.1.GA.jar ->...file:/home/galder/.m2/repository/org/jboss/marshalling/river/1.2.0.CR4-SNAPSHOT/river-1.2.0.CR4-SNAPSHOT.jar ->...file:/home/galder/.m2/repository/org/jboss/marshalling/marshalling-api/1.2.0.CR4-SNAPSHOT/marshalling-api-1.2.0.CR4-SNAPSHOT.jar ->...file:/home/galder/.m2/repository/org/jboss/jboss-common-core/2.2.14.GA/jboss-common-core-2.2.14.GA.jar ->...file:/home/galder/.m2/repository/org/jboss/logging/jboss-logging-spi/2.0.5.GA/jboss-logging-spi-2.0.5.GA.jar ->...file:/home/galder/.m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar ->...file:/home/galder/.m2/repository/com/thoughtworks/xstream/xstream/1.2/xstream-1.2.jar ->...file:/home/galder/.m2/repository/xpp3/xpp3_min/1.1.3.4.O/xpp3_min-1.1.3.4.O.jar ->...file:/home/galder/.m2/repository/com/sun/xml/bind/jaxb-impl/2.1.3/jaxb-impl-2.1.3.jar -> parent classloader = sun.misc.Launcher$ExtClassLoader@1858610 ->...file:/usr/java/jdk1.5.0_19/jre/lib/ext/localedata.jar ->...file:/usr/java/jdk1.5.0_19/jre/lib/ext/sunpkcs11.jar ->...file:/usr/java/jdk1.5.0_19/jre/lib/ext/sunjce_provider.jar ->...file:/usr/java/jdk1.5.0_19/jre/lib/ext/dnsns.jar ... Removed 22 stack frames