Как избежать необходимости указывать местоположение WSDL в CXF или JAX-WS, сгенерированном веб-сервисом?
Когда я создаю клиент webservice, используя wsdl2java из CXF (который генерирует нечто похожее на wsimport), через maven мои службы начинаются с таких кодов:
@WebServiceClient(name = "StatusManagement",
wsdlLocation = "c:/some_absolute_path_to_a_wsdl_file.wsdl",
targetNamespace = "http://tempuri.org/")
public class StatusManagement extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
static {
URL url = null;
try {
url = new URL("c:/some_absolute_path_to_a_wsdl_file.wsdl");
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from c:/some_absolute_path_to_a_wsdl_file.wsdl");
// e.printStackTrace();
}
WSDL_LOCATION = url;
}
Твердый кодированный абсолютный путь действительно сосет. Сгенерированный класс не будет работать ни на одном другом компьютере, кроме моего.
Первая идея - разместить файл WSDL (плюс все, что он импортирует, другие WSDL и XSD) где-нибудь в jar файле и classpath. Но мы хотим избежать этого. Поскольку все это было создано CXF и JAXB, основанными на WSDL и XSD, мы не видим смысла знать WSDL во время выполнения.
Атрибут wsdlLocation предназначен для переопределения местоположения WSDL (по крайней мере, это то, что я где-то читал), а значение по умолчанию - "". Поскольку мы используем maven, мы попытались включить <wsdlLocation></wsdlLocation>
в конфигурацию CXF, чтобы попытаться заставить исходный генератор оставить пустой wsdlLocation. Однако это просто игнорирует тег XML, поскольку он пуст. Мы сделали очень уродливый позорный взлом, используя <wsdlLocation>" + "</wsdlLocation>
.
Это также изменяет другие места:
@WebServiceClient(name = "StatusManagement",
wsdlLocation = "" + "",
targetNamespace = "http://tempuri.org/")
public class StatusManagement extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
static {
URL url = null;
try {
url = new URL("" + "");
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from " + "");
// e.printStackTrace();
}
WSDL_LOCATION = url;
}
Итак, мои вопросы:
-
Нам действительно нужно местоположение WSDL, даже если все классы были созданы CXF и JAXB? Если да, то почему?
-
Если нам действительно не нужно место WSDL, то какой правильный и чистый способ заставить CXF не генерировать его и полностью избегать?
-
Какие плохие побочные эффекты мы можем получить с этим взломом? Мы все еще не можем проверить это, чтобы посмотреть, что произойдет, поэтому, если кто-то может сказать заранее, было бы хорошо.
Ответы
Ответ 1
Наконец я понял правильный ответ на этот вопрос.
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/wsdl/FooService.wsdl</wsdl>
<wsdlLocation>classpath:wsdl/FooService.wsdl</wsdlLocation>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Обратите внимание, что я префикс значения в wsdlLocation
с помощью classpath:
Это говорит плагину, что wsdl будет находиться в пути к классам вместо абсолютного пути. Затем он сгенерирует код, подобный этому:
@WebServiceClient(name = "FooService",
wsdlLocation = "classpath:wsdl/FooService.wsdl",
targetNamespace = "http://org/example/foo")
public class Foo_Service extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://org/example/foo", "Foo");
public final static QName FooSOAPOverHTTP = new QName("http://org/example/foo", "Foo_SOAPOverHTTP");
static {
URL url = Foo_Service.class.getClassLoader().getResource("wsdl/FooService.wsdl");
if (url == null) {
java.util.logging.Logger.getLogger(Foo_Service.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "classpath:wsdl/FooService.wsdl");
}
WSDL_LOCATION = url;
}
Ответ 2
Мы используем
wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"
Другими словами, используйте путь относительно пути к классам.
Я считаю, что WSDL может понадобиться во время выполнения для проверки сообщений во время маршала/немаршала.
Ответ 3
Для тех, кто использует org.jvnet.jax-ws-commons:jaxws-maven-plugin
для создания клиента из WSDL во время сборки:
- Поместите WSDL где-нибудь в
src/main/resources
- Сделайте не префикс
wsdlLocation
с помощью classpath:
- Префикс
wsdlLocation
с помощью /
Пример:
- WSDL хранится в
/src/main/resources/foo/bar.wsdl
- Настройте
jaxws-maven-plugin
с помощью <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>
и <wsdlLocation>/foo/bar.wsdl</wsdlLocation>
Ответ 4
1) В некоторых случаях да. Если WSDL содержит такие вещи, как "Политики" и такие, которые направляют поведение во время выполнения, тогда WSDL может потребоваться во время выполнения. Артефакты не генерируются для связанных с политикой вещей и т.д. Кроме того, в некоторых неясных случаях RPC/Literal не все пространства имен, которые необходимы, выводятся в сгенерированном коде (для каждой спецификации). Таким образом, wsdl потребуется для них. Неясные случаи.
2) Я думал, что-то вроде будет работать. Какая версия CXF? Это звучит как ошибка. Вы можете попробовать пустую строку (просто пробелы). Не уверен, работает ли это или нет. Тем не менее, в вашем коде вы можете использовать конструктор, который берет URL WSDL и просто передает null. Wsdl не будет использоваться.
3) Только ограничения выше.
Ответ 5
Мне удалось сгенерировать
static {
WSDL_LOCATION = null;
}
путем настройки pom файла, чтобы иметь значение null для wsdlurl:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/service.wsdl</wsdl>
<extraargs>
<extraarg>-client</extraarg>
<extraarg>-wsdlLocation</extraarg>
<wsdlurl />
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Ответ 6
Возможно ли, что вы можете избежать использования wsdl2java? Вы можете сразу использовать API CXF FrontEnd для вызова вашего SOAP Webservice. Единственный улов - вам нужно создать свой SEI и VO на своем конце клиента. Вот пример кода.
package com.aranin.weblog4j.client;
import com.aranin.weblog4j.services.BookShelfService;
import com.aranin.weblog4j.vo.BookVO;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
public class DemoClient {
public static void main(String[] args){
String serviceUrl = "http://localhost:8080/weblog4jdemo/bookshelfservice";
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(BookShelfService.class);
factory.setAddress(serviceUrl);
BookShelfService bookService = (BookShelfService) factory.create();
//insert book
BookVO bookVO = new BookVO();
bookVO.setAuthor("Issac Asimov");
bookVO.setBookName("Foundation and Earth");
String result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
bookVO = new BookVO();
bookVO.setAuthor("Issac Asimov");
bookVO.setBookName("Foundation and Empire");
result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
bookVO = new BookVO();
bookVO.setAuthor("Arthur C Clarke");
bookVO.setBookName("Rama Revealed");
result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
//retrieve book
bookVO = bookService.getBook("Foundation and Earth");
System.out.println("book name : " + bookVO.getBookName());
System.out.println("book author : " + bookVO.getAuthor());
}
}
Вы можете увидеть полный учебник здесь http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/
Ответ 7
Обновление для CXF 3.1.7
В моем случае я поместил WSDL файлы в src/main/resources
и добавил этот путь к моему Srouces in Eclipse (щелкните правой кнопкой мыши по Project-> Путь сборки → Настроить путь сборки...-> Источник [Tab] → Добавить Папка).
Вот как выглядит мой pom
файл, и, как видно, нет wsdlLocation
опции wsdlLocation
:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>classpath:wsdl/FOO_SERVICE.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
И вот сгенерированная Служба. Как видно, URL-адрес получается из класса ClassLoader, а не из Абсолютного пути к файлу
@WebServiceClient(name = "EventService",
wsdlLocation = "classpath:wsdl/FOO_SERVICE.wsdl",
targetNamespace = "http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/")
public class EventService extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventService");
public final static QName EventPort = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventPort");
static {
URL url = EventService.class.getClassLoader().getResource("wsdl/FOO_SERVICE.wsdl");
if (url == null) {
java.util.logging.Logger.getLogger(EventService.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "classpath:wsdl/FOO_SERVICE.wsdl");
}
WSDL_LOCATION = url;
}
Ответ 8
Серьезно, главный ответ не работает для меня. попробовал cxf.version 2.4.1 и 3.0.10. и генерировать абсолютный путь с помощью wsdlLocation каждый раз.
Мое решение заключается в использовании команды wsdl2java
в apache-cxf-3.0.10\bin\
с -wsdlLocation classpath:wsdl/QueryService.wsdl
.
Деталь:
wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl