Вот тот, который работает для меня (в частности, через http и https ). Пример JAX-WS Oracle JDK 1.8.0_51, работающего с классами, созданными Apache CXF 3.1.1.
Обратите внимание, что удаленный WSDL в любом случае получается только при первом вызове. В зависимости от модели использования (долго работающая программа) это может быть вполне приемлемо.
Основы:
- Загрузите WSDL с удаленного хоста и сохраните как файл:
wget --output-document=wsdl_raw.xml $WSDL_URL
- Вы можете
xmllint --format wsdl_raw.xml > wsdl.xml
для хорошего форматирования
- Генерация клиентских классов с использованием инструмента командной строки :
./cxf/bin/wsdl2java -d output/ -client -validate wsdl.xml
и импорт в ваш проект
Убедитесь, что в файле WSDL присутствуют определения служб для http и https . В моем случае у провайдера не было одного для https (но он принимал https трафика), и мне пришлось добавить его вручную. Что-то вроде этого должно быть в WSDL:
<wsdl:service name="fooservice">
<wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort">
<soap:address location="http://ws.example.com/a/b/FooBarWebService"/>
</wsdl:port>
</wsdl:service>
<wsdl:service name="fooservice-secured">
<wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort">
<soap:address location="https://ws.example.com/a/b/FooBarWebService"/>
</wsdl:port>
</wsdl:service>
CXF должен был сгенерировать класс, который реализует javax.xml.ws.Service
, называемый, например, Fooservice
, с соответствующими конструкторами:
public class Fooservice extends Service {
public Fooservice(URL wsdlLocation) {
super(wsdlLocation, SERVICE);
}
public Fooservice(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
}
public Fooservice() {
super(WSDL_LOCATION, SERVICE);
}
...etc...
Где-то в вашем коде (здесь Groovy для облегчения чтения) вы инициализируете вышеприведенный экземпляр Service
, а затем вызываете порт. Здесь, в зависимости от флага secure
, мы используем https или http :
static final String NAMESPACE = 'com.example.ws.a.b'
static final QName SERVICE_NAME_HTTP = new QName(NAMESPACE, 'fooservice')
static final QName SERVICE_NAME_HTTPS = new QName(NAMESPACE, 'fooservice-secured')
Fooservice wsService
File wsdlfile = new File('/somewhere/on/disk/wsdl.xml')
// If the file is missing there will be an exception at connect
// time from sun.net.www.protocol.file.FileURLConnection.connect
// It should be possible to denote a resource on the classpath
// instead of a file-on-disk. Not sure how, maybe by adding a handler
// for a 'resource:' URL scheme?
URI wsdlLocationUri = java.nio.file.Paths(wsdlfile.getCanonicalPath()).toUri()
if (secure) {
wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTPS)
}
else {
wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP)
}
SomeServicePort port = wsService.getSomeServicePort()
port.doStuff()
Альтернатива, которая загружает WSDL по соединению, отдельному от соединения, используемого для вызова службы (используйте tcpdump -n -nn -s0 -A -i eth0 'tcp port 80'
для наблюдения за трафиком), заключается в следующем:
URI wsdlLocationUri
if (secure) {
wsdlLocationUri = new URI('https://ws.example.com/a/b/FooBarWebService?wsdl')
}
else {
wsdlLocationUri = new URI('http://ws.example.com/a/b/FooBarWebService?wsdl')
}
Fooservice wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP)
SomeServicePort port = wsService.getSomeServicePort()
port.doStuff()
Обратите внимание, что на самом деле правильно используется https , если wsdlLocationUri
указывает https , несмотря на то, что wsService
был инициализирован с SERVICE_NAME_HTTP
. (Не уверен, почему - служба использует схему, используемую для получения ресурса WSDL?)
И это все.
Для отладки соединения введите:
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true
для JVM в командной строке. Это запишет информацию из кода http-соединения в stdout (к сожалению, НЕ в java.util.logging
. Oracle, пожалуйста!).