Почему JAXB не может найти мой jaxb.index при запуске внутри Apache Felix?
-
20-08-2019 - |
Вопрос
Это прямо там, в пакете, который он должен индексировать.Тем не менее, когда я звоню
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's 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, в котором это выполняется.К сожалению, здесь я все еще немного не в своей тарелке.
Решение
Хорошо, это потребовало немалых усилий, но ответ не такой уж удивительный и даже не такой сложный:
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
экземпляр используется, но я не мог понять как, и приведенное выше решение кажется мне безопасным.
Другие советы
Я столкнулся с аналогичной проблемой с проектом, над которым я работаю.После прочтения 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
Относиться к ДЖАКСБ. класс B - это JAXBContext, а bMethod - это newInstance()
Если вы знакомы с ограничениями пакетов OSGi, то теперь вам должно быть предельно ясно, что Комплект B не импортирует пакет com.c т.е. класс C является не виден Для класс В следовательно, он не может создать экземпляр C.
Решением было бы передать Загрузчик классов к методу BM.Этот загрузчик классов должен исходить из пакет, который импортирует com.c.В этом случае мы можем пройти A.class.getClassLoader() с тех пор как пакет A импортирует com.c
Надеюсь, это было полезно.
Для решения той же проблемы я решил ее, вручную поместив пакет в импорт.
Если вы используете maven в своем проекте, то просто используйте эту библиотеку:
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-osgi</artifactId>
<version>2.2.7</version>
</dependency>
Он создан для сервера Glasfish, но также работает с Tomcat (проверено).С помощью этой библиотеки вы можете легко использовать JAXB с пакетами OSGI.
Правка 2:
Однажды у меня была похожая странная проблема с загрузкой класса в моем приложении.Если я запускал его как обычное приложение, все было в порядке, но когда я вызывал его как службу Windows, начинался сбой с ClassNotFoundExceptions.Анализ показал, что потоки каким-то образом имеют свои загрузчики классов равными null.Я решил проблему, установив SystemClassLoader в потоках:
// ...
thread.setContextClassLoader(ClassLoader.getSystemClassLoader());
thread.start();
// ...
Однако не знаю, допускает ли ваш контейнер такого рода изменения.
Я только что столкнулся с этой проблемой.Для меня решением было использовать JRE от IBM вместо Oracle.Похоже, что в этом реализация JAXB более удобна для OSGI.
Я успешно решил эту проблему, добавив пакет моих сгенерированных классов, содержащий ObjectFactory
к тому <Private-Package>
часть моего определения пакета, плюс org.jvnet.jaxb2_commons.*
Может быть другой сценарий, который может привести к этой проблеме.
Когда вы устанавливаете и запускаете пакет, который экспортирует пакет, содержащий jaxb.index или objectFactory.java
Затем, пожалуйста, убедитесь, что пакеты, импортирующие классы, остановлены или указывают на правильное имя пакета.
Также проверьте инструкции экспорта и импорта в pom.xml
Столкнулся с аналогичной проблемой в контейнере servicemix (karaf) osgi
Для меня проблема заключалась в том, что модульный тест, который не был связан с модулем, который я разработал, не имел в нем зависимости 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>
Моим Решением было:
Контекст JAXBContext = JAXBContext.newInstance (новый класс[]{"my.package.name "});
или
Контекст JAXBContext = 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%