Inconsistent ClassCastException for XmlBeans.Factory parse method - java

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.

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).

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.

This answer 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

Related

Why is there a ClassLoader exception when there are 2 objects with similar type names

I have a class with 2 methods.
In method1(), I create a local record called Abc. This local record is only available to method1() because it was defined in method1() (here are the rules on it according to Java).
In method2(), I create a local interface called ABC. Notice that there is a capitalization difference here. The local record was named Abc, but this local interface is named ABC.
Here is the class.
//package ..... //commenting out package information, as I sincerely doubt that is the cause
/** There seems to be a class loader error when running the below method in main(). */
public class ClassLoaderIssue
{
/** Method 1. */
private void method1()
{
record Abc(int a)
{
public static String iDontCare()
{
return "ignore this method";
}
}
System.out.println(Abc.iDontCare()); //error
}
/** Method 2. */
private void method2()
{
interface ABC
{
}
}
/**
*
* Main method.
*
* #param args commandline arguments that we don't care about for this example.
*
*/
public static void main(String[] args)
{
new ClassLoaderIssue().method1();
}
}
And here is the Exception that I get.
Exception in thread "main" java.lang.NoClassDefFoundError: ClassLoaderIssue$1ABC (wrong name: ClassLoaderIssue$1Abc)
at java.base/java.lang.ClassLoader.defineClass1(Native Method)
at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012)
at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:150)
at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:862)
at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:760)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:681)
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:639)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
at ClassLoaderIssue.method1(ClassLoaderIssue.java:23)
at ClassLoaderIssue.main(ClassLoaderIssue.java:49)
And here is some relevant information.
Java compile command = C:\Program Files\Java\jdk-17.0.2\bin\javac.exe
Java run command = C:\Program Files\Java\jdk-17.0.2\bin\java.exe
Obviously, this is just a runnable example of the problem, but I have a situation where it is extremely convenient and helpful to have local class/enum/record to handle weird state at the very edges of my domain. Therefore, I use this pattern a lot, but this is the first time I have come across this issue.
Now obviously, there are many ways to go around this. The simplest being that I could just rename the enum for one. I already tried that and that worked. Same for the interface.
But why does this happen?
EDIT - I also tested this with Java 18 and got the same result. I used a brand new file, a brand new folder, and I copied and pasted the text (not the file), into an empty file with the same name. Same error. I can't really get a cleaner test than that.
On Windows, file names are case-insensitive. This means that files called Something$ABC.class and Something$Abc.class cannot be distinguished.
Meanwhile, of course, the language itself is case-sensitive, and expects the file name and the name of the class within the file to match.
Your only recourse seems to be to rename either the class or the interface.
I just compiled the code and ran it on my machine. It works without any problems.
Maybe try to delete all existing class files and recompile everything.
Otherwise you can try a different java version, I had similar problems related to that in the past.

Java Class cast exception, Classloader error on using javax xml providers?

