Обновление уже подгруженных классов

 
 
 
Сообщения:18
Приветствую.
Есть ли возможность в Java динамически перезагрузить класс, который был уже загружен?
Задача придумана такая: есть некий ValueHandler:
public class ValueHandler {
	private int value = 5;
	
	public int getValue() {
		return value;
	}
	public void setValue(int value) {
		this.value = value;
}

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

В ходе копаний был написан такой исполняемый модуль:
ScheduledExecutorService threads = Executors.newSingleThreadScheduledExecutor();
threads.scheduleAtFixedRate(new Runnable() {
	@Override
	public void run() {
		try {
			java.net.URL url = new java.net.URL("file:///c:\\JavaWorkSpace\\experimental\\bin\\");
			java.net.URL[] qqq = {url};
			ClassLoader cl = new java.net.URLClassLoader(qqq);
	    	
			// Class some = Class.forName("ValueHandler");
			Class<?> some2 = cl.loadClass("ValueHandler");
			Object obj = some2.newInstance();
			java.lang.reflect.Method asd = some2.getMethod("setValue", int.class);
	    	
			asd.invoke(obj, 5);
			asd = some2.getMethod("getValue");
			System.out.println(asd.invoke(obj));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}, 1, 1, TimeUnit.SECONDS);


Как сделать так, что бы не останавливая работу программы начать получать вместо 5 - 25?
Изменен:14 июн 2013 07:04
 
 
Сообщения:389
Если classpath приложения и URLClassloader идентичны, то Ваш classloader будет делегировать загрузку classloader-у приложения. Соответственно класс будет загружен только один раз и не будет обновлен.

Передайте null вторым параметром в конструкторе URLClassloader, чтобы разорвать связь между classloader-ами.
Изменен:14 июн 2013 07:28
 
 
Сообщения:18
gaff, да, помогло, спасибо.
Осталось разобраться как достать не абстрактный Object, а сразу ValueHandler, что бы не обращаться к методам через Method.invoke.

Еще наяндексил такую статью на тему, может кому поможет: Java: магия отражений, Часть II. ClassLoader – скрытые возможности.
Изменен:14 июн 2013 11:07
 
 
Сообщения:389
DraKot:
Осталось разобраться как достать не абстрактный Object, а сразу ValueHandler, что бы не обращаться к методам через Method.invoke.

ValueHandler можно разделить на два класса: неизменяемый интерфейс и его реализацию. Интерфейс должен загружаться classloader-ом приложения, а его реализация URLClassloader-ом. Т.е. интерфейс должен быть в classpath-е приложения, а реализация только в classpath-е URLClassloader-а.
При создании URLClassloader надо сохранить связь между classloader-ами (использовать конструктор с одним параметром).

Экземпляр реализации, созданный через reflection, можно будет привести к интерфейсу и обращаться к его методам напрямую.


Подобный механизм реализован в SeviceLoader API
 
 
Сообщения:18
Немного времени спустя я вернулся к этой теме, и вот чего удалось достичь:

Абстрактный класс-оболочка (с интерфейсами почему-то не удалось обойти):
public abstract class Shell {
	public abstract int getValue(int lala);
	public abstract void setValue(int lala);
}


Класс, который нужно обновлять:
public class ValueHandler extends Shell {
	private int asd = 5;

	@Override
	public int getValue(int someinput) {
		return asd * 5;
	}
	@Override
	public void setValue(int asd) {
		this.asd = asd;
	}
}

Он наследуется от Shell, как видно, дабы в будущем можно было спокойно осуществлять приведение свежезагруженного байт-кода, и запускать без использования reflect.Method.invoke.

Сам подгрузчик:
public class Loader extends ClassLoader {
    public void reloadClass(String path, int index) {
 		java.io.FileInputStream fis = null;
 		byte[] bytes = new byte[0];
		try {
			fis = new java.io.FileInputStream("bin/" + path.replace('.', '/') + ".class");
			bytes = new byte[fis.available()];
			fis.read(bytes);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		} finally {
			if (fis != null) {
				try {
					fis.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
		try {
			ClassLoaderTests.objects.put(index, (Shell) defineClass(path, bytes, 0, bytes.length).newInstance());
		} catch (Exception e) {
			e.printStackTrace();
		}
    }
}


И исполняющий код:
public class ClassLoaderGames {
	public static java.util.HashMap<Integer, Shell> objects = new java.util.HashMap<Integer, Shell>();
	public static void main(String qwerty[]) throws Exception {
/*		ClassLoader cl = ClassLoader.getSystemClassLoader();
		objects.put(0, (Shell) cl.loadClass("pack.ValueHandler").newInstance());
*/
		objects.put(0, (Shell) new ValueHandler());
		
		java.util.concurrent.ScheduledExecutorService threads = java.util.concurrent.Executors.newSingleThreadScheduledExecutor();
		threads.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
            	try {
            		System.out.println(objects.get(0).getValue(5));
            	} catch (Exception e) {
            		e.printStackTrace();
            	}
            }
		}, 0, 3, java.util.concurrent.TimeUnit.SECONDS);

		java.io.BufferedReader inFromUser = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
		while (true) {
			String sentence = inFromUser.readLine();
			if ("1".equals(sentence)) {
				new Loader().reloadClass("pack.ValueHandler", 0);
			}
		}
	}
}

По команде "1" происходит перезагрузка (видимо, с новым класс-лоадером) класса ValueHandler, и помещение его в хэш-мэп по индексу, перезатирая старый.

Но вопросы еще не иссякли:
1) не возникнет ли при слишком частом использовании подобной схемы нехватки памяти? Соберет ли сборщик мусора первую итерацию обновления после запуска второй?
2) останется ли в памяти старый класс сам по себе?
3) нужна ли какая-нибудь дополнительная синхронизация? Подумав самостоятельно я к выводам не пришел.

