JAX-WS Загрузка WSDL из банки - PullRequest
       103

JAX-WS Загрузка WSDL из банки

44 голосов
/ 19 апреля 2009

Я пишу толстый клиент, который использует службу SOAP для некоторых функций (сообщения об ошибках и т. Д.)

У меня JAX-WS работает нормально, но по умолчанию (по крайней мере, в NetBeans) он получает WSDL с удаленного сервера каждый раз, когда служба инициализируется. Я ожидаю, что это поможет обеспечить некоторую поддержку версий и т. Д., Но это не то, что я хочу.

Я добавил аргумент wsdllocation в wsimport для указания сгенерированных классов на локальный ресурс. Следующий фрагмент представляет собой загрузку URL-адреса для ресурса WSDL из ApplicationService.java.

baseUrl = net.example.ApplicationService.class.getResource(".");
url = new URL(baseUrl, "service.wsdl");

Я почти уверен, что не должно возникать проблем с указанием ресурса, хранящегося в jar-пакете в пакете net / example / resources, и сам jar создается, как и ожидалось Однако служба не будет загружаться ... в частности, я получаю исключение NullPointerException при вызове ApplicationService.getPort ();

Возможно ли это? или просто погоня за диким гусем?

Ответы [ 13 ]

48 голосов
/ 14 мая 2009

Да, это наиболее точно возможно, поскольку я делал это при создании клиентов через javax.xml.ws.EndpointReference, связанный с WS-A класс. Я добавил ссылку на classpath к WSDL в WS-A EndPointReference, и реализация JAX-WS в Metro загрузила его просто отлично. Независимо от загрузки WSDL из WS-A EndPointReference или из файла или URL-адреса http, ваша реализация JAX-WS должна использовать тот же код синтаксического анализа WSDL, что и все, что вы делаете - это разрешаете URL-адреса.

Лучший подход для вас, вероятно, заключается в следующем:

URL wsdlUrl = MyClass.class.getResource(
            "/class/path/to/wsdl/yourWSDL.wsdl");

Service yourService= Service.create(
            wsdlUrl,
            ...);

Где ... представляет QName службы WSDL внутри вашего WSDL. Теперь важно помнить, что ваш WSDL должен быть полным и действительным. Это означает, что если ваш WSDL импортирует файлы XSD или другие WSDL, URL-адреса должны быть правильными. Если вы включили импортированные WSDL и XSD в тот же JAR-файл, что и файл WSDL, вы должны использовать относительные URL-адреса для импорта и сохранить все свои импорта в одном и том же файле JAR. Обработчик URL JAR обрабатывает относительные URL-адреса не как относительные по отношению к пути к классам, а как относительные в файле JAR, поэтому вы не можете импортировать в свой WSDL, который выполняется через JAR, если вы не реализуете собственный обработчик URL-адресов и свой собственный префикс для выполнения. разрешение импорта на основе classpath. Если ваш WSDL импортирует внешние ресурсы, это нормально, но вы подписываетесь на решение вопросов обслуживания, если эти ресурсы когда-либо перемещаются. Даже использование статической копии WSDL из вашего пути к классам противоречит духу WSDL, веб-служб и JAX-WS, но бывают случаи, когда это необходимо.

Наконец, если вы встраиваете статический WSDL, я предлагаю вам по крайней мере настроить конечную точку службы для настройки и тестирования. Код для перенастройки конечной точки вашего клиента веб-службы выглядит следующим образом:

  YourClientInterface client = yourService.getPort(
            new QName("...", "..."),
            YourClientInterface.class);
  BindingProvider bp = (BindingProvider) client;
  bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
                "http://localhost:8080/yourServiceEndpoint");
14 голосов
/ 20 августа 2013

По крайней мере, для недавнего JAX-WS вам не нужно делать какие-либо каталоги схем или программные установки местоположения wsdl ЕСЛИ вы помещаете WSDL в JAR, а затем устанавливаете wsimport wsdlLocation для относительного ресурса путь WSDL в JAR. То есть JAX-WS использует встроенную в Java Class.getResource для загрузки WSDL.

Если вы используете Maven, это что-то вроде:

  <plugin>
    <groupId>org.jvnet.jax-ws-commons</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.3</version>
    <executions>
      <execution>
        <goals>
          <goal>wsimport</goal>
        </goals>
        <!-- Following configuration will invoke wsimport once for each wsdl. -->
        <configuration>
            <!--- VERY IMPORTANT THAT THE PATH START WITH '/' -->
    <wsdlLocation>/com/adamgent/ws/blah.wsdl</wsdlLocation>
    <wsdlDirectory>${basedir}/src/main/resources/com/adamgent/ws</wsdlDirectory>
    <wsdlFiles><wsdlFile>blah.wsdl</wsdlFile></wsdlFiles>
       </configuration>
      </execution>
    </executions>
  </plugin>

