当我想将一种类型转换为另一种类型时,出现以下异常。

java.lang.ClassCastException: org.paston.certification.data.impl.BRL6000
cannot be cast to org.paston.certification.data.Certification

BRL6000扩展了认证。因此,据我所知,我应该能够将BRL6000类型的对象转换为Certification类型。

这是发生异常的代码。
Object certification = ch.getCertificationData(process, version);
Certification c = (Certification)certification;

部署

该应用程序从Eclipse部署到Tomcat 7服务器。我的应用程序使用了Tomcat环境中的一些JAR(例如Bonita_Server.jar)。

我的应用程序是(在Eclipse中)一个动态Web项目,该项目引用了另一个包含CertificationBRL6000类的项目(Certificationnl)。当我将应用程序部署到Tomcat时,Project Certificationnl已添加到Web项目的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是
org.paston.certification.data.Certification类,c2是类
org.paston.certification.data.Certification c1 == c2为假
c1.equals(c2)为假c1.getName()。equals(c2.getName())为true
c1.getClassLoader()== c2.getClassLoader()为假

还有另一个问题可能暗示了这个问题。每隔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)也会加载证书对象。它从某处上载Bonitaserver将进程上载到服务器时创建的Certification.jar1620768823629427276.tmp中的类。

此外,我发现了一个类ReflectUtil(link),该类可能用于加载这些类。

我尝试的是在doGet的开头将这两个类(servlet)加载为ClassLoaderAccessorUtil。两者具有相同的旧结果。
    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);

更新3

@GaborSch建议的以下代码的结果。我使用的代码:
    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();
    }

代码结果:
--- 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

最佳答案

我怀疑问题的根源在于运行时环境。

您的Certification数据最终来自Tomcat运行的静态方法AccessorUtil.getQueryRuntimeAPI(),因此来自它的所有对象实例都可能由Tomcat类加载器加载。

如果将jar文件复制到Tomcat下,它将使用自己的类加载器加载它。您的日食运行代码虽然使用了不同的类加载器,并且如果它们不具有加载此Certification类的公共祖先类加载器,则它们将被视为不同的类

我建议查看您的运行时类路径,从Tomcat中删除库,或者(最坏的情况)在Tomcat中运行代码(例如,作为同一应用程序中的Servlet)。

更新:

根据项目部署说明,我认为

  • AccessorUtil类由公共类加载器
  • 加载
  • 里面的列表由Tomcat部署
  • 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存在于您的Web应用程序中(这是您要将投射到的位置)
  • org.ow2.bonita.runtime.VirtualCommonClassloaderorg.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来完成相同的操作。

    07-24 09:23