Cliente JAX-WS: ¿cuál es la ruta correcta para acceder al WSDL local?


86

El problema es que necesito crear un cliente de servicio web a partir de un archivo que me han proporcionado. He almacenado este archivo en el sistema de archivos local y, aunque guardo el archivo WSDL en la carpeta del sistema de archivos correcta, todo está bien. Cuando lo implemento en un servidor o elimino el WSDL de la carpeta del sistema de archivos, el proxy no puede encontrar el WSDL y aparece un error. Busqué en la web y encontré las siguientes publicaciones todavía no he podido hacer que funcione:
JAX-WS Cargando WSDL desde jar
http://www.java.net/forum/topic/glassfish/metro -y-jaxb / client-jar-cant-find-local-wsdl-0
http://blog.vinodsingh.com/2008/12/locally-packaged-wsdl.html

Estoy usando NetBeans 6.1 (esta es una aplicación heredada que debo actualizar con este nuevo cliente de servicio web). A continuación se muestra la clase de proxy JAX-WS:

    @WebServiceClient(name = "SOAService", targetNamespace = "http://soaservice.eci.ibm.com/", wsdlLocation = "file:/C:/local/path/to/wsdl/SOAService.wsdl")
public class SOAService
    extends Service
{

    private final static URL SOASERVICE_WSDL_LOCATION;
    private final static Logger logger = Logger.getLogger(com.ibm.eci.soaservice.SOAService.class.getName());

    static {
        URL url = null;
        try {
            URL baseUrl;
            baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
            url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
        } catch (MalformedURLException e) {
            logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
            logger.warning(e.getMessage());
        }
        SOASERVICE_WSDL_LOCATION = url;
    }

    public SOAService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public SOAService() {
        super(SOASERVICE_WSDL_LOCATION, new QName("http://soaservice.eci.ibm.com/", "SOAService"));
    }

    /**
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP() {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class);
    }

    /**
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP(WebServiceFeature... features) {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class, features);
    }

}


Este es mi código para usar el proxy:

   WebServiceClient annotation = SOAService.class.getAnnotation(WebServiceClient.class);
   // trying to replicate proxy settings
   URL baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("");//note : proxy uses "."
   URL url = new URL(baseUrl, "/WEB-INF/wsdl/client/SOAService.wsdl");
   //URL wsdlUrl = this.getClass().getResource("/META-INF/wsdl/SOAService.wsdl"); 
   SOAService serviceObj = new SOAService(url, new QName(annotation.targetNamespace(), annotation.name()));
   proxy = serviceObj.getSOAServiceSOAP();
   /* baseUrl;

   //classes\com\ibm\eci\soaservice
   //URL url = new URL(baseUrl, "../../../../wsdl/SOAService.wsdl");

   proxy = new SOAService().getSOAServiceSOAP();*/
   //updating service endpoint 
   Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
   ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
   ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WebServiceUrl);

NetBeans colocó una copia del WSDL en web-inf / wsdl / client / SOAService , así que no quiero agregarlo también a META-INF . Las clases de servicio están en WEB-INF / classes / com / ibm / eci / soaservice / y la variable baseurl contiene la ruta completa del sistema de archivos (c: \ path \ to \ the \ project ... \ soaservice). El código anterior genera el error:

javax.xml.ws.WebServiceException: no se pudo acceder al WSDL en: archivo: /WEB-INF/wsdl/client/SOAService.wsdl. Falló con: \ WEB-INF \ wsdl \ client \ SOAService.wsdl (no se puede encontrar la ruta)

Entonces, en primer lugar, ¿debo actualizar la ubicación wsd de la clase de proxy? Entonces, ¿cómo le digo a la clase SOAService en WEB-INF / classes / com / ibm / eci / soaservice que busque el WSDL en \ WEB-INF \ wsdl \ client \ SOAService.wsdl?

EDITADO : Encontré este otro enlace: http://jianmingli.com/wp/?cat=41 , que dice poner el WSDL en el classpath. Me avergüenza preguntar: ¿cómo lo coloco en la ruta de clases de la aplicación web?


Respuestas:


117

La mejor opción es usar jax-ws-catalog.xml

Cuando compile el archivo WSDL local, anule la ubicación WSDL y configúrelo en algo como

http: //localhost/wsdl/SOAService.wsdl

No se preocupe, esto es solo un URI y no una URL, lo que significa que no tiene que tener el WSDL disponible en esa dirección.
Puede hacer esto pasando la opción wsdllocation al compilador wsdl to java.

Hacerlo cambiará su código proxy de

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

a

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "http://localhost/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'http://localhost/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

Archivo de aviso: // cambiado a http: // en el constructor de URL.

Ahora viene en jax-ws-catalog.xml. Sin jax-ws-catalog.xml, jax-ws intentará cargar el WSDL desde la ubicación

http: //localhost/wsdl/SOAService.wsdl
y fallar, ya que tal WSDL no estará disponible.

Pero con jax-ws-catalog.xml puede redirigir jax-ws a un WSDL empaquetado localmente siempre que intente acceder al WSDL @

http: //localhost/wsdl/SOAService.wsdl
.

Aquí está jax-ws-catalog.xml

<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
        <system systemId="http://localhost/wsdl/SOAService.wsdl"
                uri="wsdl/SOAService.wsdl"/>
    </catalog>

Lo que está haciendo es decirle a jax-ws que siempre que necesite cargar WSDL desde

http: //localhost/wsdl/SOAService.wsdl
, debería cargarlo desde la ruta local wsdl / SOAService.wsdl.

Ahora, ¿dónde debería poner wsdl / SOAService.wsdl y jax-ws-catalog.xml? Esa es la pregunta del millón de dólares, ¿no?
Debe estar en el directorio META-INF de su aplicación jar.

entonces algo como esto

ABCD.jar  
| __ META-INF    
    | __ jax-ws-catalog.xml  
    | __ wsdl  
        | __ SOAService.wsdl  

De esta manera, ni siquiera tiene que anular la URL en su cliente que accede al proxy. El WSDL se obtiene de su JAR y evita tener que tener rutas de sistema de archivos codificadas en su código.

Más información en jax-ws-catalog.xml http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

Espero que ayude


ok, no he podido resolver el problema de esta manera dentro de la aplicación web: he intentado poner wsdl dentro de web-inf sin éxito, probablemente por mi falta de conocimiento. De todos modos, funciona con un frasco, así que haré una biblioteca contenedora, como debería haberse hecho desde el principio. Gracias por su apoyo

Pude usar esta respuesta con éxito y creo que esta es una mejor solución que todas las otras alternativas que documentan otros artículos y tutoriales. Entonces, para mí, esta es la mejor práctica. Me pregunto por qué esta solución no está documentada en los otros artículos y tutoriales oficiales que cubren el tema JAX-WS.
Rahul Khimasia

19

Otro enfoque que hemos adoptado con éxito es generar el código proxy del cliente WS usando wsimport (de Ant, como una tarea Ant) y especificar el atributo wsdlLocation.

<wsimport debug="true" keep="true" verbose="false" target="2.1" sourcedestdir="${generated.client}" wsdl="${src}${wsdl.file}" wsdlLocation="${wsdl.file}">
</wsimport>

Dado que ejecutamos esto para un proyecto con múltiples WSDL, el script resuelve el valor $ (wsdl.file} dinámicamente que está configurado para ser /META-INF/wsdl/YourWebServiceName.wsdl relativo a la ubicación de JavaSource (o / src, dependiendo de cómo haya configurado su proyecto). Durante el proceso de compilación, los archivos WSDL y XSDs se copian en esta ubicación y se empaquetan en el archivo JAR. (similar a la solución descrita por Bhasakar anteriormente)

MyApp.jar
|__META-INF
   |__wsdl
      |__YourWebServiceName.wsdl
      |__YourWebServiceName_schema1.xsd
      |__YourWebServiceName_schmea2.xsd

Nota: asegúrese de que los archivos WSDL utilicen referencias relativas a los XSD importados y no a las URL http:

  <types>
    <xsd:schema>
      <xsd:import namespace="http://valueobject.common.services.xyz.com/" schemaLocation="YourWebService_schema1.xsd"/>
    </xsd:schema>
    <xsd:schema>
      <xsd:import namespace="http://exceptions.util.xyz.com/" schemaLocation="YourWebService_schema2.xsd"/>
    </xsd:schema>
  </types>

En el código generado , encontramos esto:

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2-b05-
 * Generated source version: 2.1
 * 
 */
