I get the following exception when I want to cast one type to another.
java.lang.ClassCastException: org.paston.certification.data.impl.BRL6000
cannot be cast to org.paston.certification.data.Certification
BRL6000 extends the Certification. So in my understanding I should be able to cast a object of the type BRL6000 to the Certification type.
This is the code where the exception occurs.
Object certification = ch.getCertificationData(process, version);
Certification c = (Certification)certification;
Deploying
The application is deployed from Eclipse to a Tomcat 7 server. My application uses a few JARs from the Tomcat environment (e.g. Bonita_Server.jar).
My application is (in Eclipse) a dynamic web projects which references an other project (Certificationnl) which contains the classes Certification
and BRL6000
. Project Certificationnl is added to the webproject's WAR when I deploy the application to Tomcat.
Classes
The BRL6000 class
package org.paston.certification.data.impl;
import org.paston.certification.data.Certification;
import org.paston.certification.data.CertificationStep;
public class BRL6000 extends Certification{
/**
*
*/
public static final long serialVersionUID = -8215555386637513536L;
public static final String processName = "BRL6000";
}
Certification class
package org.paston.certification.data;
import java.util.ArrayList;
import java.util.List;
import org.ow2.bonita.facade.runtime.impl.AttachmentInstanceImpl;
public class Certification implements java.io.Serializable{
public enum Section{
ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN
}
/**
* SerializationVersionUID
*/
private static final long serialVersionUID = -5158236308772958478L;
}
getCertificationData
public Object getCertificationData(String process, String version) {
if (loginContext == null)
login();
System.out.println("Process: "+ process + " Version: "+ version);
ProcessDefinitionUUID pdu = new ProcessDefinitionUUID(process, version);
QueryRuntimeAPI queryRuntimeAPI = AccessorUtil
.getQueryRuntimeAPI();
try {
Set<ProcessInstance> processInstances = queryRuntimeAPI
.getProcessInstances(pdu);
if (processInstances.size() != 1)
System.out.println("Best number of instances is 1. Found: "
+ processInstances.size());
for (ProcessInstance processInstance : processInstances) {
Map<String, Object> variables = processInstance
.getLastKnownVariableValues();
if (((Boolean) variables.get("active")) == true) {
return variables.get("certification");
}
}
} catch (ProcessNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Update with code as a Servlet
package org.paston.certification.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.paston.certification.CertificationHandler;
import org.paston.certification.data.Certification;
import org.paston.certification.data.CertificationI;
/**
* Servlet implementation class SomeServlet
*/
public class SomeServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
/**
* @see HttpServlet#HttpServlet()
*/
public SomeServlet() {
super();
// TODO Auto-generated constructor stub
}
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
CertificationHandler ch = new CertificationHandler();
String process = request.getParameter("p");
String version = request.getParameter("v");
Object certification = ch.getCertificationData(process, version);
Class<?> clazz = certification.getClass();
while (clazz != null) {
System.out.println(clazz.getName());
clazz = clazz.getSuperclass();
}
Class c1 = certification.getClass().getSuperclass();
Class c2 = Certification.class;
System.out.println("c1 is " + c1 + ", c2 is " + c2);
System.out.println("c1 == c2 is " + (c1 == c2));
System.out.println("c1.equals(c2) is " + c1.equals(c2));
System.out.println("c1.getName().equals(c2.getName()) is "
+ c1.getName().equals(c2.getName()));
System.out.println("c1.getClassLoader() == c2.getClassLoader() is "
+ (c1.getClassLoader() == c2.getClassLoader()));
CertificationI c = (CertificationI) certification;
PrintWriter out = response.getWriter();
out.println("Hello World");
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
}
}
Console output of the Servlet:
Process: BRL6000 Version: 1.0 org.paston.certification.data.impl.BRL6000 org.paston.certification.data.Certification java.lang.Object c1 is class org.paston.certification.data.Certification, c2 is class org.paston.certification.data.Certification c1 == c2 is false c1.equals(c2) is false c1.getName().equals(c2.getName()) is true c1.getClassLoader() == c2.getClassLoader() is false
There is also one other issue that maybe hinting for the problem. Every 10 seconds I see the following Exception in the console:
May 07, 2013 2:09:45 PM org.apache.catalina.loader.WebappClassLoader loadClass
INFO: Illegal access: this web application instance has been stopped already. Could not load org.ow2.bonita.runtime.tx.StandardTransaction. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1566)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1526)
at org.ow2.bonita.util.ReflectUtil.loadClass(ReflectUtil.java:68)
at org.ow2.bonita.env.descriptor.ObjectDescriptor.construct(ObjectDescriptor.java:195)
at org.ow2.bonita.env.WireContext.construct(WireContext.java:521)
at org.ow2.bonita.env.WireContext.create(WireContext.java:498)
at org.ow2.bonita.env.WireContext.create(WireContext.java:484)
at org.ow2.bonita.env.WireContext.get(WireContext.java:456)
at org.ow2.bonita.env.WireContext.get(WireContext.java:343)
at org.ow2.bonita.env.WireContext.get(WireContext.java:746)
at org.ow2.bonita.env.BasicEnvironment.get(BasicEnvironment.java:151)
at org.ow2.bonita.env.BasicEnvironment.get(BasicEnvironment.java:142)
at org.ow2.bonita.util.EnvTool.getEnvClass(EnvTool.java:175)
at org.ow2.bonita.util.EnvTool.getTransaction(EnvTool.java:84)
at org.ow2.bonita.runtime.tx.StandardTransactionInterceptor.execute(StandardTransactionInterceptor.java:42)
at org.ow2.bonita.services.impl.EnvironmentInterceptor.execute(EnvironmentInterceptor.java:40)
at org.ow2.bonita.services.impl.RetryInterceptor.execute(RetryInterceptor.java:59)
at org.ow2.bonita.runtime.event.EventExecutorThread.run(EventExecutorThread.java:61)
Update 2
I came to understand the problem a little better. The Bonitaserver ( AccessorUtil
) does load the Certification object as well. It somewhere loads the classes from Certification.jar1620768823629427276.tmp
which the Bonitaserver created when a process was uploaded to the server.
Also, I found a class ReflectUtil
(link) which is probably used to load those classes.
What I tried is to load the classes at the start of the doGet for both this (the servlet) as the ClassLoader
of the AccessorUtil
. Both with the same old result.
ArrayList<String> classesNames = new ArrayList<String>();
classesNames.add("org.paston.certification.data.Certification");
classesNames.add("org.paston.certification.data.CertificationI");
classesNames.add("org.paston.certification.data.impl.BRL6000");
ClassLoader cl = this.getClass().getClassLoader();
Class<?>[] classes = ReflectUtil.loadClasses(cl, classesNames);
Update 3
Results of the following code proposed by @GaborSch. The code as I used it:
System.out.println("--- Test ClassLoader certification object---");
ClassLoader cl1 = certification.getClass().getSuperclass().getClassLoader();
while (cl1 != null) {
System.out.println(cl1.getClass().getCanonicalName() + " " + cl1.hashCode() + " " + cl1);
cl1 = cl1.getParent();
}
System.out.println("--- Test ClassLoader Certification class---");
ClassLoader cl2 = Certification.class.getClassLoader();
while (cl2 != null) {
System.out.println(cl2.getClass().getCanonicalName() + " " + cl2.hashCode() + " " + cl2);
cl2 = cl2.getParent();
}
Result of the code:
--- Test ClassLoader certification object---
org.ow2.bonita.runtime.ProcessClassLoader 451656
org.ow2.bonita.runtime.ProcessClassLoader@6e448
org.ow2.bonita.runtime.VirtualCommonClassloader 1182018350
org.ow2.bonita.runtime.VirtualCommonClassloader@46742b2e
org.apache.catalina.loader.StandardClassLoader 318536939
org.apache.catalina.loader.StandardClassLoader@12fc7ceb
sun.misc.Launcher.AppClassLoader 1667514408
sun.misc.Launcher$AppClassLoader@63644028
sun.misc.Launcher.ExtClassLoader 1253061906
sun.misc.Launcher$ExtClassLoader@4ab03512
--- Test ClassLoader Certification class---
org.apache.catalina.loader.WebappClassLoader 2136824254
WebappClassLoader context: /Certification delegate: false
repositories:
/WEB-INF/classes/
----------> Parent Classloader: org.apache.catalina.loader.StandardClassLoader@12fc7ceb
org.apache.catalina.loader.StandardClassLoader 318536939
org.apache.catalina.loader.StandardClassLoader@12fc7ceb
sun.misc.Launcher.AppClassLoader 1667514408
sun.misc.Launcher$AppClassLoader@63644028
sun.misc.Launcher.ExtClassLoader 1253061906
sun.misc.Launcher$ExtClassLoader@4ab03512
I suspect that the root of the problem lies in the runtime environment.
Your Certification
data ultimately come from AccessorUtil.getQueryRuntimeAPI()
, which is a static method run by Tomcat, so all object instances coming from it are likely to be loaded by the Tomcat classloader.
If you copy the jar file under Tomcat, it will load it with its own classloader. Your eclipse-run code although uses a different classloader, and if they have no common ancestor classloader where this Certification
class is loaded, they will be considered as a different Class.
I suggest to review your runtime class paths, remove libs from you Tomcat, or (worst case) run your code within Tomcat (e.g. as a Servlet in the same app).
Update:
Based on the project deployment description I believe that
AccessorUtil
class is loaded by a common class loaderCertification
instances from your Certificationnl
classes from your Tomcat deployment
AccessorUtil
class, but the class definitions are not accessible from your Eclipse code.It's a bad idea to access Tomcat-defined objects from outside on JVM-level. Either put your code into the same container, or use a properly-defined interface (e.g. web services).
If you want to stay with the current project setup, you could also use interfaces that are put into the common classloader (next to AccessorUtil
), and cast to the interface.
Update 2:
To check the class loader hierarchy, execute a code like this:
Class c1 = certification.getClass().getSuperclass().getClassLoader();
while (c1 != null) {
System.out.println(c1.getClass().getCanonicalName() + " " + c1.hashCode() + " " + c1);
c1 = c1.getParent();
}
Class c2 = Certification.class.getClassLoader();
while (c2 != null) {
System.out.println(c2.getClass().getCanonicalName() + " " + c2.hashCode() + " " + c2);
c2 = c2.getParent();
}
You can find the common ancestor by comparing the stack. The best way to go on is still debugging.
Update 3:
It is visible that your common classloader is org.apache.catalina.loader.StandardClassLoader
. On top of that
org.apache.catalina.loader.WebappClassLoader
is present for your webapp (this is where you want to cast to)org.ow2.bonita.runtime.VirtualCommonClassloader
and org.ow2.bonita.runtime.ProcessClassLoader
are present where the objects come from (that's where they're instantiated)It seems that Bonita is using some kind of classloading mechanism.
The solution is that you make the StandardClassloader
(or any other classloader) to load your classes. Since Certification
(and thus BRL6000
) are dependent to Bonita classes, you must put Bonita_Server.jar
into endorsed. See the Tomcat Classloader HOWTO, it may give you more insight.
Update 4:
To do the serializing/deserializing recommended in my comments, you can use this piece of code:
Certification certification = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(ch.getCertificationData(process, version));
oos.flush();
certification = (Certification) new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject();
} catch (IOException | ClassNotFoundException ex) {
}
Note that the (Certification)
cast is made to the currently loaded class.
Update 5:
The final resolution was not using direct Java class-level access, but rather using a proper API to do the same trick.
I think there are two possible explanations:
Certification
class in two different classloaders.Certification
classes that have names that just look the same.Here's what I suggest you do to figure out what is going on.
Before the statement that does (Certification) certification
insert the following:
// Get the classes that are being compared in the typecast.
Class c1 = certification.getClass().getSuperclass();
Class c2 = Certification.class;
System.out.println("c1 is " + c1 + ", c2 is " + c2);
System.out.println("c1 == c2 is " + c1 == c2);
System.out.println("c1.equals(c2) is " + c1 == c1.equals(c2));
System.out.println("c1.getName().equals(c2.getName()) is " +
c1.getName().equals(c2.getName()));
System.out.println("c1.getClassLoader() == c2.getClassLoader() is " +
c1.getClassLoader() == c2.getClassLoader());
Check that the first line gives (what looks to be) the same names for c1
and c2
. (If not, we've selected the wrong classes to compare. Tweak my code to get the right ones.)
The c1 == c2
and c2.equals(c2)
tests should give the same answer, and I predict it will be false
.
Comparing the names is the tests that distinguishes the two alternative explanations:
The final test will confirm that c1
and c2
do or do not have the same classloader.
This won't explain why your web application is in this state, but it will clearly tell you whether the problem is classloaders, funky class names ... or something else.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With