Здравствуйте, Oval, Вы писали:
O>Загруженый статическим методом class.forName O>(перезагрузить надо прекомпилированный class-плагин)
Да. загружай его отдельным загрузчиком (Classloader). И forName делай с указанием конкретного загрузчика. Для обновления нужна создать новый загрузчик. И спросить класс у него. Он загрузит его заново.
Здравствуйте, Blazkowicz, Вы писали:
B>Здравствуйте, Oval, Вы писали:
O>>Загруженый статическим методом class.forName O>>(перезагрузить надо прекомпилированный class-плагин)
B>Да. загружай его отдельным загрузчиком (Classloader). И forName делай с указанием конкретного загрузчика. Для обновления нужна создать новый загрузчик. И спросить класс у него. Он загрузит его заново.
B>В Яве 1.5 обещают более красивое решение.
Мысля вокруг этого и крутилась — не охота было проверять — тонну кода перелапачивать.
То есть практически в программе возможно существование разных классов но с одинаковыми именами?
Q: I use the Class.forName() method to dynamically load Java classes. However, if I recompile a class and reload it, the new recompiled class does not load. How do I force class reloading?
A: The standard class loader will not reload a class for you. Instead, you need to develop a custom class loader. Luckily, such a class loader already exists, and I'm sure with a little alteration it will serve your needs.
First, download JUnit. Then look at its junit.runner.TestCaseClassLoader.java, a class loader able to reload any given class. JUnit uses TestCaseClassLoader so you do not need to shut down the test GUI (graphical user interface) every time you recompile your test classes. Unfortunately, the latest version cannot reload classes from jar files.
Здравствуйте, Oval, Вы писали:
O>Мысля вокруг этого и крутилась — не охота было проверять — тонну кода перелапачивать. O>То есть практически в программе возможно существование разных классов но с одинаковыми именами?
Да. И что самое противное такая ситуация является потонцеальным источником различных ошибок. Особенно в таких бесплатных решениях как JBoss.
Я бы на твоём месте попытался построить решение на базе 1.5. Если, конечно есть возможность.
The new java.lang.instrument package provides services that allow Java programming agents to instrument programs running on the Java virtual machine. The intrumentation mechanism is modification of the bytecodes of methods.
Здравствуйте, fenix13, Вы писали:
F>У меня постоянно такая ошибка redefineClasses is not supported in this environment
Полнай текст ошибки + stacktrace. А так же используемая JVM, ОС, и как запускаешь.
Здравствуйте, fenix13, Вы писали:
F>Я использую пример по данной вами ссылки выше .
F>И у меня ничего не выходит . F>У меня постоянно такая ошибка redefineClasses is not supported in this environment
На текущий момент в большинстве случаев я предпочел бы вариант с classloader'ами (не такой уж некрасивый этот вариант), а Instrumentation оставил бы для очень специфических случаев.
Этот myBCI.jar — я делал следующим образом :
скомпилил класс myBCI из примера — получил myBCI.class , сдела вручную MANIFEST.MF — со следующим контентом :
Manifest-Version: 1.0
Created-By: 1.6.0_04 (Sun Microsystems Inc.)
Premain-Class: myBCI
Дальше вручную сделал myBCI.jar файл : jar cfM myBCI.jar META-INF/MANIFEST.MF myBCI.class . Получил правельный джар.
Ошибка :
OriginalClass — это выполнение первой версии класс , она проходит нормально
java.lang.UnsupportedOperationException: redefineClasses is not supported in this environment
Если выполнять эту программу в консоли так как написано в примере то результатом будет :
OriginalClass
тоесть ошибки не выдает , или просто не пишет сюда .
Здравствуйте, dshe, Вы писали:
D>На текущий момент в большинстве случаев я предпочел бы вариант с classloader'ами (не такой уж некрасивый этот вариант), а Instrumentation оставил бы для очень специфических случаев.
Да это неплохой и рабочий вариант . Но сколько я не бился — он не позволяет подменить класс. Загрузить новый класс и работать с ним можно . Но это не то что мне нужно .
Если есть реализация примера с classloader'ами — которая подменяет классы то киньте мне . Интересно как сделано в Tomacat ? У них реализовано както что Сервлеты подгребаються автоматически без перезагрузки Tomcat .
Здравствуйте, fenix13, Вы писали:
F>скомпилил класс myBCI из примера — получил myBCI.class , сдела вручную MANIFEST.MF — со следующим контентом : F>Manifest-Version: 1.0 F>Created-By: 1.6.0_04 (Sun Microsystems Inc.) F>Premain-Class: myBCI
Здравствуйте, fenix13, Вы писали:
F>Здравствуйте, dshe, Вы писали:
D>>На текущий момент в большинстве случаев я предпочел бы вариант с classloader'ами (не такой уж некрасивый этот вариант), а Instrumentation оставил бы для очень специфических случаев.
F>Да это неплохой и рабочий вариант . Но сколько я не бился — он не позволяет подменить класс. Загрузить новый класс и работать с ним можно . Но это не то что мне нужно .
С подменой класса есть некоторые нюансы, которые надо учитывать. Например, что делать с существующими экземплярами классов, если класс исчез, или в классе добавилось новое поле? Расширять? Каким значением тогда инициализировать? Что делать если в момент переопределения исполняется метод, который в новом классе отсутствует? или идет обращение в удаленному (или переименованному) полю?
Поэтому Instrumentation обладает ограниченными возможностями по переопределению классов (в частности, насколько я понял из javadoc, добавление, удаление и переименовывание полей и методов не допускаются; можно только менять тела методов). Вариант с classloader'ами в определенной степени разрешает данные вопросы поскольку допускает сосуществование старой и новой версии классов (загруженных разными класслоадерами).
F>Если есть реализация примера с classloader'ами — которая подменяет классы то киньте мне . Интересно как сделано в Tomacat ? У них реализовано както что Сервлеты подгребаються автоматически без перезагрузки Tomcat .
Каждое веб-приложение загружаются своим classloader'ом. Когда Tomcat обнаруживает, что нужно перегрузить некоторое приложение (например, если поменялся web.xml), то он останавливает загруженное (вызывает destroy у сервлетов, фильтров, session и servlet context listener'ов), загружает новым класслоадером новое приложение и заново его инициализирует, а о старом (с его класслоадером) забывает позволяя GC его собрать. С jsp та же история.
D>С подменой класса есть некоторые нюансы, которые надо учитывать. Например, что делать с существующими экземплярами классов, если класс исчез, или в классе добавилось новое поле? Расширять? Каким значением тогда инициализировать? Что делать если в момент переопределения исполняется метод, который в новом классе отсутствует? или идет обращение в удаленному (или переименованному) полю?
Есть не спорю , но идея таки в том что я буду менять только тело методов .
D>Поэтому Instrumentation обладает ограниченными возможностями по переопределению классов (в частности, насколько я понял из javadoc, добавление, удаление и переименовывание полей и методов не допускаются; можно только менять тела методов). Вариант с classloader'ами в определенной степени разрешает данные вопросы поскольку допускает сосуществование старой и новой версии классов (загруженных разными класслоадерами).
F>>Если есть реализация примера с classloader'ами — которая подменяет классы то киньте мне . Интересно как сделано в Tomacat ? У них реализовано както что Сервлеты подгребаються автоматически без перезагрузки Tomcat .
D>Каждое веб-приложение загружаются своим classloader'ом. Когда Tomcat обнаруживает, что нужно перегрузить некоторое приложение (например, если поменялся web.xml), то он останавливает загруженное (вызывает destroy у сервлетов, фильтров, session и servlet context listener'ов), загружает новым класслоадером новое приложение и заново его инициализирует, а о старом (с его класслоадером) забывает позволяя GC его собрать. С jsp та же история.
Вот это, мне очень нравиться . Я по началу тоже пытался так сделать . Но не придумал как удалять старый клас лоадер . Если есть идеи поделитесь . И если например убить старый клас лоадер . То я так понимаю когда я загружу новый лоадер с новой версией класс . То когда понадобиться этот класс — то автоматически подгребётся новая версия класса .
Намёк я понял и использовал его . По идеи в Манифесте нехватало Can-Redefine-Classes: true
Я поставил и функция isRedefineClassesSupported() — стала выдавать true . Незнаю или правду говорит или просто берет значение из манифеста . Может еще чего нужно конфигурировать в самом JVM ?
Появились новые проблемы (это происходит в момент вызова redefineClasses):
OriginalClass
Exception in thread "main" java.lang.NoClassDefFoundError: class names don't match
at sun.instrument.InstrumentationImpl.redefineClasses0(Native Method)
at sun.instrument.InstrumentationImpl.redefineClasses(Unknown Source)
at BCITest.main(BCITest.java:28)
Значения я проверяю , валидные ему даю все нормально . Что еще может быть ?
Может этот пример очень сырой — его както дополнять нужно может ?
Здравствуйте, fenix13, Вы писали:
F>Вот это, мне очень нравиться . Я по началу тоже пытался так сделать . Но не придумал как удалять старый клас лоадер . Если есть идеи поделитесь . И если например убить старый клас лоадер . То я так понимаю когда я загружу новый лоадер с новой версией класс . То когда понадобиться этот класс — то автоматически подгребётся новая версия класса .
Не надо удалять загрузчики. Надо контролировать потоки, так чтобы они шли через классы и их экземпляры исключительно нового загрузчика.
Появилась еще одна интересная задачя . Если есть идеи то подскажы . Сейчас обрисую :
Мне нужно перегрузить метод classloader . А именно чтобы с самого начала работы программы — она лезла в мой класслоадер а не в системный . Возможно ли такое сделать? Мне это нужно для того чтобы контролировать какую именно версию класса мне нужно загружать в момент работы программы .
Если есть вопросы или предложения , давай подискусируем .
Здравствуйте, fenix13, Вы писали:
F>Появилась еще одна интересная задачя . Если есть идеи то подскажы . Сейчас обрисую : F>Мне нужно перегрузить метод classloader . А именно чтобы с самого начала работы программы — она лезла в мой класслоадер а не в системный . Возможно ли такое сделать? Мне это нужно для того чтобы контролировать какую именно версию класса мне нужно загружать в момент работы программы . F>Если есть вопросы или предложения , давай подискусируем .
Да, проще простого. Все твои классы нужно сделать недоступным для класслоадера который обычно их загружеает. Затем в коде создать экземпляр своего класслоадера, научить его искать твои классы, и далее все обращения вести через него. Там уже надо смотреть по ситуации и окружению. Thread.setContextClassloader(), либо просто загрузить класс своим класслоадером, а этот класс уже будет подтягивать зависимости только через этот же класслоадер. В общем штудируй API к классам ClassLoader/URLClassLoader, смотри исходный код этих классов и все у тебя получится.
Здравствуйте, fenix13, Вы писали:
F>Здравствуйте, Blazkowicz
F>Появилась еще одна интересная задачя . Если есть идеи то подскажы . Сейчас обрисую :
F>Мне нужно перегрузить метод classloader . А именно чтобы с самого начала работы программы — она лезла в мой класслоадер а не в системный . Возможно ли такое сделать? Мне это нужно для того чтобы контролировать какую именно версию класса мне нужно загружать в момент работы программы . F>Если есть вопросы или предложения , давай подискусируем .
Возможно тебе поможет свойство java.system.class.loader подменить системный класслоадер. Параметром является имя класса класслоадера, который должен иметь конструктор с одном параметром, принимающим родительский класслоадер. Например,
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
public class My extends URLClassLoader {
public My(ClassLoader parent) throws Exception {
super(new URL[] { new File("./classes").toURL() }, parent);
}
}
запуск
java -Djava.system.class.loader=My Main
Для полноты картины пусть Main класс выводит цепочку класслоадеров
public class Main {
public static void main(String[] args) {
for (ClassLoader loader = Main.class.getClassLoader(); loader != null; loader = loader.getParent()) {
System.out.print(loader == ClassLoader.getSystemClassLoader() ? "system " : " ");
System.out.println(loader);
}
}
}
Тогда с подмененным класслоадером цепочка выглядит так
system My@42e816
sun.misc.Launcher$AppClassLoader@7d772e
sun.misc.Launcher$ExtClassLoader@11b86e7
А со стандартным
system sun.misc.Launcher$AppClassLoader@d9f9c3
sun.misc.Launcher$ExtClassLoader@9cab16
Т.е. свой класслоадер загружается стандартным системным (AppClassLoader) и он же передается в качестве родительского.
Здравствуйте, Blazkowicz, Вы писали:
B>Да, проще простого. Все твои классы нужно сделать недоступным для класслоадера который обычно их загружеает. Затем в коде создать экземпляр своего класслоадера, научить его искать твои классы, и далее все обращения вести через него. Там уже надо смотреть по ситуации и окружению. Thread.setContextClassloader(), либо просто загрузить класс своим класслоадером, а этот класс уже будет подтягивать зависимости только через этот же класслоадер. В общем штудируй API к классам ClassLoader/URLClassLoader, смотри исходный код этих классов и все у тебя получится.
Ты меня натолкнул вобще на сногсшибательную идею : я только что сделал тестовую прогу которая в самом начале — просто напросто копирует либы из моего дочернего сервера в дерикторию класпаза — таким образом либы просто подменяются — и когда происходит загрузка обьекта — он уже все танет с новых либ .
Это работает 100%
Что ты думаешь по этому поводу ? насколько это безопасный вариант ?
Здравствуйте, fenix13, Вы писали:
F>Ты меня натолкнул вобще на сногсшибательную идею : я только что сделал тестовую прогу которая в самом начале — просто напросто копирует либы из моего дочернего сервера в дерикторию класпаза — таким образом либы просто подменяются — и когда происходит загрузка обьекта — он уже все танет с новых либ .
Понимаешь ли в чем дело. Я понятия не имею кто такой дочерний сервер, что да дирекотрия класпаза и в каком контектсе работает твое приложение вообще. Standalone, J2EE, Spring, Eclipse RCP.
F>Это работает 100%
Не ясно зачем копировать либы со старта, если их можно вообще не помещать куда не надо. Есть ещё такая бага под виндой, что когда URLClassLoader загружает класс из jar, то jar лочится файловой системой.
F>Что ты думаешь по этому поводу ? насколько это безопасный вариант ?
На безопасный вариант не похоже, а так ХЗ, что там за приложение на этим скрывается.