当我想将一种类型转换为另一种类型时,我得到以下异常。
java.lang.ClassCastException: org.paston.certification.data.impl.BRL6000
cannot be cast to org.paston.certification.data.Certification
BRL6000扩展了认证。因此,根据我的理解,我应该能够将BRL6000类型的对象转换为认证类型。
这是发生异常的代码。
Object certification = ch.getCertificationData(process, version);
Certification c = (Certification)certification;
部署
应用程序从Eclipse部署到Tomcat 7服务器。我的应用程序使用Tomcat环境中的一些JAR(例如Bonita_Server.jar)。
我的应用程序(在Eclipse中)是一个动态Web项目,它引用了包含类Certification
和BRL6000
的其他项目(Certificationnl)。当我将应用程序部署到Tomcat时,Project Certificationnl被添加到webproject的WAR中。
类
BRL6000类
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";
}
认证课程
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;
}
使用代码作为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
}
}
Servlet的控制台输出:
流程:BRL6000版本:1.0 org.paston.certification.data.impl.BRL6000 org.paston.certification.data.Certification java.lang.Object c1是 class org.paston.certification.data.Certification,c2是class org.paston.certification.data.Certification c1 == c2为false c1.equals(c2)为false c1.getName()。equals(c2.getName())为true c1.getClassLoader()== c2.getClassLoader()为false
还有一个问题可能暗示这个问题。每隔10秒我在控制台中看到以下异常:
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)
更新2
我开始明白这个问题了。 Bonitaserver(AccessorUtil
)也会加载Certification对象。它在某处加载来自Certification.jar1620768823629427276.tmp
的类,Bonitaserver在将进程上传到服务器时创建了这些类。
另外,我找到了一个类ReflectUtil
(link),它可能用于加载这些类。
我尝试的是在doGet的开头为这个(servlet)加载类作为ClassLoader
的{{1}}。两者都有相同的旧结果。
AccessorUtil
更新3
@GaborSch提出的以下代码的结果。我用它的代码:
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);
代码结果:
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();
}
答案 0 :(得分:7)
我怀疑问题的根源在于运行时环境。
您的Certification
数据最终来自AccessorUtil.getQueryRuntimeAPI()
,这是Tomcat运行的静态方法,因此来自它的所有对象实例都可能由Tomcat类加载器加载。
如果您在Tomcat下复制jar文件,它将使用自己的类加载器加载它。您的eclipse运行代码虽然使用了不同的类加载器,但如果它们没有加载此Certification
类的共同祖先类加载器,则将被视为不同的类。
我建议检查你的运行时类路径,从你的Tomcat中删除libs,或者(最糟糕的情况)在Tomcat中运行你的代码(例如在同一个应用程序中作为Servlet)。
<强>更新强>
基于项目部署描述,我相信
AccessorUtil
类由公共类加载器加载Certification
类的Certificationnl
个实例填充 AccessorUtil
类访问这些对象,但无法从Eclipse代码访问类定义。在JVM级别从外部访问Tomcat定义的对象是个坏主意。将代码放入同一容器中,或使用正确定义的接口(例如Web服务)。
如果您想继续使用当前项目设置,您还可以使用放入公共类加载器(AccessorUtil
旁边)的接口,并转换为接口。
更新2:
要检查类加载器层次结构,请执行如下代码:
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();
}
您可以通过比较堆栈找到共同的祖先。最好的方法仍然是调试。
更新3:
可见,您的常用类加载器为org.apache.catalina.loader.StandardClassLoader
。最重要的是
org.apache.catalina.loader.WebappClassLoader
存在于您的网络应用中(这是您希望强制转换为的地方)org.ow2.bonita.runtime.VirtualCommonClassloader
和org.ow2.bonita.runtime.ProcessClassLoader
出现在对象来自的地方(它们被实例化的地方)似乎 Bonita 正在使用某种类加载机制。
解决方案是您使StandardClassloader
(或任何其他类加载器)加载您的类。由于Certification
(以及BRL6000
)依赖于Bonita类,因此您必须将Bonita_Server.jar
放入背书。请参阅Tomcat Classloader HOWTO,它可能会为您提供更多见解。
更新4:
要进行评论中推荐的序列化/反序列化,您可以使用以下代码:
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) {
}
请注意,(Certification)
强制转换为当前加载的类。
更新5:
最终解决方案不使用直接Java类级别访问,而是使用适当的API来执行相同的操作。
答案 1 :(得分:3)
我认为有两种可能的解释:
Certification
类。Certification
类,其名称看起来相同。以下是我建议你弄清楚发生了什么。
在(Certification) certification
语句插入以下内容之前:
// 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());
检查第一行是否为c1
和c2
提供了相同的名称(看起来是什么)。 (如果没有,我们选择了错误的类进行比较。调整我的代码以获得正确的类。)
c1 == c2
和c2.equals(c2)
测试应该给出相同的答案,我预测它会是false
。
比较名称是区分两种替代解释的测试:
最终测试将确认c1
和c2
是否具有相同的类加载器。
这不会解释为什么您的网络应用程序处于此状态,但它会清楚地告诉您问题是类加载器,时髦的类名称还是别的。