Для приведенного выше примера вы, таким образом, поместили бы WSDL, используя макет проекта Maven src/main/resources/com/adamgent/ws.

Убедитесь, что WSDL входит в JAR для Maven, как:

<build>
      <resources>
        <resource>
          <directory>src/main/resources</directory>
        </resource>
      </resources> ....

Теперь ваш сгенерированный wsimport-код и WSDL находятся в автономном JAR-файле. Чтобы использовать сервис, вам не нужно указывать местоположение WSDL, это так же просто, как:

BlahService myService = new BlayService_Service().getBlahServicePort();

Должно быть несложно отобразить это на wsimport ANT.

6 голосов
/ 20 ноября 2009

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

Если следующая строка в классе обслуживания

baseUrl = net.example.ApplicationService.class.getResource(".");

изменено на

baseUrl = net.example.ApplicationService.class.getResource("");

работает нормально даже с WSDL, который упакован в JAR. Не уверен в точном предполагаемом поведении getResource () в любом из этих случаев, но у меня пока не возникало проблем с этим подходом для нескольких версий ОС и Java.

5 голосов
/ 10 апреля 2013

Что вы описываете, это ошибка в JAX-WS: JAX_WS-888 - Неверный код для разрешения URL для пользовательского wsdlLocation .

Это было исправлено для V2.2, поэтому просто установка wsdlLocation, как вы пишете, теперь должна работать.

3 голосов
/ 08 января 2010

Другой ответ - использовать

new Service(wsdllocation, servicename );

чтобы получить объект службы.

Вот так я решил проблему.

3 голосов
/ 06 мая 2009

Если у вашего пути к классам есть "." в нем Class.getResource (".") вернет URL-адрес каталога, из которого вы выполнили команду java. Иначе, он вернет ноль. Отрегулируйте wsdllocation соответственно.

2 голосов
/ 24 июня 2011

Я заменил расположение WSDL перед тем, как собирать jar-файл клиента.

  1. Скопируйте WSDL в классы dir.
  2. Замените класс Service ссылкой на WSDL, используя classpath.
  3. сборка клиентских заглушек.
  4. баночка окурков.
<copy todir="@{dest-dir}/@{dir-package}" verbose="@{verbose}">
  <fileset dir="@{dir-wsdl}" includes="*.wsdl,*.xsd" />
</copy>
<echo message="Replacing Service to point to correct WSDL path..." />
<replaceregexp match="new URL(.*)" replace='Class.class.getResource("@{name-wsdl}");' flags="gm">
  <fileset dir="@{source-dest-dir}">
    <include name="@{dir-package}/*Service.java" />
  </fileset>
</replaceregexp>
<replaceregexp match="catch (.*)" replace='catch (Exception ex) {' flags="gm">
  <fileset dir="@{source-dest-dir}">
    <include name="@{dir-package}/*Service.java" />
  </fileset>
</replaceregexp>
2 голосов
/ 09 июня 2009

Я наткнулся на ту же проблему. JAXWS генерирует клиентский код, используя трюк MyService.class.getResource(".") для загрузки файла wsdl ... но после тестирования это работает, только если файл класса находится в каталоге на файловой системе Если файл класса находится в JAR, этот вызов возвращает ноль для URL.

Звучит как ошибка в JDK, поскольку, если вы создадите свой URL-адрес следующим образом:

final URL url = new URL( MyService.class.getResource( MyService.class.getSimpleName() + ".class"), "myservice.wsdl");

тогда это также работает, если класс и wsdl объединены в банку.

Полагаю, большинство людей на самом деле собираются в банку!

1 голос
/ 02 сентября 2015

Вот тот, который работает для меня (в частности, через 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, пожалуйста!).

1 голос
/ 19 апреля 2009

Вот мой хакерский обходной путь.

Я распаковываю WSDL из банки и записываю его в файл рядом с банкой:

File wsdl = new File("../lib/service.wsdl");
InputStream source = getClass().getResource("resources/service.wsdl").openStream();
FileOutputStream out = new FileOutputStream(wsdl);

byte[] buffer = new byte[512];
int read;
while((read = source.read(buffer)) >= 0) {
    out.write(buffer, 0, read);
}

Затем укажите классы обслуживания на file:../lib/service.wsdl.

Это работает, но я буду признателен, если кто-нибудь покажет мне более элегантное решение.

...