У меня есть сторонняя банка, которая поставляется с jaxb-impl.jar и включает ее в свой явный путь к классам. Проблема в том, что, по-видимому, поставка вашей собственной версии 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, будь то в пути к классам или в поддерживаемой 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, они говорят, что вам нужно использовать одобренную lib, чтобы правильно переопределить версию 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 и все еще иметь возможность успешно записывать сообщения об ошибках? (Все работает отлично для меня, пока нет ошибок, сгенерированных веб-службой).