2

I'm getting a class cast exception when trying to marhsal Java object into a string. I've included JAXB-2.1 jar in my lib folder. When deploying to WAS, I've changed the classloader strategy to parent last so that the jar in my local library will be picked up first. But this is still throwing classcast exception with the following message. What is the reason for this error?

javax.xml.bind.JAXBException: ClassCastException: attempting to cast jar:file:/opt/was7/base/crm/java/jre/lib/rt.jar!/javax/xml/bind/JAXBContext.class to wsjar:file:/prod/wesadm/wes/was7/base/profiles/sadasd/installedApps/asdadad/myapp.ear/myapp_war.war/WEB-INF/lib/jaxb-api-2.1.jar!/javax/xml/bind/JAXBContext.class.  Please make sure that you are specifying the proper ClassLoader.
        at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:96)
        at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:214)
        at javax.xml.bind.ContextFinder.find(ContextFinder.java:372)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:574)
        at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:522)
        at com.my.MyClass.convertObjectToXML()

This is the convertObjectToXML() method.

private <T> String convertObjectToXMLString(T obj) throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = jaxbContext.createMarshaller();

        StringWriter sw = new StringWriter();
        marshaller.marshal(obj, sw);

        return sw.toString();

    }

This logic works fine when deployed to Tomcat. I'm unable to figure out why WAS jars are being picked up even though I mentioned the class loader as parent last.

RKodakandla
  • 3,318
  • 13
  • 59
  • 79
  • did you check the policy in you server, if the policy is single then the server-level mode will be forced on all your application if the policy is multiple then each app will have its own mode https://www.ibm.com/support/knowledgecenter/was_beta/com.ibm.websphere.base.doc/ae/crun_classload.html%23crun_classload__crun_classload_modes – achabahe May 05 '16 at 16:11

3 Answers3

1

It seems you bundled the JAXB api jars with your app. remove them from your app and it will work. Those jars are already bundled with WebSphere (in fact they are part of the JRE) and it causes classloader exception because two versions of the same classes are present

titou10
  • 2,814
  • 1
  • 19
  • 42
  • This will likely solve the problem, but it doesn't really explain why the JAXB API in the application failed to load a corresponding JAXB implementation from the application. Perhaps OP only had the API JAR in the application... – Brett Kail May 06 '16 at 05:41
  • JAXB classes are part of the JRE so it is a bad idea to override JRE classes like this. The official way to override JRE classes is to put the jars in the \lib\endorsed folder. you *may* (to be verified) override those classes by setting the "javax.xml.bind.JAXBContext" property, include the jars in your app and inverse the class loading order for your app (parent first). But I'm not sure this will work.. Why it works in Tomcat and not in WAS? it is due to a different way to handle class loading in both products – titou10 May 06 '16 at 12:24
  • WebSphere does not support overriding the JAXB API that is included with the JVM, and it does not support setting javax.xml.bind.JAXBContext. That's because the product itself uses JAXB internally, and overriding the defaults can cause problems. Presumably Tomcat does not use JAXB itself. Ultimately, overriding JAXB is fragile, and it's probably best to avoid it altogether. – Brett Kail May 06 '16 at 13:55
1

I was getting below exception while migrating app to Websphere 9.

java.lang.Exception: javax.xml.bind.JAXBException: ClassCastException: attempting to cast jar:file:/D:/WebSphere/AppServer/endorsed_apis/jaxb-api.jar!/javax/xml/bind/JAXBContext.class to wsjar:file:/D:/sites/XXXX.ear/Web.war/WEB-INF/lib/jaxb-api-2.2.10.jar!/javax/xml/bind/JAXBContext.class.  Please make sure that you are specifying the proper ClassLoader.    

I excluded all the references of the jars from application which are part of IBM jdk now. e.g. stax-api

    <dependency>
        <groupId>org.apache.xmlbeans</groupId>
        <artifactId>xmlbeans</artifactId>
        <version>2.6.0</version> 
        <exclusions>
            <exclusion>
                <groupId>stax</groupId>
                <artifactId>stax-api</artifactId>
            </exclusion>
            <exclusion>
                <groupId>xml-apis</groupId>
                <artifactId>xml-apis</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
0

The javax.xml.bind.ContextFinder.find(Class[] classes, Map properties) will look for jaxb.property first. If not found, it try to search/create a factory in following sequence:

  1. System property javax.xml.bind.context.factory
  2. Lookup using OSGi ServiceLoader
  3. Search META-INF services

I believe your program does not offer jaxb.property in package com.my. So the factory class is loading from Websphere JDK/library, it will return a object of JAXBContext class. But that class is different than the one in your WAR, so javax.xml.bind.ContextFinder throw ClassCastException.

Beck Yang
  • 3,004
  • 2
  • 21
  • 26
  • Your description is correct, however my war file has a osgi wrapper for the `javax.xml.bind.*` classes. Now, the question would be: What are my options if I need to ship my war file with my own version for `javax.xml.bind.*`? – lealceldeiro Nov 07 '19 at 16:21