Возможно кому-нибудь пригодится реализация, если с ней всё нормально (с точки зрения отсутствия подводных камней. Так-то оно работает).
Изменен:18 ноя 2013 13:01
 
 
Сообщения:7989
Выглядит жутковато... :)

А почему вам не использовать скрипты (через пакет javax.script) вместо скомпилированного кода для тех фрагментов которые могут меняться на ходу. Это ж самое естественное применение - типа плагинов...

www.codeabbey.com - programming problems for novice coders (+ certificates)
 
 
Сообщения:18
Дело в том, что не известно что может меняться на ходу, вплоть до добавления какого-нибудь другого ValueHandlerABC под индексом 1, и далее взаимодействие в приложении осуществляется уже с обоими.
Но я не в курсе про содержимое пакета, приму к изучению. =)
 
 
Сообщения:7989
Quote:

Дело в том, что не известно что может меняться на ходу, вплоть до добавления какого-нибудь другого ValueHandlerABC под индексом 1

Это уж ваша забота, как архитектуру и стратегию работы с вашими же "плагинами" устроить.

Quote:

Но я не в курсе про содержимое пакета, приму к изучению.

Ну оно позволяет писать код работающий в нашей же java-машине, но в виде скрипта (встроенный язык javascript, но можно и другие подключить). Сходство языков синтаксическое в данном случае довольно удобно.

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

www.codeabbey.com - programming problems for novice coders (+ certificates)
 
 
Сообщения:22
Подниму тему: у мя похожая проблема, но с одним отличием, объясню на примере:
Есть класс Class1(сделан не мной, переделывать непосредственно нельзя), в нем реализованы какие-то методы, мне надо, чтобы вместо этих методов из всех обращений к этому классу вызывались методы из класса Class2. Через него хочу хочу отмодифицировать методы первого класса. Заранее спасибо за помощь)
 
 
Сообщения:798
hohserg:
Подниму тему: у мя похожая проблема, но с одним отличием, объясню на примере:
Есть класс Class1(сделан не мной, переделывать непосредственно нельзя), в нем реализованы какие-то методы, мне надо, чтобы вместо этих методов из всех обращений к этому классу вызывались методы из класса Class2. Через него хочу хочу отмодифицировать методы первого класса. Заранее спасибо за помощь)

Здесь можно использовать какой-нибудь АОП фреймворк. Написать advice - around invoke для вызова методов Class1.

Нет ничего проще, чем заблудиться в иллюзиях, нет ничего сложнее, чем освободиться от них.
 
 
Сообщения:22
Посоветуете что-нибудь? Мне бы желательно, чтобы имело вид AOP.advice(Class1.class,Class2.class);
 
 
Сообщения:798
Вот ссылка, попробуйте:
http://habrahabr.ru/post/112466/

Нет ничего проще, чем заблудиться в иллюзиях, нет ничего сложнее, чем освободиться от них.
 
 
Сообщения:22
Попробую, спасибо)
 
Модераторы:Нет
Сейчас эту тему просматривают:Нет