java - crear - ¿Cómo evitar la necesidad de especificar la ubicación de WSDL en un cliente de servicio web generado por CXF o JAX-WS?
crear cliente web service eclipse wsdl (8)
¿Es posible que puedas evitar usar wsdl2java? Puede utilizar directamente las API CXF FrontEnd para invocar su servicio web SOAP. La única pega es que necesitas crear tu SEI y VO en tu cliente final. Aquí hay un código de muestra.
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());
}
}
Puede ver el tutorial completo aquí http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/
Cuando genero un cliente de servicio web usando wsdl2java de CXF (que genera algo similar a wsimport), a través de maven, mis servicios comienzan con códigos como este:
@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;
}
La ruta absoluta codificada realmente apesta. La clase generada no funcionará en ninguna otra computadora que no sea la mía.
La primera idea es colocar el archivo WSDL (más todo lo que importa, otros WSDL y XSD) en algún lugar en un archivo jar y clonarlo. Pero queremos evitar esto. Dado que todo eso fue generado por CXF y JAXB basados en los WSDL y XSD, no vemos sentido en la necesidad de conocer el WSDL en tiempo de ejecución.
El atributo wsdlLocation está destinado a anular la ubicación WSDL (al menos esto es lo que leí en alguna parte), y su valor predeterminado es "". Como usamos maven, intentamos incluir <wsdlLocation></wsdlLocation>
dentro de la configuración de CXF para intentar forzar al generador de origen a dejar wsdlLocation en blanco. Sin embargo, esto simplemente hace que ignore la etiqueta XML porque está vacía. Hicimos un hack vergonzoso realmente feo, usando <wsdlLocation>" + "</wsdlLocation>
.
Esto cambia otros lugares también:
@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;
}
Entonces, mis preguntas son:
¿Realmente necesitamos una ubicación WSDL incluso si todas las clases fueron generadas por CXF y JAXB? ¿Si es así por qué?
Si realmente no necesitamos la ubicación WSDL, ¿cuál es la forma correcta y limpia de hacer que CXF no lo genere y lo evite por completo?
¿Qué efectos secundarios negativos podríamos obtener con ese truco? Todavía no podemos probar eso para ver qué sucede, así que si alguien pudiera decirlo con anticipación, estaría bien.
1) En algunos casos, sí. Si el WSDL contiene elementos como Políticas y de tal manera que dirige el comportamiento del tiempo de ejecución, entonces es posible que se requiera el WSDL en tiempo de ejecución. Los artefactos no se generan para cosas relacionadas con las políticas y tal. Además, en algunos casos poco claros de RPC / Literal, no todos los espacios de nombres que se necesitan se generan en el código generado (por especificación). Por lo tanto, el wsdl sería necesario para ellos. Casos oscuros sin embargo.
2) Pensé que algo así podría funcionar. ¿Qué versión de CXF? Eso suena como un error. Puedes probar una cadena vacía allí (solo espacios). No estoy seguro de si eso funciona o no. Dicho esto, en su código, puede usar el constructor que toma la URL WSDL y acaba de pasar el nulo. El wsdl no se usará.
3) Solo las limitaciones anteriores.
En serio, la respuesta principal no funciona para mí. intentado cxf.version 2.4.1 y 3.0.10. y genere una ruta absoluta con wsdlLocation todas las veces.
Mi solución es usar el comando wsdl2java
en el apache-cxf-3.0.10/bin/
with -wsdlLocation classpath:wsdl/QueryService.wsdl
.
Detalle:
wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl
Finalmente descubrí la respuesta correcta a esta pregunta hoy.
<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>
Observe que he puesto como prefijo el valor en wsdlLocation
con classpath:
Esto le dice al complemento que el wsdl estará en el classpath en lugar de una ruta absoluta. Entonces generará un código similar a esto:
@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;
}
Para aquellos que usan org.jvnet.jax-ws-commons:jaxws-maven-plugin
para generar un cliente a partir de WSDL en tiempo de compilación:
- Coloque el WSDL en algún lugar de su
src/main/resources
- No prefija
wsdlLocation
conclasspath:
- Prefijo el
wsdlLocation
con/
Ejemplo:
- WSDL se almacena en
/src/main/resources/foo/bar.wsdl
- Configure
jaxws-maven-plugin
con<wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>
y<wsdlLocation>/foo/bar.wsdl</wsdlLocation>
Pude generar
static {
WSDL_LOCATION = null;
}
configurando el archivo pom para tener un valor nulo para 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>
Usamos
wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"
En otras palabras, use una ruta relativa a la ruta de clase.
Creo que el WSDL puede ser necesario en tiempo de ejecución para la validación de mensajes durante el marshal / unmarshal.
Actualización para CXF 3.1.7
En mi caso puse los archivos WSDL en src/main/resources
y agregué esta ruta a mis Srouces en Eclipse (clic derecho en Project-> Build Path-> Configure Build Path ...-> Source [Tab] -> Add Folder )
Así es como se ve mi archivo pom
y, como se puede ver, NO wsdlLocation
opción 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>
Y aquí está el Servicio generado. Como se puede ver, la URL se obtiene de ClassLoader y no de Absolute File-Path
@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;
}