通过JMX访问用户定义的类时出现ClassCastException

时间:2012-03-16 21:26:02

标签: jmx classcastexception

我正在通过JMX监视在Tomcat中运行的servlet,以及在Tomcat的同一个实例中运行的另一个servlet。当我设置get()以返回标准Java数据类型(String,int,byte []等)时,这很好。但是当我使用用户定义的类时,我得到一个ClassCastException,它给出了这条消息:

java.lang.ClassCastException: blah.My_UserDefinedClass cannot be cast to blah.My_UserDefinedClass

我很确定这是因为检测和管理层(分别是受监控的 servlet和监控 servlet)中的不同类加载器。我已经仔细检查了包含每个servlet的用户定义类的.jar文件,并且两个jar文件彼此相同。

我正在使用标准MBean,并设置了受监控的servlet以返回此属性:

public interface MyMonitorMBean
{
    public My_UserDefinedClass getAllData();
}

实现:

public class MyMonitor implements MyMonitorMBean
{
    private My_UserDefinedClass mAllData;

    @Override
    public My_UserDefinedClass getAllData()
    {
        return mAllData;
    }   
}

管理servlet中用于访问此数据的代码:

private void getAllDataFromMBean()
{
    try
    {
        // this line generates the ClassCastException
        My_UserDefinedClass allData = (My_UserDefinedClass)mMBS.getAttribute( mObjectName, "AllData" );
    }
    catch( Exception e )
    {
    }
}

虽然每次发送标准Java数据类型都可以生成多个get(),但我想构建/使用我自己的POD / POJO类,它封装了所有各种标准位和bob(标准Java数据类型)我想发回去,以便我可以在一次通话中获取我的数据。

有什么想法吗?

谢谢,

比尔

使用tomcat7,java6,windows xp,32bit

1 个答案:

答案 0 :(得分:0)

这可能对你有用。您需要将[临时]线程的上下文类加载器设置为MBean的类加载器。您可以通过从MBeanServer请求类加载器实例来确定这一点。

private void getAllDataFromMBean()
{
    try
    {
        final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
        try {
            ClassLoader mbeanClassLoader = mMBS.getClassLoaderFor(mObjectName);
            Thread.currentThread().setContextClassLoader(mbeanClassLoader);
            My_UserDefinedClass allData = (My_UserDefinedClass)mMBS.getAttribute( mObjectName, "AllData" );
            // Process allData here
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
    }
    catch( Exception e )
    {
    }
}

====更新====

还有一个密切相关的问题在这里得到了更全面的回答:ClassCastException when casting to the same class

====更新====

我对你的确切设置并不完全清楚,但不管怎样,你肯定有一个类加载器问题,在运行时,你有[至少]两个不同的类加载器加载了My_UserDefinedClass。

这些是一些选择:

<强>生命周期

确保定义getAllDataFromMBean的类在同一个类加载器中进行了类加载,因此只有一个类加载器。 (可能很棘手)

或者,在加载之前,将包含该类的jar放入系统类路径中。 (即引导类路径或系统类路径)

<强>反射

这里有类加载器的核心问题,因为你的 getAllDataFromMBean 方法在一个类中,只要加载就会触发一个My_UserDefinedClass的加载,所以正确蝙蝠,你有两个不同的类加载器,因此不兼容的类型。因此,由于底层值都是标准java类型,因此您可以反射性地访问该实例。

**一般化**

不要为您的值创建用户定义的类,而是将它们保存在名称/值映射中。这样,传递的所有数据都是核心java类型。

**外化**

如果您强制将用户定义的实例序列化(比如一个byte []),然后在读取端反序列化它,您将绕过此问题。

**打开类型**

这是我认为最好的解决方案。将您的类型定义为CompositeData。 这样,根本没有自定义类依赖。这增加了一些优点,例如您可以在远程JMX客户端(如JConsole等)中显示值,该客户端甚至在其类路径中没有自定义类。

//尼古拉斯