47 votos

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

Supongo que es una pregunta trivial pero después de pasar mucho tiempo probando todos los caminos me he dado por vencido.
El problema es que necesito construir 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, mientras mantengo el archivo WSDL en la carpeta correcta del sistema de archivos, todo está bien. Cuando lo despliego en un servidor o quito el WSDL de la carpeta del sistema de archivos, el proxy no puede encontrar el WSDL y da un error. He buscado en la web y he encontrado los siguientes posts, pero no he sido capaz de hacerlo funcionar:
JAX-WS Carga de WSDL desde jar
http://www.java.net/forum/topic/glassfish/metro-and-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 tengo que actualizar con este nuevo cliente de servicio web). Abajo está la clase 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 utilizar 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 pone una copia del WSDL en web-inf/wsdl/client/SOAService , por lo que no quiero añadirlo a META-INF también. 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: Failed to access the WSDL at: file:/WEB-INF/wsdl/client/SOAService.wsdl. It failed with: \WEB-INF\wsdl\client\SOAService.wsdl (cannot find the path) .

Así que, en primer lugar, ¿debo actualizar el wsdllocation de la clase proxy? Luego, ¿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 ?

Gracias de antemano.

EDITADO : He encontrado este otro enlace - http://jianmingli.com/wp/?cat=41 que dicen que hay que poner el WSDL en el classpath. Me da vergüenza preguntar: ¿cómo lo pongo en el classpath de la aplicación web?

71voto

Bhaskar Karambelkar Puntos 1571

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

Cuando se compila el archivo WSDL local, se anula la ubicación del WSDL y se establece algo como

http://localhost/wsdl/SOAService.wsdl

No te preocupes, esto es sólo un URI y no una URL, lo que significa que no tienes que tener el WSDL disponible en esa dirección.
Puede hacerlo pasando la opción wsdllocation al compilador de wsdl a java.

Al hacerlo, su código proxy cambiará 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;
}

Observe que file:// ha cambiado a http:// en el constructor de la URL.

Ahora entra jax-ws-catalog.xml. Sin jax-ws-catalog.xml jax-ws intentará efectivamente cargar el WSDL desde la ubicación

http://localhost/wsdl/SOAService.wsdl
y fallará, ya que no se dispondrá de dicho WSDL.

Pero con jax-ws-catalog.xml puedes redirigir jax-ws a un WSDL empaquetado localmente cada vez 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ás haciendo es decirle a jax-ws que cuando 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, ¿no?
Debe estar en el directorio META-INF de su jar de aplicación.

así que algo como esto

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

De esta manera, ni siquiera tienes que anular la URL en tu cliente que accede al proxy. El WSDL es recogido desde dentro de tu JAR, y evitas tener rutas de sistema de archivos codificadas en tu código.

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

Espero que eso ayude

11voto

mark Puntos 51

Otro enfoque que hemos adoptado con éxito es generar el código proxy del cliente WS utilizando wsimport (desde 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 WSDLs, el script resuelve el valor $(wsdl.file} dinámicamente, el cual está configurado para ser /META-INF/wsdl/YourWebServiceName.wsdl relativo a la ubicación de JavaSource (o /src, dependiendo de cómo tengas configurado tu proyecto). Durante el proceso de construcción, los archivos WSDL y XSD se copian a 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 están utilizando referencias relativas a cualquier XSD importado y no URLs 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 generado código, 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 sólo un enfoque diferente que no utiliza el enfoque de "catálogo".

0voto

Piko Puntos 3470

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

Después de MUCHAS horas tratando de depurar esto, me di cuenta de que la Excepción era siempre lanzada desde la misma línea exacta (en mi caso 41). Finalmente, esta mañana decidí enviar el código fuente de mi cliente a nuestro socio comercial para que al menos entienda cómo se ve el código, pero quizás construya el suyo propio. A mi shock y horror Encontré un montón de archivos de clase mezclados con mis archivos .java dentro del árbol de fuentes de mi cliente. ¡¡¡Que bizarro!!! Sospecho que son un subproducto de la herramienta de construcción de clientes JAX-WS.

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

ES UNA DECISIÓN QUE PUEDE SER TOMADA POR TODOS LOS INTERESADOS, Andrew

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X