I get the following error when I switched from OSGI Equinox to OSGI Karaf runtime.
Earlier the com.sun implementation was working fine, but now I need an apache implementation to run in one environment and com.sun in the older.
I know that OSGI has different class loading mechanism when compared to Java Class loading.
We are providing the javax packages as a part of the rt.jar which contains the Provider interface.
The implementation is coming from com.sun.xml
com.sun.xml.internal.ws.spi.ProviderImpl cannot be cast to javax.xml.ws.spi.Provider (loaded by org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8) (found matching super class javax.xml.ws.spi.Provider loaded by , but needed loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8)
at javax.xml.ws.spi.Provider.provider(Provider.java:43)
at javax.xml.ws.Service.<init>(Service.java:35)
at javax.xml.ws.Service.create(Service.java:126)
When I look at the logs, both class loaders seem to have the same ID, then why get a class cast exception?
javax.xml.ws.spi.Provider (loaded by org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8) (found matching super class javax.xml.ws.spi.Provider loaded by , but needed loader org.apache.felix.framework.BundleWiringImpl$BundleClassLoader#0x0000000100162fd8)
As of now, I am using a hack to get around this in two different environments
private static final String PROVIDER_CXF = "org.apache.cxf.jaxws.spi.ProviderImpl";
private static final String PROVIDER_DEFAULT = "com.sun.xml.internal.ws.spi.ProviderImpl";
private String setProvider() {
log.debug("set default provider");
System.setProperty(PROVIDER, PROVIDER_DEFAULT);
try {
Service.create(new QName(null, ""));
return PROVIDER_DEFAULT;
} catch (Exception e) {
log.debug("setting cxf provider");
System.setProperty(PROVIDER, PROVIDER_CXF);
return PROVIDER_CXF;
}
}
You may need to set -Dorg.osgi.framework.bootdelegation=com.sun.*. See https://osgi.org/specification/osgi.core/7.0.0/framework.module.html#framework.module.parentdelegation. It is possible Equinox provides a common, useful default for boot delegation while Karaf does not.

Java project that uses library throws NoClassDefFoundError when using project that uses the same library

I'm creating a Java library for using in other Java projects. The projects use Repast Symphony and my library does so too (so i'm afraid this error is being caused by some conflict). Everything builds fine, but when I run a the Repast simulation, it throws java.lang.NoClassDefFoundError: repast/simphony/context/Context
I tried exporting my library as a jar, importing the project directly and adding the library to my project's classpath, to no avail. What can I be doing wrong?
This Context class is being used in both my library and my projects. The following is a snippet of it use in two classes:
// MyContextBulder.java
// This file is in my project
// This class is called by Repast first
import repast.simphony.context.Context;
import repast.simphony.dataLoader.ContextBuilder;
import mylibrary.core.DF;
import mylibrary.core.DF.MyContext;
public class MyContextBuilder implements ContextBuilder<Object> {
#Override
public Context<Object> build(Context<Object> context) {
context.setId("test");
DF.setContext((MyContext) context);
// Create agent
new MyAgent();
// Add the agent to the Repast context.
// context.add(t);
return context;
}
}
// DF.java
// This file is in my library
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import org.apache.commons.collections15.Predicate;
import repast.simphony.context.Context;
import repast.simphony.context.ContextListener;
import repast.simphony.space.projection.Projection;
import repast.simphony.util.collections.IndexedIterable;
import repast.simphony.valueLayer.ValueLayer;
import mylibrary.Agent;
/**
* This static class provides the Directory Facilitator Service
* and is used to send messages to agents
* and to keep a directory of all agents in the application.
* Agents use the static method send(ACLMessage) to send a message
* to one or more agents. The ACLMessage object contains
* the receiver agent and the sender (so the receiver can reply back).
*
* This class needs to be setup initially before registering new agents.
* To do that, simply call setContext(...);
* #author joaolopes
*
*/
public class DF {
private static int lastAID = 0; // Just to help generate new identifiers
private static HashMap<Integer, Agent> agents; // Contains all agents
/**
* The Repast context that contains all
* scheduled Repast objects.
*/
private static MyContext context = null;
/**
* Registers the agent in the directory and returns its
* AID (freshly generated for it). If the agent is already
* registered, returns its current AID.
* #param agent The agent to be registered
* #return The AID generated for the agent.
*/
public static int registerAgent(Agent agent) {
// If this agent is already in the hashMap,
// just return its key.
if (getAgents().containsValue(agent)) {
return agent.getAID();
}
// Quick way to find a new ID for this agent
// that is not in use at the moment.
while (getAgents().containsKey(lastAID)) {
lastAID++;
}
// The agent must know their own ID.
agent.setAID(lastAID);
agents.put(lastAID, agent);
System.err.println(context.toString());
context.add(agent);
return lastAID;
}
public static void setContext(MyContext c){
context = c;
}
}
Editing to add relevant info from the comments:
I don't import the repast JAR directly in my projects as I do in my library. Repast Symphony is installed in Eclipse as a plugin, so I created "Repast Projects" that include all Repast libraries. Therefore, I'm unable to remove the specific JAR that is causing the possible conflict of classes.
Exactly as you said. This error should be the conflict between the same classes in a jar. If you are using an IDE try to clean the build and rebuild again.
And also I would suggest you to use only one symphony library jar. Multiple class definitions always leads to ambiguity for the JVM class loader.
Try not to use symphony jar in the importing project since you already have it in your exported jar. After importing your lib, there should no errors.
Try this and let me know how it goes.
I suggest that you use an build tool. Something like Maven. Then maven with the right plugin will fix this problem for you. All you need to do, is to tell Maven that you need a particular jar file. Then a magic will occur, and you will have a well working jar-file to distribute
java.lang.NoClassDefFoundError is thrown when the JVM tries to run the application. Typical cases is when you got one jar-file as "interface". Then you got other jar-file that implement that interface.
So what you need to do, is that have the Repast jar inside your jars classpath. So that your program can find the right class you want to use.

Cannot call method in red5 server from AS3 Flash CS6

