Поставка другой версии JAXB для JAX-WS в Java 1.6
У меня есть сторонний jar, который поставляется с jaxb-impl.jar и включает его в свой манифест classpath. Проблема в том, что кажется, что предоставление вашей собственной версии JAXB (независимо от того, какая версия это может быть), кажется, нарушает SoapFaultBuilder в JAX-WS.
Согласно неофициальному руководству JAXB, кажется, что Sun намеренно изменила имя пакета, когда свернула JAXB в JDK, чтобы избежать конфликтов с автономной версией. Однако SoapFaultBuilder (я полагаю, часть JAX-WS), который поставляется с JDK, явно зависит от нового внутреннего имени пакета. Это приводит к сбою при создании сообщения об ошибке, если вы добавили автономный JAXB-файл (даже если это тот же номер версии JAXB).
Вот мой маленький контрольный пример: я делаю тривиальный веб-сервис:
package wstest;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
//Service Endpoint Interface
@WebService
@SOAPBinding(style = Style.RPC)
public interface HelloWorld{
@WebMethod String getHelloWorldAsString(String name);
}
И реализация, которая просто вызывает исключение. (Так как проблема возникает только в SOAPFaultBuilder):
package wstest;
import javax.jws.WebService;
//Service Implementation
@WebService(endpointInterface = "wstest.HelloWorld")
public class HelloWorldImpl implements HelloWorld{
@Override
public String getHelloWorldAsString(String name) {
//return "Hello World JAX-WS " + name;
throw new RuntimeException("Exception for: " + name);
}
}
И класс для публикации веб-службы:
package wstest;
import javax.xml.ws.Endpoint;
//Endpoint publisher
public class HelloWorldPublisher{
public static void main(String[] args) {
Endpoint.publish("http://localhost:9999/ws/hello", new HelloWorldImpl());
}
}
Я запускаю HelloWorldPublisher, а затем запускаю этот клиент для него:
package wstest;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class HelloWorldClient{
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:9999/ws/hello?wsdl");
//1st argument service URI, refer to wsdl document above
//2nd argument is service name, refer to wsdl document above
QName qname = new QName("http://wstest/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
System.out.println(hello.getHelloWorldAsString("Matt"));
}
}
Это правильно выплевывает исключение, которое было выброшено веб-службой. Однако, когда я добавляю любую версию jaxb-impl.jar, будь то в classpath или в одобренной lib, я получаю следующую трассировку стека:
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:107)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:107)
at $Proxy19.getHelloWorldAsString(Unknown Source)
at wstest.HelloWorldClient.main(HelloWorldClient.java:21)
Caused by: java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.<clinit>(SOAPFaultBuilder.java:533)
... 5 more
Исключение происходит потому, что com.sun.xml.bind.v2.runtime.JAXBContextImpl из моего jaxb-impl расширяет com.sun.xml.bind.api.JAXBRIContext вместо com.sun.xml.internal.bind.api.JAXBRIContext (обратите внимание на отсутствующий "внутренний" подпакет в иерархии пакетов).
Также, согласно неофициальному руководству JAXB, говорится, что вам нужно использовать одобренную библиотеку для корректного переопределения версии JAXB. Как оказалось, SOAPFaultBuilder использует JAXBContext.newInstance() для поиска пути к классу для файла с именем /META-INF/services/javax.xml.bind.JAXBContext
затем вручную загрузите (и рефлексивно создайте) JAXBContext на основе имени класса, указанного в файле. Так что это не имеет значения - classpath или одобренная lib дают вам такое же поведение.
Один из способов это добавить -Djavax.xml.bind.JAXBContext=com.sun.xml.internal.bind.v2.ContextFactory
в командной строке, которая заставляет JAXBContext.newInstance() игнорировать /META-INF/services/javax.xml.bind.JAXBContext
файл на пути к классам и вручную указывает встроенную версию JAXB. Другой обходной путь - просто не указывать свой собственный JAXB и использовать версию, встроенную в JDK, но из неофициального руководства по JAXB видно, что Sun разработала эту систему, чтобы иметь возможность обрабатывать собственную реализацию JAXB. Кто-нибудь был в состоянии успешно предоставить версию JAXB и все еще быть в состоянии успешно захватить сообщения об ошибках? (Все работает нормально для меня, пока нет ошибок, генерируемых веб-службой).
3 ответа
Я застрял в этой проблеме, но смог использовать "обходные пути", перечисленные в этом разделе "Вопросы и ответы", установив системное свойство следующим образом:
System.setProperty("javax.xml.bind.JAXBContext",
"com.sun.xml.internal.bind.v2.ContextFactory");
Суть этой проблемы в том, что в API JAXB произошли изменения, и реализация среды выполнения, которую вы пытаетесь использовать, не соответствует версии API JAXB, связанной с JDK.
Чтобы использовать другую версию, вы должны скопировать базовые версии jaxb-api.jar и jaxws-api.jar в одобренную библиотеку (например, %JAVA_HOME%\lib\endorsed
).
Полный список опций приведен в разделе 7.1.2 неофициального руководства JAXB.
Ошибочно копировать файлы jar реализации (например, jaxb-impl.jar) в одобренную библиотеку, они просто должны быть в вашем пути к классам.
Также обратите внимание, что вы можете столкнуться с проблемами, если попытаетесь использовать более новую версию jaxb, не включая также совместимую версию jaxws. Это потому, что старый jaxws пытается сослаться на старый jaxb, поэтому, если вы меняете один, убедитесь, что вы делаете оба. (Стек-трассировка в упаковке com.sun.xml.internal.ws
подразумевает старую реализацию jax-ws. Даже последний выпуск Java все еще поставляется со старой версией 1 jaxb и jaxws apis).
Другое возможное решение без изменения системных свойств описано в документе API
Вы можете разместить файл jaxb.properties внутри пакета класса вашей модели.
javax.xml.bind.context.factory=com.sun.xml.internal.bind.v2.ContextFactory