ClassCastException при доступе к пользовательскому классу через JMX

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

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

Я почти уверен, что это из-за разных загрузчиков классов на инструментальном уровне и на уровне управления (сервлет monitoring и сервлет monitoring соответственно). Я дважды проверил файл .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), которые я хочу отправить обратно, чтобы я мог получить свои данные за один вызов.

есть идеи?

Спасибо,

Счет

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


person joe7pak    schedule 16.03.2012    source источник
comment
Что ж, вот возможное решение: вместо вызова MBS.getAttribute() вызовите MBS.getAttributes(), который должен вернуть все атрибуты с их значениями. По-прежнему нельзя упростить классы MBean, но можно несколько упростить на уровне управления.   -  person joe7pak    schedule 17.03.2012


Ответы (1)


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

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

Отражение

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

** Обобщение **

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

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

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

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

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

//Николас

person Nicholas    schedule 17.03.2012
comment
Спасибо, похоже, это может быть ответ, но, увы, я получаю точно такие же результаты. Я действительно удивлен этим очевидным ограничением JMX, что можно передавать только стандартные классы Java. Я полагаю, именно поэтому примеры, которые я видел, показывали только целые числа и строки и никогда пользовательский класс, и никогда не обсуждались пользовательские классы в контексте JMX. - person joe7pak; 18.03.2012
comment
Еще одна мысль об этом ответе. Я подозреваю, что мне нужен загрузчик классов из отслеживаемого сервлета, а не из MBeanServer. Проблема в том, что я пытаюсь не включать ссылки на отслеживаемый сервлет в сервлет мониторинга... это делается для того, чтобы я мог добавлять больше отслеживаемых сервлетов, не редактируя сервлет мониторинга для каждого нового отслеживаемого сервлета. - person joe7pak; 19.03.2012
comment
Хорошо, я думаю, что у нас есть победитель здесь. - person joe7pak; 21.03.2012
comment
Хорошо, я думаю, что у нас есть победитель здесь. Карта имени/значения звучала как хорошая идея, но я этого не сделал, я все еще хотел сделать это со своим классом. Что касается ручной сериализации/десериализации, да, это тоже могло сработать, но я посчитал, что это слишком много работы и слишком много сложностей. Победителем является использование MXBeans не из-за их поддержки OpenTypes (мне не нужна эта функция), а из-за их поддержки «CompositeData», который является способом имитации доступа к определяемому пользователем классу. Этот доступ к определяемому пользователем классу был моей целью, и MXBeans сделал это чисто и просто. Спасибо, Николай! - person joe7pak; 21.03.2012