ClassCastException при создании экземпляра класса с использованием отражения и ClassLoaders

В первую очередь это Java 1.4 (ограничения проекта). Я пытаюсь создать диспетчер приложений. Он загружает основной класс каждого приложения, используя собственный экземпляр пользовательского загрузчика классов. После этого он создает экземпляр основного класса, используя отражение. Каждое приложение реализует общий интерфейс, поэтому после создания экземпляра он запускает предопределенный метод приложения.

Однако у меня возникли проблемы с CRASH POINT 1 (см. код). Класс не распознается как одна из реализаций его интерфейса. Если я прокомментирую этот фрагмент кода, я получу ClassCastException в CRASH POINT 2.

Я полагаю, что обе ошибки связаны с одной и той же проблемой (конечно).

Может кто-нибудь помочь мне? Далее следует соответствующая часть кода (импорт удален)...

Большое спасибо.

Маркус

// AppManager.java

public class AppManager {
    public ThreadGroup threadGroup;
    private Class appClass;
    private AppInstance appInst;
    public AppContextImpl context;

    private AppManager(CustomClassLoader cl, String mainClass) throws ClassNotFoundException {
        final String className = mainClass;
        final CustomClassLoader finalLoader = cl;

        appClass = cl.loadClass(mainClass);

        // DEBUG CODE:
        Class[] k1 = AppInstance.class.getInterfaces();
        System.out.println(k1.length + " interfaces for AppInstance.class:");
        for (int ii = 0; ii < k1.length; ii++) {
            System.out.println("   " + ii + " - " + k1[ii].getName() + " (" + k1[ii].getClassLoader() + ")");
        }

        Class[] k2 = appClass.getInterfaces();
        System.out.println(k2.length + " interfaces for appClass instance:");
        for (int ii = 0; ii < k2.length; ii++) {
            System.out.println("   " + ii + " - " + k2[ii].getName() + " (" + k2[ii].getClassLoader() + ")");
        }

        // CRASH POINT 1
        if (!(AppInstance.class.isAssignableFrom(appClass))) {
            throw new IllegalArgumentException("Attempt to run a non-AppInstance class: " + appClass);
        }

        context = new AppContextImpl(mainClass, this);
        cl.setAppManager(this);
        Constructor m;
        try {
            m = appClass.getConstructor(new Class[0]);
           // CRASH POINT 2
            appInst = (AppInstance) m.newInstance(new Object[0]);
            appInst.init(context);
        } catch (Exception e) {
            System.out.println("Got ClassCastException here!\n");
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        App app1;

        String path1 = "/home/user/workspace/MultiTaskTest/bin/";
        String app1Name = "App1";

        Vector v1 = new Vector();
        try {
            v1.add(new URL(path1));
        } catch (MalformedURLException e1) {
            final File file1 = new File(path1);
            try {
                URL path1aux = (URL) AccessController.doPrivileged(
                    new PrivilegedExceptionAction() {
                        public Object run() throws IOException {
                            if (!file1.exists()) {
                                System.out.println("Warning: \"" + file1.getPath() + "\" not found");
                                return null;
                            }
                        return file1.toURI().toURL();
                        }
                    });

                if (path1aux != null) {
                    v1.add(path1aux);
                }
            } catch (PrivilegedActionException e) {
                e.getException().printStackTrace();
            }
    }

        final URL[] array1 = (URL[]) v1.toArray(new URL[v1.size()]);
        CustomClassLoader cl1 = (CustomClassLoader) AccessController.doPrivileged(
            new PrivilegedAction() { public Object run() {
                return new CustomClassLoader(array1);
            }});
        System.out.println("ClassLoader 1 created: " + cl1);
        try {
            app1 = new App(cl1, app1Name);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("Cannot find class App1.");
        }
    }
}

// AppInstance.java

public interface AppInstance {
    public void init(ContextImpl context);
}

// App1.java

public class App1 implements AppInstance {
    private AppContextImpl contextObj;

    public void init(AppContextImpl context) {
        this.contextObj = context;
        System.out.println("Running App1...");
    }
}

// AppContextImpl.java

public class AppContextImpl {
    public String mainClass;
    public AppManager app;

    public AppContextImpl(String mainClass, AppManager app) {
        this.mainClass = mainClass;
        this.app = app;
    }
}

// CustomClassLoader.java

public class CustomClassLoader extends URLClassLoader {
    AppManager appInst;

