Вопрос по JavaSE classloading

Gaishnik

У меня есть закрытый проприетарный клиент для некоторого application сервера, который хранит настройки подключения в static полях некоторого класса. Из-за этого параллельная работа с разными серверами в несколько потоков становится невозможной. Я пытаюсь решить проблему, загружая копии всех классов отдельно для каждого сервера.
Общая схема примерно такая

package classloading;
public class Singleton {
public static String name;
}


package classloading;
public class SingletonUser{
public String getSingletonName{
return Singleton.name;
}
public void setSingletonName(String name){
Singleton.name = name;
}
}

  
package classloading;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws Exception {
URL[] urlsToLoadFrom = new URL[] { new URL("file:subdir/") };
URLClassLoader loader1 = new URLClassLoader(urlsToLoadFrom);
URLClassLoader loader2 = new URLClassLoader(urlsToLoadFrom);
Class cls1 = Class.forName("classloading.SingletonUser", true, loader1);
Class cls2 = Class.forName("classloading.SingletonUser", true, loader2);
Object su1 = cls1.newInstance;
Object su2 = cls2.newInstance;
su1.getClass.getMethod("setSingletonName",new Class[]{String.class} ).
invoke(su,"111111");
su2.getClass.getMethod("setSingletonName",new Class[]{String.class} ).
invoke(su2,"222222");
}
}

В результате мы имеем 2 разные копии Singleton, и, соответственно, Singleton.name
Проблема в том, что приходится работать через reflection, и вызовы методов становятся громоздкими. Как нибудь можно это исправить?

klyv

[хренасе заморочка /]
можно генерить при загрузке проксю (например, на Groovy) к классу настроек :)

kokoc88

Проблема в том, что приходится работать через reflection, и вызовы методов становятся громоздкими. Как нибудь можно это исправить?
Не понял только одного, почему ты Object потом не приводишь к Singleton? В твоём примере это будет работать.

Gaishnik

Не понял только одного, почему ты Object потом не приводишь к Singleton? В твоём примере это будет работать.
Я пробовал, не прокатывает. Singleton.name начинают перекрываться. Но я на всякий случай завтра еще раз проверю.
хренасе заморочка
Вроде по другому никак не сделать. :confused:
можно генерить при загрузке проксю (например, на Groovy) к классу настроек :)
Тогда же ведь придется декомпилировать и править код клиентской библиотеки? Я не буду этого делать, потому что имеющееся решение с рефлекшеном лучше.
Представь, что Singleton и SingletonUser (это как раз и есть аналог моей закрытой библиотеки) ты не можешь менять, а можешь менять только Test. И в Test тебе надо создать несколько потоков и SingletonUser в каждом и вызывать в них методы SingletonUser, чтобы они не мешали друг другу(а мешают они потому что все обращаются к static полю класса Singleton)

pitrik2

Я пробовал, не прокатывает. Singleton.name начинают перекрываться. Но я на всякий случай завтра еще раз проверю.
типа кастинг приводит к конкретизации класслоадера :(

kokoc88

Я пробовал, не прокатывает. Singleton.name начинают перекрываться. Но я на всякий случай завтра еще раз проверю.
Понятно. Сначала я не заметил static. Обычно singleton содержит только одно статическое поле (себя) и имеет private конструктор. Как раз для рефакторинга под нужды, подобные твоим.
Я собрал твой пример, и вызвал getSingletonName через reflection. У меня получилось, что строка общая для обоих классов.

myrka68

видимо, URL неверный указал и классы дефолтным (parent) класс-лоадером загрузились
добавь assert cls1 != cls2

kokoc88

видимо, URL неверный указал и классы дефолтным (parent) класс-лоадером загрузились
Так и есть. Я без понятия, что там надо ей указать, чтобы она загрузила разные.

Svyatogor

Проблема в том, что приходится работать через reflection, и вызовы методов становятся громоздкими. Как нибудь можно это исправить?
Подобная проблема часто решается при поддержке различных плагинов, которые загружаются внутри с использованием своих ClassLoader's. И решение там - базовый интерфейс, реализуемый соответствующими плагинами (сущностями). После создания объекта, его тип приводится к интерфейсу плагина и вся дальнейшая работа выполняется уже с этим интерфейсом.

package classloading;
public interface SingletonInterface {
public String getSingletonName;
public void setSingletonName(String name);
}


public class SingletonUser implements SingletonInterface {
// implementation skipped
}


package classloading;
import java.net.URL;
import java.net.URLClassLoader;
public class Test {
public static void main(String[] args) throws Exception {
URL[] urlsToLoadFrom = new URL[] { new URL("file:subdir/") };
URLClassLoader loader1 = new URLClassLoader(urlsToLoadFrom);
URLClassLoader loader2 = new URLClassLoader(urlsToLoadFrom);
Class cls1 = Class.forName("classloading.SingletonUser", true, loader1);
Class cls2 = Class.forName("classloading.SingletonUser", true, loader2);
SingletonInterface su1 = (SingletonInterface) cls1.newInstance;
SingletonInterface su2 = (SingletonInterface) cls2.newInstance;
su1.setSingletonName("111111");
su2.setSingletonName("222222");
System.out.println(su1.getSingletonName;
System.out.println(su2.getSingletonName;
}
}

В данном случае SingletonInterface должен грузиться тем же classloader'ом, что и Test (например, быть в classpath). SingletonUser и Singleton - наоборот, должны грузиться отдельными classloader'ами (точно так же, как в исходном примере).

*** ~/jtest/loading $ ls classloading/*.class
classloading/SingletonInterface.class classloading/Test.class
*** ~/jtest/loading $ ls subdir/classloading/*.class
subdir/classloading/Singleton.class subdir/classloading/SingletonUser.class
Оставить комментарий
Имя или ник:
Комментарий: