Reputation: 35
I know this is a very long shot, but I've been trying to figure it out for like two weeks already, so any idea pointing to the right direction could be priceless.
So, I have a very old application which uses XmlBeans. My task is to migrate from Tomcat 7.0.67 to Tomcat 8.5.11 introducing Spring Sessions and Spring Security instead of Realm-based authentication. Prior the migration everything was working fine both locally (MacOS, Oracle JDK 8) and on Heroku (Ubuntu, OpenJDK 8), but after the migration everything works on my local environment, but on Heroku, sometimes, when the app tries to parse a string to appropriate XmlBean, this ClassCastException occurs:
java.lang.ClassCastException: foo.bar.2.impl.PreferencesDocumentImpl cannot be cast to foo.bar.1.PreferencesDocument
at foo.bar.1.PreferencesDocument$Factory.parse(Unknown Source)
I have two auto-generated by XmlBeans classes, which were generated from two xsd-schemas without any namespace set. Classes share the name, but are located in different packages (parse method where the exception occurs is located in the Factory inner class, other methods are omitted):
/*
* An XML document type.
* Localname: Preferences
* Namespace:
* Java type: foo.bar.1.PreferencesDocument
*
* Automatically generated - do not modify.
*/
package foo.bar.1;
public interface PreferencesDocument extends org.apache.xmlbeans.XmlObject {
public static final org.apache.xmlbeans.SchemaType type = (org.apache.xmlbeans.SchemaType)
org.apache.xmlbeans.XmlBeans.typeSystemForClassLoader(PreferencesDocument.class.getClassLoader(), "schemaorg_apache_xmlbeans.system.s2D5798E4F4AFDA8394735C8512CDCBC7").resolveHandle("preferencesa8bfdoctype");
public static final class Factory {
public static foo.bar.1.PreferencesDocument parse(java.lang.String xmlAsString) throws org.apache.xmlbeans.XmlException {
return (foo.bar.PreferencesDocument) org.apache.xmlbeans.XmlBeans.getContextTypeLoader().parse( xmlAsString, type, null );
}
}
}
/*
* An XML document type.
* Localname: Preferences
* Namespace:
* Java type: foo.bar.1.PreferencesDocument
*
* Automatically generated - do not modify.
*/
package foo.bar.1.impl;
public class PreferencesDocumentImpl extends org.apache.xmlbeans.impl.values.XmlComplexContentImpl implements foo.bar.1.PreferencesDocument {
public PreferencesDocumentImpl(org.apache.xmlbeans.SchemaType sType) {
super(sType);
}
private static final javax.xml.namespace.QName PREFERENCES$0 = new javax.xml.namespace.QName("", "Preferences");
}
/*
* An XML document type.
* Localname: Preferences
* Namespace:
* Java type: foo.bar.2.PreferencesDocument
*
* Automatically generated - do not modify.
*/
package foo.bar.2;
public interface PreferencesDocument extends org.apache.xmlbeans.XmlObject {
public static final org.apache.xmlbeans.SchemaType type = (org.apache.xmlbeans.SchemaType)
org.apache.xmlbeans.XmlBeans.typeSystemForClassLoader(PreferencesDocument.class.getClassLoader(), "schemaorg_apache_xmlbeans.system.sC8953008EC716AA258D3951B84AB1CB7").resolveHandle("preferencesa8bfdoctype");
public static final class Factory {
public static foo.bar.2.PreferencesDocument parse(java.lang.String xmlAsString) throws org.apache.xmlbeans.XmlException {
return (foo.bar.2.PreferencesDocument) org.apache.xmlbeans.XmlBeans.getContextTypeLoader().parse( xmlAsString, type, null ); }
}
}
/*
* An XML document type.
* Localname: Preferences
* Namespace:
* Java type: foo.bar.2.PreferencesDocument
*
* Automatically generated - do not modify.
*/
package foo.bar.2.impl;
public class PreferencesDocumentImpl extends org.apache.xmlbeans.impl.values.XmlComplexContentImpl implements foo.bar.2.PreferencesDocument {
public PreferencesDocumentImpl(org.apache.xmlbeans.SchemaType sType) {
super(sType);
}
private static final javax.xml.namespace.QName PREFERENCES$0 =
new javax.xml.namespace.QName("", "Preferences");
}
}
Sometimes, when the app deployed to Heroku is restarted, the problem is gone, but after another restart it's back again.
According to this, the root cause is the absence of namespaces which leads to collision. But due to our requirements I can't add or change namespace of the xsds. So do you have any ideas why does it work locally with Tomcat 7, locally with Tomcat 8 and on Heroku with Tomcat 7, but doesn't work on Heroku with Tomcat 8?
Thanks in advance.
Upvotes: 1
Views: 3382
Reputation: 1863
This question appears at the top for the following class of errors as well so I am adding my answer here:
class org.apache.xmlbeans.impl.values.XmlComplexContentImpl cannot be cast to class <class you generated a schema from>
This is a very bizarre error, after all why would this cast even be happening at all? In any case, this was because I was using the maven-shade-plugin minimizeJar
feature and it was removing something (I do not know what yet), if I figure out what exactly I need to explicitly re-add I will update this answer, but for now I just disabled minimizeJar.
It seems it was removing the impl
part of the schema generation code.
Update: add this to the filters section of the shade plugin if you want to continue using minimizeJar:
<filter>
<artifact>org.apache.xmlbeans:xmlbeans</artifact>
<includes>
<include>**</include>
</includes>
</filter>
Update 2: it seems that the above does not work all the time, I am very confused as to what the new issue is here, but the point still stands that minimizeJar
is very volatile w.r.t dependencies
Upvotes: 2
Reputation: 35
It works on Tomcat 7 because of this.
Prior Tomcat 8 a Tomcat's ClassLoader was loading resources in alphabetical order. Our app was working only because of that.
It works locally on Tomcat 8 + MacOS, because Tomcat 8's classloader loads resources in an order provided by OS, which, in case of OSX, seems to be ordered.
Upvotes: 1
Reputation: 10328
I suspect the unpredictable nature of the problem (i.e. sometimes it does or does not happen after a restart) is due to the non-determinisitic nature of the JVM classloader. If you have two different versions of the same class, they will be loaded in a non-deterministic order.
In this case, it sounds like you have two different classes with the same name (am I correct?). Even though they are auto-generated, only one will win.
I think you must find a way to give the classes different names (or packages).
Upvotes: 0