ClassCastException при доступе к пользовательскому классу через JMX - PullRequest
0 голосов
/ 17 марта 2012

Я отслеживаю через JMX сервлет, работающий в Tomcat, с другим сервлетом, работающим в том же экземпляре Tomcat. Когда я настраиваю get () для возврата стандартных типов данных Java (String, int, byte [] и т. Д.), Это работает нормально. Но когда я использую пользовательский класс, я получаю ClassCastException, которое выдает следующее сообщение:

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

Я вполне уверен, что это из-за разных загрузчиков классов на инструментальных средствах и на уровне управления (отслеживаемый сервлет и сервлет мониторинга *1007* соответственно). Я дважды проверил файл .jar, который содержит определяемый пользователем класс для каждого сервлета, и оба файла jar идентичны друг другу.

Я использую стандартные MBeans и настроил отслеживаемый сервлет для возврата этого атрибута:

public interface MyMonitorMBean
{
    public My_UserDefinedClass getAllData();
}

реализация:

public class MyMonitor implements MyMonitorMBean
{
    private My_UserDefinedClass mAllData;

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

Код в сервлете управления для доступа к этим данным:

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

Хотя я мог генерировать несколько get () с каждой отправкой стандартного типа данных Java, я хотел бы создать / использовать свой собственный класс POD / POJO, который инкапсулирует все различные стандартные биты и бобы (которые являются стандартными типами данных Java) что я хочу отправить обратно, чтобы я мог получить свои данные в один вызов.

Есть мысли?

Спасибо

Bill

Использование tomcat7, java6, windows xp, 32bit

1 Ответ

0 голосов
/ 17 марта 2012

Это может сработать для вас.Вам необходимо установить загрузчик класса контекста потока [временно] на загрузчик класса 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 при приведении к тому же классу .

==== ОБНОВЛЕНИЕ ====

Я не являюсьполностью ясно, какая у вас точная настройка, но, так или иначе, у вас определенно есть проблема с загрузчиком классов, когда во время выполнения у вас есть [как минимум] два разных загрузчика классов, которые загрузили My_UserDefinedClass.

Это некоторыепараметры:

Жизненный цикл

Убедитесь, что класс, определяющий getAllDataFromMBean, загружен в тот же загрузчик классов, чтобы был только один загрузчик классов.(Может быть сложно)

В качестве альтернативы, поместите банку, содержащую класс, в системный путь к классам до того, как он будет загружен.(т. е. загрузочный classpath или системный classpath)

Reflection

Здесь есть основная проблема с загрузчиками классов, поскольку ваш getAllDataFromMBean метод находится вКласс, который вызовет загрузку My_UserDefinedClass, как только будет загружен it , так что сразу у вас есть два разных загрузчика классов и, следовательно, несовместимые типы.Соответственно, поскольку все базовые значения являются стандартными типами Java, вы можете рефлексивно обращаться к экземпляру.

** Generification **

Вместо того, чтобы создавать пользовательский класс для ваших значений, храните их в карте имя / значение.Таким образом, все передаваемые данные находятся в основных типах java.

** Экстернализация **

Если вы принудительно сериализуете определенный пользователем экземпляр (скажем, в байт []) изатем десериализовать его в конце чтения, вы обойдете эту проблему.

** Открытые типы **

Это решение, которое я считаю лучшим.Определите ваш тип как CompositeData .Таким образом, нет никакой пользовательской зависимости класса вообще.Это дает дополнительные преимущества, такие как тот факт, что вы можете отображать значения в удаленном клиенте JMX (например, JConsole и т. Д.), Который даже не имеет пользовательского класса в своем пути к классам.

// Николас

...