Hi,

I was encountering a problem this week that I luckily managed to solve. However, I thought it might be useful to share my knowledge about this on the mailing list. The problem involves an AxisFault in my web service, which was apparently caused by a NoClassDefFoundError. For the impatient: the short solution is, to always define "private static final long serialVersionUID" to a meaningful value.
For anyone who's interested, this is what happened:

System:
- axis2 1.4 (also used 1.3)
- Java JDK 1.6 update 6
- tomcat 6.0.16, with axis2.war running as a web application
- linux (some old fedora version with kernel 2.4.20-31.9)

I am testing axis2 for a scenario where clients pass TV metadata via SOAP to a web server, which collates it and hopefully does interesting things with it. On the server side I need to convert from the databinding classes to our own custom Java classes, because further encoding steps depend on them. The converter class ADBBean2TVAnytimeAPI was put into a larger converter library, which had to be linked against some of the axis2 jars and some of the code that was generated from the WSDL. A colleague had started this project and written the WSDL and also decided to generate and compile client-side and server-side code independently into different directories (autogen/client and autogen/service). Both directories would contain certain common files that were needed on both sides (most importantly, the ADBBeans). The client-side JAR file was used by the converter library.

I implemented the additional server-side code that would contain the actual application logic (for which the new converter class was needed). Then I deployed the compiled code as an axis2 service into tomcat. When I tried to send data from the client to the server, it would fail on the server side with the following error:

[ERROR] tva/cri/ResultType
java.lang.NoClassDefFoundError: tva/cri/ResultType
at bbc.rd.tvadb.converter.ADBBean2TVAnytimeAPI.convert(ADBBean2TVAnytimeAPI.java:90) at bbc.rd.tvadb.converter.ADBBean2TVAnytimeAPI.convert(ADBBean2TVAnytimeAPI.java:55)
   at bbc.rd.tvadb.server.database.TimedResult.<init>(TimedResult.java:72)
at bbc.rd.tvadb.server.database.ServerCRIDatabase.add(ServerCRIDatabase.java:161) at bbc.rd.tvadb.server.database.ServerCRIDatabase.update(ServerCRIDatabase.java:134) at uk.co.bbc.rd.metadataInterchangeService.MetadataInterchangeServiceSkeleton.CRI_Resolutions_Operation(MetadataInterchangeServiceSkeleton.java:765) at uk.co.bbc.rd.metadataInterchangeService.MetadataInterchangeServiceMessageReceiverInOut.invokeBusinessLogic(MetadataInterchangeServiceMessageReceiverInOut.java:84) at org.apache.axis2.receivers.AbstractInOutMessageReceiver.invokeBusinessLogic(AbstractInOutMessageReceiver.java:40) at org.apache.axis2.receivers.AbstractMessageReceiver.receive(AbstractMessageReceiver.java:100)
   at org.apache.axis2.engine.AxisEngine.receive(AxisEngine.java:176)
at org.apache.axis2.transport.http.HTTPTransportUtils.processHTTPPostRequest(HTTPTransportUtils.java:275) at org.apache.axis2.transport.http.AxisServlet.doPost(AxisServlet.java:131)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
   at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286) at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:856) at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:565) at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1509)
   at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.ClassNotFoundException: tva.cri.ResultType
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1360) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1206)
   at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
   ... 26 more

I wasn't sure what to make of this first, as the server-side code that was packaged in the axis2.war DID contain all the necessary classes. Also, it had worked before. I was sure that it wasn't a classpath issue (although the error sounds like it was). The only change was, that I now used the converter mentioned above. It became clear to me, that the classes (ResultType and others) used by the converter (it was built using the client-side JAR file!) and those used on the server side were incompatible. Which is strange, as they were not only created using the very same WSDL file, the source files (they are stored in different directories) were also identical. The only possible difference was, that, because these automatically generated classes both didn't define the serialVersionUID, it would be generated differently by the compiler.

I had a look on this mailing list and found that there had been a discussion about the serialVersionUID before:
http://marc.info/?l=axis-user&m=112180252016164&w=2
There it was suggested that setting it explicitly was not necessary, as Java would create it automatically based on the class content. Changes in class members would automatically cause a different version number. Note, that I compiled everything on the same machine, some java, ant, axi2, etc... version. The java code was identical. So in theory, the auto-generated serialVersionUID should be the same, too. At least that's what I thought. To test this assumption, I added the following, respectively, to my ant targets generate.client and generate.service in the common build.xml:
       <replace dir="${build.service.dir}/src"
                token="implements org.apache.axis2.databinding.ADBBean{"
value="implements org.apache.axis2.databinding.ADBBean{ ${serialVersionUID}">
           <include name="**/*.java"/>
       </replace>
...
       <replace dir="${build.client.dir}/src"
                token="implements org.apache.axis2.databinding.ADBBean{"
value="implements org.apache.axis2.databinding.ADBBean{ private static final long serialVersionUID=${version.serialVersionUID}L;">
           <include name="**/*.java"/>
       </replace>

The serial version number was derived from our version number for the web service itself (the date of the last modification to the WSDL file is used). The code above effectively sets the serialVersionUID in all classes that are derived from ADBBean. It's not nice, maybe, but after compiling everything my web service worked again as expected.

Lessons learned:
- Java seems to be inconsistent with its automatic generation of serialVersionUIDs. So don't rely on any assumptions! - Java errors are sometimes a bit misleading. Why did it not say that the class definitions were incompatible? - maybe adding an option to wsdl2java, that creates a serialVersionUID, should be considered?
- don't use duplicate copies of your code!

I hope that this helps anyone who has similar problems. Although I have to admit that, looking at it now, my problem looks like a very rare and unlikely situation...

Peter

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to