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, mientras guardo el archivo WSDL en la carpeta correcta del sistema de archivos, todo va bien. Cuando lo despliego 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. He buscado en la web y he encontrado los siguientes posts pero no he sido capaz de hacerlo funcionar: https://stackoverflow.com/questions/764772/jax-ws-loading-wsdl-from-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 (es una aplicación legacy que tengo que actualizar con este nuevo cliente de servicios web). A continuación se muestra 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 puso una copia del WSDL en web-inf/wsdl/client/SOAService, así 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 al sistema de ficheros (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. Falló con: \WEB-INF\wsdl\client\SOAService.wsdl (no se puede encontrar la ruta)
Así que, en primer lugar, ¿debo actualizar la wsdllocation de la clase proxy? Después, ¿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: I'he encontrado este otro enlace - http://jianmingli.com/wp/?cat=41, que dicen que poner el WSDL en el classpath. Me da vergüenza preguntar: ¿cómo lo pongo en el classpath de la aplicación web?
La mejor opción es utilizar jax-ws-catalog.xml
Cuando compile el archivo WSDL local, anule la ubicación del WSDL y establezca algo como
pre> http://localhost/wsdl/SOAService.wsdl
Don't worry this is only a URI and not a URL , meaning you don't have the WSDL available at that address.
Puedes hacerlo pasando la opción wsdllocation al compilador wsdl to java.
Haciendo esto 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;
}
Observe que file:// ha 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.wsdly fallará, ya que tal WSDL no estará disponible.
Pero con jax-ws-catalog.xml puedes 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ás haciendo es decirle a jax-ws que cuando necesite cargar WSDL desde
http://localhost/wsdl/SOAService.wsdl, debe 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?
Debería estar en el directorio META-INF de su jar de aplicación.
Así que algo como esto
pre> ABCD.jar |META-INF |jax-ws-catalog.xml |wsdl |SOAService.wsdl
De esta forma ni siquiera tienes que sobreescribir la URL en tu cliente que accede al proxy. El WSDL es recogido desde dentro de tu JAR, y evitas tener que tener rutas de sistema de ficheros 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 le sirva de ayuda
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 que está configurado para ser /META-INF/wsdl/YourWebServiceName.wsdl relativo a la ubicación JavaSource (o /src, dependiendo de cómo tengas configurado tu proyecto). Durante el proceso de compilació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 XSDs importados 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 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;
}
}
Tal vez esto podría ayudar también. Es sólo un enfoque diferente que no utiliza el "catálogo" enfoque.
Tenía el exact 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 de un servidor web), todavía se hace referencia a la ubicación original incrustado en el árbol de origen del proceso de servidor.
Después de Muchas horas tratando de depurar esto, me di cuenta de que la excepción siempre se lanzaba desde la misma línea (en mi caso 41). Finalmente, esta mañana, decidí enviar mi código fuente del cliente 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 shock y horror encontré un montón de archivos de clase mezclados con mis archivos .java dentro de mi árbol de código fuente del cliente. ¡Qué bizarro! Sospecho que son 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