Okay, this had been making me very mad. I've followed almost 8 tutorials all over the Internet and in the end, I got my Red5 server instance working. Good for me! But when I'm calling my Java methods in my Red5 apps from my AS3 apps, in the 'Console' window in Eclipse, I got this error :
[ERROR] [NioProcessor-1] org.red5.server.service.ServiceInvoker - Method getTheName with parameters [] not found in org.red5.core.Application#17e5fde
Here's my Application.java file.
package org.red5.core;
import org.red5.server.adapter.ApplicationAdapter;
import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.service.ServiceUtils;
/**
* Sample application that uses the client manager.
*
* #author The Red5 Project (red5#osflash.org)
*/
public class Application extends ApplicationAdapter {
/** {#inheritDoc} */
#Override
public boolean connect(IConnection conn, IScope scope, Object[] params) {
return true;
}
/** {#inheritDoc} */
#Override
public void disconnect(IConnection conn, IScope scope) {
super.disconnect(conn, scope);
}
public String getTheName() { return "MyName!"; }
}
And here's my AS3 code. I just put this on the Timeline.
var nc:NetConnection = new NetConnection();
nc.connect("http://localhost/Mintium/RoomHere", "SomeUsernameHere");
nc.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus);
nc.objectEncoding = ObjectEncoding.AMF0;
function onNetStatus(e:NetStatusEvent):void
{
switch (e.info.code)
{
case "NetConnection.Connect.Success" :
trace("connected");
nc.call("getTheName", new Responder(getName_result, getName_error));
break;
}
}
function getName_result(res:Object):void { append("Name : " + res.toString()); }
function getName_error(res:Object):void { append(res.toString()); }
Its been a week I've been trying to figure it out and my dateline is next month. If this stuff is not solved, I'm gonna fail my assessment. Please help me with my problems. Thank you very much.
Sorry I did not see this 2 months ago, I could have helped you pass your assessment. Nevertheless, I think I can answer this question, having had a similar problem calling Red5 services.
The key to solving this problem is in those parts of Red5 that utilize the Spring Framework. In your project, there should be a file called red5-web.xml that resides in the Server project's WEB-INF folder. This file contains some Bean dependencies used by Red5's Spring components. This is not mentioned in the tutorials that I read, or even in most of the (rather sparse and distributed) red5 programming documentation.
What you have to do is add a bean entry for your method in that file. In your case, the entry should look like this:
<bean id="getTheName.service" class="org.red5.core.Application" />
Note my use of the name of your function, with ".service" appended. I do not understand why, but you need the ".service" appended in order for Red5 to find your function. You need to add a similar entry for every class whose functions you want to use as services.
Of course, I based everything I said above on the fact that you put the service into the Application class -- something which I never do. if you read the red5-web.xml file, you will see that there is already an entry for that class, because it is already injected through Spring as the class that acts as an "endpoint" for processing requests over the web. I do not know if using the Application class as an endpoint and a provider of services is a good idea (it violates "separation of concerns" in OOP and may cause problems with Spring).
What I usually do is add a separate class in the org.red5.core package (or any other package you might want) that acts to deliver the desired service, then put an entry into red5-web.xml that injects the class and its method. So, for your project, lets assume you have a class called NameProvider in the org.red5.core package:
public class NameProvider
{
public NameProvider() {}
public String getTheName() { return("MyName!"); }
}
then you add the following entry to your red5-web.xml file:
<bean id="getTheName.service" class="org.red5.core.NameProvider" />
That should make everything work.
I hope this helps you in the future, or anyone else having this problem. I just wish I'd seen this question sooner.

Get javadoc version for a class?

I use the javadoc #version tag in my classes, however I am not sure how to get the version in the class itself. Let's look at an example...
/**
* #version 1.0a
* #author Redandwhite
*/
public class JClass extends JFrame implements ActionListener, KeyListener {
String version = ...
}
Is there a method/class that can retrieve the version? Ideally, it's as simple as an API call which returns a simple String.
The javadoc comments are not included in the generated byte code in any form, so there is no way to access the value of the #version tag from Java code (unless you parse the source code of course). There might be a version annotation that can be used to specify the class version instead of or in addition to the javadoc #version tag, and this annotation would be accessible via the Java reflection API (i.e. Class.getAnnotations()).
As it's already been noted, it's not possible to get to that information.
An alternative solution is to read the Implementation-Version/Specification-Version property of the package.
That value can be defined in the MANIFEST.MF file of a jar file. If it is defined, then it can be queried for every package that's contained in that jar file.
So if you have a class org.example.MyClass and the jar it comes in has the relevant entries in the MANIFEST.MF file, then you can get the version like this:
Class<?> clazz = org.example.MyClass.class
Package pkg = clazz.getPackage();
String version = pkg.getImplementationVersion();

Categories