1 Jun 2011

java.io.InvalidClassException: NoSuchCustomerException; local class incompatible: stream classdesc serialVersionUID = 20110530114741


While working on a camel demo that routes a soap message in POJO mode from a cxf-consumer endpoint via jms to a camel processor, I ran into the above exception when the camel processor returned a soap fault. The full error reads [1].

The Camel route definition is as simple as


<route id="CXF-to-Queue">
<from uri="cxf:bean:customer-ws?dataFormat=POJO"/>
<inOut uri="activemq:queue:lookupCustomer?jmsMessageType=Object&transferException=true"/>
</route>

<route id="Queue-to-Processor">
<from uri="activemq:queue:lookupCustomer?jmsMessageType=Object&transferException=true" />
<process ref="lookupCustomer"/>
</route>



I ran each Camel route on a different machine with both routes connecting to the same broker. Further all msgs were passed as ObjectMessages in JMS (but you would get the same issue with JMS ByteMessages or any binary serialization of the exception).
The SOAP fault (raised by the lookupCustomer processor) was marshaled into the JMS ObjectMessage correctly and sent back on the reply-to queue, but the receiving camel-jms endpoint (my first route) had problems unmarshaling the data and raised the InvalidClassException as shown above.

After some digging I got to learn that since I re-compiled my demo on the other machine, a new and different serialVersionUID was generated for NoSuchCustomerException.java:


@WebFault(name = "NoSuchCustomer",
targetNamespace = "http://demo.fusesource.com/wsdl/CustomerService/")
public class NoSuchCustomerException extends Exception {
public static final long serialVersionUID = 20110530174707L;
...
}



This serial version uid is generated based on a time stamp by default! So each time wsdl2java is re-run, a different uid will be generated. This is often the case with mvn based projects. As I compiled my demo on both machines (at different times), the uid differed.

There was an easy fix to it however, which is to tell the wsdl2java compiler to generate the serial version uid based on the fully qualified classname (-useFQCNForFaultSerialVersionUID). This will always generate the same uid for the same fault definition no matter how often I run wsdl2java. See http://cxf.apache.org/docs/wsdl-to-java.html for more information.

IMHO, -useFQCNForFaultSerialVersionUID should be the default in order to avoid such problems.


You won't have this problem when using SOAP/HTTP as the transport. The JAXB marshaling won't marshal the uid.
This problem only arises when using binary transports such as JMS.



[1] full error
org.apache.camel.RuntimeCamelException: Failed to extract body due to: javax.jms.JMSException: Failed to build body from bytes. Reason: java.io.InvalidClassException: com.fusesource.demo.wsdl.customerservice.NoSuchCustomerException; local class incompatible: stream classdesc serialVersionUID = 20110530114741, local class serialVersionUID = 20110530112224. Message: ActiveMQObjectMessage {commandId = 61, responseRequired = false, messageId = ID:nbwfhtmielke-1657-1306750363546-3:1:1:1:4, originalDestination = null, originalTransactionId = null, producerId = ID:nbwfhtmielke-1657-1306750363546-14:1:1:1, destination = queue://reply.test1, transactionId = null, expiration = 0, timestamp = 1306755695062, arrival = 0, brokerInTime = 1306755696022, brokerOutTime = 1306755696678, correlationId = ID-XPS-53828-1306755653494-0-6, replyTo = queue://reply.test1, persistent = true, type = null, priority = 4, groupID = null, groupSequence = 0, targetConsumerId = null, compressed = false, userID = null, content = org.apache.activemq.util.ByteSequence@ed92dbb, marshalledProperties = org.apache.activemq.util.ByteSequence@5449579a, dataStructure = null, redeliveryCounter = 0, size = 0, properties = {Content_HYPHEN_Type=text/xml;charset=UTF-8, operationNamespace=http://demo.fusesource.com/wsdl/CustomerService/, operationName=lookupCustomer, Host=localhost:10443, SOAPAction="http://www.example.org/CustomerService/lookupCustomer", User_HYPHEN_Agent=Jakarta Commons-HttpClient/3.1, CamelJmsDeliveryMode=2, accept_HYPHEN_encoding=gzip,deflate}, readOnlyProperties = true, readOnlyBody = true, droppable = false}
at org.apache.camel.component.jms.JmsBinding.extractBodyFromJms(JmsBinding.java:158)[camel-jms-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.apache.camel.component.jms.JmsMessage.createBody(JmsMessage.java:183)[camel-jms-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.apache.camel.impl.MessageSupport.getBody(MessageSupport.java:41)[camel-core-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.apache.camel.component.jms.reply.ReplyManagerSupport.processReply(ReplyManagerSupport.java:112)[camel-jms-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.apache.camel.component.jms.reply.TemporaryQueueReplyHandler.onReply(TemporaryQueueReplyHandler.java:52)[camel-jms-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.apache.camel.component.jms.reply.PersistentQueueReplyHandler.onReply(PersistentQueueReplyHandler.java:45)[camel-jms-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.apache.camel.component.jms.reply.PersistentQueueReplyManager.handleReplyMessage(PersistentQueueReplyManager.java:84)[camel-jms-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.apache.camel.component.jms.reply.ReplyManagerSupport.onMessage(ReplyManagerSupport.java:98)[camel-jms-2.5.0-fuse-00-00.jar:2.5.0-fuse-00-00]
at org.springframework.jms.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:560)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.jms.listener.AbstractMessageListenerContainer.invokeListener(AbstractMessageListenerContainer.java:498)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.jms.listener.AbstractMessageListenerContainer.doExecuteListener(AbstractMessageListenerContainer.java:467)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.doReceiveAndExecute(AbstractPollingMessageListenerContainer.java:325)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.jms.listener.AbstractPollingMessageListenerContainer.receiveAndExecute(AbstractPollingMessageListenerContainer.java:263)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.invokeListener(DefaultMessageListenerContainer.java:1058)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.executeOngoingLoop(DefaultMessageListenerContainer.java:1050)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at org.springframework.jms.listener.DefaultMessageListenerContainer$AsyncMessageListenerInvoker.run(DefaultMessageListenerContainer.java:947)[spring-jms-3.0.5.RELEASE.jar:3.0.5.RELEASE]
at java.lang.Thread.run(Thread.java:636)[:1.6.0_20]