    public CustomClassLoader(URL[] paths) { super(paths, null); }
    public void setAppManager(AppManager app) { this.appInst = app; }
}

Вывод кода отладки в файле AppManager.java:

0 interfaces for AppInstance.class:
1 interfaces for appClass instance:
   0 - AppInstance (CustomClassLoader@480457)

person Marcus    schedule 08.06.2010    source источник
comment
Похоже, это проблема с загрузчиком классов. Вы должны убедиться, что класс, который вы загружаете, а также сам интерфейс AppInstance были загружены одним и тем же загрузчиком классов, иначе Java считает их совершенно не связанными.   -  person Eyal Schneider    schedule 08.06.2010
comment
Каков результат Class.getGenericInterfaces() и Class.getInterfaces()?   -  person TheLQ    schedule 08.06.2010
comment
Lord.Quackstar: Проверьте вопрос еще раз. Я вставил несколько комментариев, чтобы предоставить информацию, которую вы просили.   -  person Marcus    schedule 08.06.2010
comment
Эял: Я еще немного изучу информацию о загрузчиках классов. Я вернусь, как только у меня будут новости. Спасибо   -  person Marcus    schedule 08.06.2010
comment
Эял: Это действительно проблема с загрузчиком классов. Пожалуйста, прочитайте мой комментарий к ответу Гийома ниже. Вы знаете, возможно ли решить эти проблемы? Спасибо еще раз.   -  person Marcus    schedule 09.06.2010
comment
Эяль: Не могли бы вы взглянуть на 6-й комментарий к ответу Гийома ниже? Это решает проблему, я просто не могу воспроизвести это в своем приложении, я не могу найти, где происходит волшебство.   -  person Marcus    schedule 10.06.2010


Ответы (1)


Ваш класс AppInstance, вероятно, загружается отдельно каждым пользовательским загрузчиком классов. Поскольку объекты класса зависят от фактического класса И от загрузчика классов, это действительно разные классы. Таким образом, AppInstance из загрузчика классов 1 отличается от AppInstance из загрузчика классов 2.

Что вам нужно сделать, так это использовать стандартную иерархию загрузчика классов: используйте корневой загрузчик классов для своего приложения и убедитесь, что AppInstance загружается загрузчиком классов. Затем сделайте свой пользовательский загрузчик классов из корня. Всякий раз, когда им нужно получить доступ к классу AppInstance, они будут использовать то, что загружено из корня.

Итак, вместо этого:

public CustomClassLoader(URL[] paths) { super(paths, null); }

Вам нужно указать родителя для вашего CustomClassLoader

person Guillaume    schedule 08.06.2010
comment
Изоляция, обеспечиваемая отдельными загрузчиками классов, как раз и является целью. Если я установлю для них родительский загрузчик классов, изоляция будет нарушена, не так ли? Я немного изучу информацию о загрузчиках классов, чтобы убедиться, что я ничего не понимаю неправильно. Я скоро вернусь. Между тем, спасибо за ваш ответ. - person Marcus; 08.06.2010
comment
Вам нужно решить, какая часть вашего приложения будет общей, а какая будет специфичной для модуля. Похоже, вы пытаетесь заново изобрести OSGi, возможно, вы захотите изучить его;) - person Guillaume; 09.06.2010
comment
Я проверил, что такое OSGi. Что мне нужно сделать, это только это. Однако я не могу использовать OSGi, потому что мое приложение основано на общедоступной спецификации, которая накладывает некоторые ограничения. Мне нужно загрузить каждое приложение, используя свой собственный загрузчик классов. После этого я буду вести нить мониторинга в общем порядке. Приложения будут использовать специальный API для взаимодействия между собой. Знаете ли вы, можно ли решить проблемы в CRASH POINTs (1 и 2) без установки родительского загрузчика классов для каждого пользовательского загрузчика классов? Спасибо еще раз. - person Marcus; 09.06.2010
comment
Из того, что вы говорите, общий API должен загружаться одним загрузчиком классов, и этот загрузчик классов должен быть родителем загрузчиков классов ваших модулей. Поскольку родительский загрузчик классов не будет видеть классы модуля (только API), он не сможет их загрузить; они будут загружены на уровне модуля. - person Guillaume; 09.06.2010
comment
Я думаю так же, как и вы. Тем не менее, я мог бы найти пример кода, который делает именно то, что я хочу сделать, я просто не могу найти, как он это делает. Код, который я привел в примере, почти такой же. Взгляните на код в следующем комментарии, который я отправлю. - person Marcus; 10.06.2010
comment
Щелкните каждый файл и щелкните ver или baixar, чтобы просмотреть или загрузить его. ginga.lavid.ufpb.br/projects/ginga-j/repository/revisions/ Остальные файлы находятся здесь: ginga. lavid.ufpb.br/projects/ginga-j/repository/revisions/ - person Marcus; 10.06.2010
comment
Если я изменю этот код, вставив тот же код DEBUG, который я указал в начале вопроса (файл AppManager.java), он напечатает: 0 javax.microedition.xlet.Xlet.class: 1 интерфейсы для экземпляра xletClass: 0 - javax .microedition.xlet.Xlet (null) Я просто не могу этого понять. - person Marcus; 10.06.2010
comment
Я подозреваю, что магия имеет какое-то отношение к свойствам безопасности. Я сейчас немного читаю об этом. - person Marcus; 10.06.2010
comment
Я не эксперт, Xlet кажется частью базовых классов JME, вероятно, он загружается загрузчиком классов начальной загрузки. Классы из базовой JRE всегда загружаются начальной загрузкой, поэтому они не могут быть продублированы. - person Guillaume; 10.06.2010
comment
Именно в этом загвоздка. Xlet — это системный класс, поэтому он загружается загрузчиком классов начальной загрузки. Мои приложения должны реализовывать системный класс. Я справлюсь. Большое спасибо за вашу помощь и извините за слишком долгий ответ, я отсутствовал в течение дня. - person Marcus; 11.06.2010