@WebServiceClient(name = "YourService", targetNamespace = "http://test.webservice.services.xyz.com/", wsdlLocation = "/META-INF/wsdl/YourService.wsdl")
public class YourService_Service
    extends Service
{

    private final static URL YOURWEBSERVICE_WSDL_LOCATION;
    private final static WebServiceException YOURWEBSERVICE_EXCEPTION;
    private final static QName YOURWEBSERVICE_QNAME = new QName("http://test.webservice.services.xyz.com/", "YourService");

    static {
        YOURWEBSERVICE_WSDL_LOCATION = com.xyz.services.webservice.test.YourService_Service.class.getResource("/META-INF/wsdl/YourService.wsdl");
        WebServiceException e = null;
        if (YOURWEBSERVICE_WSDL_LOCATION == null) {
            e = new WebServiceException("Cannot find '/META-INF/wsdl/YourService.wsdl' wsdl. Place the resource correctly in the classpath.");
        }
        YOURWEBSERVICE_EXCEPTION = e;
    }

    public YourService_Service() {
        super(__getWsdlLocation(), YOURWEBSERVICE_QNAME);
    }

    public YourService_Service(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    /**
     * 
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort() {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class);
    }

    /**
     * 
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort(WebServiceFeature... features) {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class, features);
    }

    private static URL __getWsdlLocation() {
        if (YOURWEBSERVICE_EXCEPTION!= null) {
            throw YOURWEBSERVICE_EXCEPTION;
        }
        return YOURWEBSERVICE_WSDL_LOCATION;
    }

}

Quizás esto también pueda ayudar. Es simplemente un enfoque diferente que no utiliza el enfoque de "catálogo".


Me gusta este enfoque ... pero ¿por qué el directorio META-INF?
IcedDante

1
Tenga en cuenta que esto requiere el uso de JAX-WS RI 2.2, no 2.1 que viene con JDK 6 por defecto
ᄂ ᄀ

4

Muchas gracias por la respuesta de Bhaskar Karambelkar, que explica en detalle y solucionó mi problema. Pero también me gustaría reformular la respuesta en tres sencillos pasos para alguien que tiene prisa por arreglar

  1. Haga su referencia de ubicación local wsdl como wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. Cree una carpeta META-INF justo debajo de src. Coloque sus archivos wsdl en una carpeta bajo META-INF, diga META-INF / wsdl
  3. Cree un archivo xml jax-ws-catalog.xml en META-INF como se muestra a continuación

    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="http://localhost/wsdl/yourwsdlname.wsdl" uri="wsdl/yourwsdlname.wsdl" /> </catalog>

Ahora empaqueta tu frasco. No más referencias al directorio local, todo está empaquetado y referenciado dentro


4

Para aquellos que todavía están buscando una solución aquí, la solución más fácil sería usarla <wsdlLocation>, sin cambiar ningún código. Los pasos de trabajo se dan a continuación:

  1. Coloque su wsdl en el directorio de recursos como: src/main/resource
  2. En el archivo pom, agregue wsdlDirectory y wsdlLocation (no se pierda / al comienzo de wsdlLocation), como se muestra a continuación. Mientras que wsdlDirectory se usa para generar código y wsdlLocation se usa en tiempo de ejecución para crear un proxy dinámico.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
    
  3. Luego, en su código java (con un constructor sin argumentos):

    MyPort myPort = new MyPortService().getMyPort();
    
  4. Para completar, proporciono aquí la parte de generación de código completa, con una API fluida en el código generado.

    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.5</version>
    
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-fluent-api</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-tools</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>
    
    <executions>
        <execution>
            <id>wsdl-to-java-generator</id>
            <goals>
                <goal>wsimport</goal>
            </goals>
            <configuration>
                <xjcArgs>
                    <xjcArg>-Xfluent-api</xjcArg>
                </xjcArgs>
                <keep>true</keep>
                <wsdlDirectory>src/main/resources/package</wsdlDirectory>
                <wsdlLocation>/package/my.wsdl</wsdlLocation>
                <sourceDestDir>${project.build.directory}/generated-sources/annotations/jaxb</sourceDestDir>
                <packageName>full.package.here</packageName>
            </configuration>
        </execution>
    </executions>
    



0

Tuvo exactamente el mismo problema que se describe aquí. No importa lo que hice, siguiendo los ejemplos anteriores, para cambiar la ubicación de mi archivo WSDL (en nuestro caso desde un servidor web), todavía estaba haciendo referencia a la ubicación original incrustada dentro del árbol de origen del proceso del servidor.

Después de MUCHAS horas tratando de depurar esto, noté que la Excepción siempre se lanzaba desde la misma línea exacta (en mi caso 41). Finalmente, esta mañana, decidí enviar mi código de cliente fuente a nuestro socio comercial para que al menos puedan entender cómo se ve el código, pero tal vez construir el suyo propio. Para mi sorpresa y horror , encontré un montón de archivos de clase mezclados con mis archivos .java dentro de mi árbol de origen del cliente. ¡¡Qué extraño !! Sospecho que estos fueron un subproducto de la herramienta de creación de clientes JAX-WS.

Una vez que eliminé esos tontos archivos .class y realicé una limpieza completa y una reconstrucción del código del cliente, ¡todo funciona perfectamente! Redonculous !!

YMMV, Andrew

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.