Подтвердить что ты не робот

Почему JAXB не может найти мой jaxb.index при работе внутри Apache Felix?

Именно там, в пакете, он должен индексироваться. Тем не менее, когда я звоню

JAXBContext jc = JAXBContext.newInstance("my.package.name");

Я получаю исключение JAXBException, говорящее, что

"my.package.name" не содержит ObjectFactory.class или jaxb.index

хотя он содержит оба.

Что работает, но не совсем то, что я хочу,

JAXBContext jc = JAXBContext.newInstance(my.package.name.SomeClass.class);

Этот вопрос от других людей появляется в некоторых списках рассылки и форумах, но, похоже, не получает ответов.

Я запускаю это на OpenJDK 6, поэтому я получил исходные пакеты и отступил от своего отладчика в библиотеке. Он начинается с поиска jaxb.properties, затем ищет системные свойства и не находит их, он пытается создать контекст по умолчанию, используя com.sun.internal.xml.bind.v2.ContextFactory. Там исключается Исключение (внутри ContextFactor.createContext(String ClassLoader, Map)), но я не вижу, что происходит, потому что источника здесь нет.

ETA

Судя по исходному коду ContentFactory, я нашел здесь, это, вероятно, часть кода, которая не работает должным образом:

/**
 * Look for jaxb.index file in the specified package and load it contents
 *
 * @param pkg package name to search in
 * @param classLoader ClassLoader to search in
 * @return a List of Class objects to load, null if there weren't any
 * @throws IOException if there is an error reading the index file
 * @throws JAXBException if there are any errors in the index file
 */
private static List<Class> loadIndexedClasses(String pkg, ClassLoader classLoader) throws IOException, JAXBException {
    final String resource = pkg.replace('.', '/') + "/jaxb.index";
    final InputStream resourceAsStream = classLoader.getResourceAsStream(resource);

    if (resourceAsStream == null) {
        return null;
    }

Из моего предыдущего опыта, я предполагаю, что это связано с механизмами загрузки классов из контейнера OSGi, в котором он работает. К сожалению, я все еще немного не в своей глубине.

4b9b3361

Ответ 1

ОК, это заняло довольно много рывка, но ответ не такой уж удивительный и даже не такой сложный:

JAXB не может найти jaxb.index, потому что по умолчанию newInstance(String) использует текущий загрузчик класса потока (который возвращается Thread.getContextClassLoader()). Это не работает внутри Felix, потому что пакеты OSGi и потоки фреймов имеют отдельные загрузчики классов.

Решение состоит в том, чтобы получить подходящий загрузчик классов откуда-то и использовать newInstance(String, ClassLoader). Я получил подходящий загрузчик классов из одного из классов пакета, содержащего jaxb.index, разумный выбор по причинам гибкости, вероятно, составляет ObjectFactory:

ClassLoader cl = my.package.name.ObjectFactory.class.getClassLoader();
JAXBContext jc = JAXBContext.newInstance("my.package.name", cl);

Возможно, вы также можете получить загрузчик классов, который использует экземпляр Bundle, но я не мог понять, как, и приведенное выше решение кажется мне безопасным.

Ответ 2

Я столкнулся с аналогичной проблемой с проектом, над которым я работаю. После прочтения http://jaxb.java.net/faq/index.html#classloader я понял, что JAXBContext не может найти пакет, содержащий jaxb.index.

Я постараюсь сделать это максимально ясным.

Мы имеем

Bundle A
   -- com.a
      A.java
        aMethod()
        {
            B.bMethod("com.c.C");
        }
MANIFEST.MF
Import-Package: com.b, com.c         

Bundle B
   -- com.b
      B.java
        bmethod(String className)
        {
            Class clazz = Class.forName(className);
        }

Export-Package: com.b

Bundle C
   -- com.c
      C.java
        c()
        {
            System.out.println("hello i am C");
        }

Export-Package: com.c

Отнестись к JAXB. класс B - JAXBContext, а bMethod - newInstance()

Если вы знакомы с ограничениями пакета OSGi, тогда должно быть очень ясно, что Bundle B не импортирует пакет com.c, то есть класс C не отображается до класса B, поэтому он не может создать экземпляр C.

Решение состоит в передаче ClassLoader в bMethod. Этот ClassLoader должен состоять из пакета , который импортирует com.c. В этом случае мы можем передать A.class.getClassLoader(), поскольку пакет A импортирует com.c

Надеюсь, это было полезно.

Ответ 3

По той же самой проблеме я разрешил ее, вручную помещая пакет в импорт.

Ответ 4

Если вы используете maven в своем проекте, просто используйте эту библиотеку:

<dependency>
    <groupId>com.sun.xml.bind</groupId>
    <artifactId>jaxb-osgi</artifactId>
    <version>2.2.7</version>
</dependency>

Он создан для сервера Glasfish, но также работает с Tomcat (проверено). С помощью этой библиотеки вы можете легко использовать JAXB с пакетами OSGI.

Ответ 5

Изменить 2:

У меня была такая же странная проблема загрузки классов в моем приложении. Если я запустил его как обычное приложение, все было в порядке, но когда я вызвал его как службу Windows, он начал терпеть неудачу с ClassNotFoundExceptions. Анализ показал, что потоки имеют свои загрузчики классов как-то как-то. Я решил проблему, установив SystemClassLoader в потоки:

// ...
thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
thread.start();
// ...

Не знаю, разрешает ли ваш контейнер такое изменение.

Ответ 6

Я только что столкнулся с этой проблемой. Для меня решение заключалось в использовании IBM JRE вместо Oracle. Похоже, что реализация JAXB более удобна для OSGI в этом.

Ответ 7

Я успешно разрешил это, добавив пакет моих сгенерированных классов, содержащий ObjectFactory в часть <Private-Package> моего определения пакета, плюс org.jvnet.jaxb2_commons.*

Ответ 8

Может возникнуть другой сценарий, который может дать эту проблему.

При установке и запуске пакета, который экспортирует пакет, содержащий jaxb.index или objectFactory.java

Затем убедитесь, что пакеты, импортирующие классы, остановлены или указывают на правильное имя пакета.

Также проверьте инструкции экспорта и импорта в файле pom.xml

Столкнувшись с подобной проблемой в контейнере osgi servicemix (karaf)

Ответ 9

Для меня проблема заключалась в том, что unit test, который не был связан с модулем, который я разработал, не имел в нем никакого отношения pom.xml к моему модулю. UT по-прежнему распознал мой модуль из-за выбора списка пакетов из файла общей конфигурации.

При запуске UT он не скомпилировал новый модуль, чтобы он не сгенерировал ObjectFactory.java, поэтому я получил ошибку, хотя при компиляции модуля я смог увидеть ObjectFactory.java

добавлена ​​следующая зависимость:

<dependency>
    <groupId>com.myCompany</groupId>
    <artifactId>my-module-name</artifactId>
    <version>${project.version}</version>
    <scope>test</scope>
</dependency>

Ответ 10

Мое решение было:

JAXBContext context = JAXBContext.newInstance(новый класс [] { "my.package.name" });

ИЛИ

JAXBContext context = JAXBContext.newInstance(новый класс [] {class.getName()});

ИЛИ

полное решение:

public static <T> T deserializeFile(Class<T> _class, String _xml) {

        try {

            JAXBContext context = JAXBContext.newInstance(new Class[]{_class});
            Unmarshaller um = context.createUnmarshaller();

            File file = new File(_xml);
            Object obj = um.unmarshal(file);

            return _class.cast(obj);

        } catch (JAXBException exc) {
            return null;
        }
    }

Работает 100%