Работая над одним проектом, наткнулся на интересное поведение ObjectInputStream jvm от Sun/Oracle.
Надеюсь все знают что в джава классы загружаются при помощи ClassLoader`а, специального класса такого. При запуске приложения и, соответственно, потока к нему (потоку) прикрепляется его ClassLoader, который можно получить/изменить при помощи Thread.currentThread().set/getContextClassLoader. Если хотите можно конечно использовать свой ClassLoader либо напрямую вызывая loadClass, далее получая объект загруженного класса и вызывая его методы либо Thread.currentThread().setContextClassLoader(myClassLoader).
Это вроде как работает, но оказывается есть ситуации когда нельзя понять какой ClassLoader будет использоваться.
Начну наверное с того как я на это наткнулся.
Есть веб приложение с поддержкой плагинов. Плагины хранятся в user.home/./plugins. Таким образом плагины не находятся classpass приложения, для загрузки классов плагинов есть специальный ClassLoader, который умеет это делать.
Один из таких плагинов может сериализовать/десериализовать некоторые объекты на диск. В одном классе я неожиданно для себя получил ClassNotFoundException во время чтения объектов из ObjectInputStream.
Не буду вдаваться в подробности, exception получил при следующем коде:
...
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(someFile));
Object obj = ois.readObject();
...
Стектрейс получил следующий:
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1516)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1361)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:604)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1575)
...
Из стектрейса понял что используется нетот ClassLoader при загрузке класса модуля. Я удивился и попробовал перед вызовом метода установить тот ClassLoader что нужен был:
Thread.currentThread().setContextClassLoader(myClassLoader). И опять получил ClassNotFoundException. Вот *ля подумал я, что за дела и начал гуглить/смотреть javadoc/тацневать с бубном/и т.д.
Так же видно, что exception выкидывает ObjectInputStream.resolveClass. Я открыл javadoc на этот метод и увидел следующее:
...
The default implementation of this method in
ObjectInputStream
returns the result of callingClass.forName(desc.getName(), false, loader)
where loader
is determined as follows: if there is a method on the current thread's stack whose declaring class was defined by a user-defined class loader (and was not a generated to implement reflective invocations), then loader
is class loader corresponding to the closest such method to the currently executing frame; otherwise, loader
is null
....
Так же на forums.sun.com прочитал, что данный метод использует
native latestUserDefinedLoader()
и не понятно что значит описание javadoc ObjectInputStream.resolveClass метода.
Почитал несколько разделов форума forums.sun.com, там люди пришли к решению унаследовать ObjectInputStream и переопределить resolveClass. Да это решение будет работать, но что делать, если нет возможности этого сделать, например я использую стороннюю библиотеку, внутри которой всё это происходит.
Во дела, тупик! о0 либо я что-то очень важное упустил?
з.ы. мой первый пост, так что приветствую любые комментарии.
Хм... интересная проблемка :)
ReplyDeleteА что если попробовать использовать свой класс-лоадер самым первым для всего веб-приложения? Если же это сделать нельзя, и все это происходит в сторонней библиотеке - тогда придется лезть туда и подменять методы или классы бинутилсами. Где-то же создается ObjectInputStream, кто-то же далает ему new - значит можно и зацепиться за этот метод. Решение, конечно, не из простых и не из самых может даже нормальных - но первое, что пришло в голову :)
Спасибо за совет. Но пока что "игра не стоит свеч" для выделения времени на эту проблему, по позже может попробую.
ReplyDeleteКстати, всё это находится в сторонней библиотеке в моём случае :) и "хачить" её пока не очень то хочется.
Противно, что проблема то распространённая оказалась для той библиотеки, а её разработчики не очень то активно отвечают на письма (