JAX-WS Caricamento WSDL dal jar

Sto scrivendo un fat client che fa uso di un servizio SOAP per alcune funzionalità (bug reporting ecc.)

JAX-WS funziona correttamente, ma per impostazione predefinita (almeno in netbeans) recupera il WSDL dal server remoto ogni volta che il servizio viene inizializzato. Mi aspetto che questo aiuti a fornire un supporto per il controllo delle versioni ecc., Ma non è quello che voglio.

Ho aggiunto l’ wsdllocation wsdllocation a wsimport per indirizzare le classi generate a una risorsa locale. Lo snippet seguente è il caricamento dell’URL per la risorsa WSDL da ApplicationService.java.

 baseUrl = net.example.ApplicationService.class.getResource("."); url = new URL(baseUrl, "service.wsdl"); 

Sono abbastanza sicuro che non dovrebbe avere problemi a puntare a una risorsa memorizzata all’interno di un jar nel pacchetto net / example / resources e il jar stesso è costruito come previsto. Tuttavia il servizio non verrà caricato … in particolare, ottengo una NullPointerException quando chiamo ApplicationService.getPort ();

È ansible? o solo una caccia all’oca selvaggia?

Sì, questo è sicuramente ansible come ho fatto durante la creazione di client tramite javax.xml.ws.EndpointReference, una class correlata WS-A. Ho aggiunto un riferimento al classpath al WSDL per il WS-A EndPointReference e l’implementazione Metro di JAX-WS lo ha caricato bene. Sia che si carichi WSDL dal WS-A EndPointReference o da un file o URL http, l’implementazione JAX-WS deve utilizzare lo stesso codice di analisi WSDL mentre tutto ciò che si sta facendo è la risoluzione degli URL.

L’approccio migliore per te è probabilmente quello di fare qualcosa di simile al seguente:

 URL wsdlUrl = MyClass.class.getResource( "/class/path/to/wsdl/yourWSDL.wsdl"); Service yourService= Service.create( wsdlUrl, ...); 

Dove … rappresenta il QName di un servizio WSDL all’interno del tuo WSDL. Ora la cosa importante da ricordare è che il tuo WSDL deve essere completo e valido. Ciò significa che se il tuo WSDL importa file XSD o altri WSDL, gli URL devono essere corretti. Se hai incluso il WSDL e XSD importati nello stesso JAR del file WSDL, dovresti utilizzare gli URL relativi per le importazioni e conservare tutte le tue importazioni nello stesso file JAR. Il gestore URL JAR non considera gli URL relativi come relativi rispetto al classpath, ma piuttosto rispetto all’interno del file JAR, quindi non è ansible importare nel WSDL importazioni che girano su JAR a meno che non si implementi un gestore di URL personalizzato e il proprio prefisso da fare risoluzione basata sulle classpath delle importazioni. Se il tuo WSDL importa risorse esterne, va bene, ma ti stai registrando per problemi di manutenzione se quelle risorse si spostano mai. Anche l’utilizzo di una copia statica di WSDL dal classpath è contrario allo spirito di WSDL, servizi Web e JAX-WS, ma ci sono momentjs in cui è necessario.

Infine, se incorpori un WSDL statico, ti suggerisco di rendere l’endpoint del servizio configurabile per scopi di test e implementazione. Il codice per riconfigurare l’endpoint del client del servizio Web è il seguente:

  YourClientInterface client = yourService.getPort( new QName("...", "..."), YourClientInterface.class); BindingProvider bp = (BindingProvider) client; bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://localhost:8080/yourServiceEndpoint"); 

Atleast per il recente JAX-WS non è necessario eseguire alcun catalogo di schemi o impostazione di posizione wsdl programmatica SE si inserisce WSDL nel JAR e quindi si imposta wsdlLocation wsdlLocation nel percorso di risorse relativo del WSDL nel JAR. Questo è JAX-WS che utilizza la Class.getResource di Class.getResource per caricare il WSDL.

Se usi Maven è qualcosa del tipo:

   org.jvnet.jax-ws-commons jaxws-maven-plugin 2.3    wsimport     /com/adamgent/ws/blah.wsdl ${basedir}/src/main/resources/com/adamgent/ws blah.wsdl     

Per l’esempio precedente, si src/main/resources/com/adamgent/ws quindi il WSDL usando il layout del progetto Maven qui src/main/resources/com/adamgent/ws .

Assicurati che il WSDL entri nel JAR per Maven come:

    src/main/resources   .... 

Ora il tuo codice generato da wsimport e WSDL si trovano in un JAR autonomo. Per utilizzare il servizio non è necessario impostare la posizione WSDL ed è facile come:

 BlahService myService = new BlayService_Service().getBlahServicePort(); 

Dovrebbe essere banale mapparlo sopra al wsimport di ANT.

Forse un po ‘tardi, ma ho trovato una soluzione abbastanza semplice che ha funzionato per risolvere questo problema, ma questo ha comportato una modifica del codice generato della class Service:

Se la seguente riga nella class di servizio

 baseUrl = net.example.ApplicationService.class.getResource("."); 

è cambiato in

 baseUrl = net.example.ApplicationService.class.getResource(""); 

funziona bene anche con un WSDL che è incluso in un JAR. Non sono sicuro dell’esatto comportamento presunto di getResource () in entrambi i casi, ma non ho riscontrato alcun problema con questo approccio finora, su più versioni di OS e Java.

Quello che descrivi è un bug in JAX-WS: JAX_WS-888 – Codice errato per la risoluzione dell’URL per una posizione personalizzata su wsdl .

È stato corretto per V2.2, quindi l’impostazione di wsdlLocation , come si scrive, dovrebbe funzionare ora.

Se il classpath ha “.” al suo interno, quindi Class.getResource (“.”) restituirà l’URL della directory da cui è stato eseguito il comando java. Altrimenti, restituirà un valore nullo. Regolare di conseguenza il wsdocation.

Un’altra risposta è usare il nuovo servizio (wsdllocation, servicename); per ottenere l’object di servizio.

Ecco come ho risolto il problema.

Mi sono imbattuto nello stesso problema. Il codice client JAXWS genera il MyService.class.getResource(".") Per caricare il file wsdl … ma dopo averlo testato, sembra funzionare solo se il file di class si trova in una directory sul filesytem. Se il file di class si trova in un JAR, questa chiamata restituisce null per l’URL.

Sembra un bug nel JDK poiché se si crea il tuo URL in questo modo:

 final URL url = new URL( MyService.class.getResource( MyService.class.getSimpleName() + ".class"), "myservice.wsdl"); 

quindi funziona anche se la class e wsdl sono raggruppati in un barattolo.

Immagino che la maggior parte delle persone in realtà si impacchetta in un barattolo!

Ho sostituito la posizione WSDL prima di build il jar client qui va.

  1. Copia il WSDL nella directory delle classi.
  2. Sostituire la class di servizio fare riferimento a WSDL utilizzando classpath.
  3. build gli stub dei client.
  4. jar gli stub.
               

Ecco la mia soluzione alternativa.

Disimballa il WSDL dal barattolo e lo scrivo in un file vicino al barattolo:

 File wsdl = new File("../lib/service.wsdl"); InputStream source = getClass().getResource("resources/service.wsdl").openStream(); FileOutputStream out = new FileOutputStream(wsdl); byte[] buffer = new byte[512]; int read; while((read = source.read(buffer)) >= 0) { out.write(buffer, 0, read); } 

Quindi indirizzare le classi di servizio sul file:../lib/service.wsdl .

Funziona, ma sarei grato se qualcuno potesse mostrarmi una soluzione più elegante.

Eccone uno che funziona per me (in particolare, tramite http e https ). Caso di JAX-WS di Oracle JDK 1.8.0_51 che funziona con classi create da Apache CXF 3.1.1.

Si noti che il WSDL remoto viene ottenuto solo in caso di prima chiamata. A seconda del modello di utilizzo (programma a esecuzione prolungata), ciò potrebbe essere completamente accettabile.

Le basi:

  • Scarica il WSDL dall’host remoto e memorizzalo come file: wget --output-document=wsdl_raw.xml $WSDL_URL
  • Si consiglia di xmllint --format wsdl_raw.xml > wsdl.xml per una buona formattazione
  • Genera classi client usando lo strumento della riga di comando : ./cxf/bin/wsdl2java -d output/ -client -validate wsdl.xml e importa nel tuo progetto

Verificare che le definizioni del servizio per http e https siano presenti nel file WSDL. Nel mio caso, il provider non ne aveva uno per https (ma accettava il traffico https ) e dovevo aggiungerlo manualmente. Qualcosa lungo queste linee dovrebbe essere nel WSDL:

            

CXF dovrebbe aver generato una class che implementa javax.xml.ws.Service chiamato per esempio Fooservice , con costruttori appropriati:

 public class Fooservice extends Service { public Fooservice(URL wsdlLocation) { super(wsdlLocation, SERVICE); } public Fooservice(URL wsdlLocation, QName serviceName) { super(wsdlLocation, serviceName); } public Fooservice() { super(WSDL_LOCATION, SERVICE); } ...etc... 

Da qualche parte nel codice (qui, alcuni Groovy per una lettura più semplice), si inizializza l’istanza di Service sopra, quindi si invoca una porta. Qui, a seconda del flag chiamato secure , utilizziamo https o http :

 static final String NAMESPACE = 'com.example.ws.ab' static final QName SERVICE_NAME_HTTP = new QName(NAMESPACE, 'fooservice') static final QName SERVICE_NAME_HTTPS = new QName(NAMESPACE, 'fooservice-secured') Fooservice wsService File wsdlfile = new File('/somewhere/on/disk/wsdl.xml') // If the file is missing there will be an exception at connect // time from sun.net.www.protocol.file.FileURLConnection.connect // It should be possible to denote a resource on the classpath // instead of a file-on-disk. Not sure how, maybe by adding a handler // for a 'resource:' URL scheme? URI wsdlLocationUri = java.nio.file.Paths(wsdlfile.getCanonicalPath()).toUri() if (secure) { wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTPS) } else { wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP) } SomeServicePort port = wsService.getSomeServicePort() port.doStuff() 

L’alternativa, che scarica WSDL su una connessione separata dalla connessione utilizzata per il richiamo del servizio (utilizzare tcpdump -n -nn -s0 -A -i eth0 'tcp port 80' per osservare il traffico) è fare semplicemente:

 URI wsdlLocationUri if (secure) { wsdlLocationUri = new URI('https://ws.example.com/a/b/FooBarWebService?wsdl') } else { wsdlLocationUri = new URI('http://ws.example.com/a/b/FooBarWebService?wsdl') } Fooservice wsService = new Fooservice(wsdlLocationUri.toURL(), SERVICE_NAME_HTTP) SomeServicePort port = wsService.getSomeServicePort() port.doStuff() 

Si noti che in realtà utilizza https correttamente se wsdlLocationUri specifica https , nonostante il fatto che wsService sia stato inizializzato con SERVICE_NAME_HTTP . (Non è sicuro il perché: il servizio utilizza lo schema utilizzato per recuperare la risorsa WSDL?)

E questo è tutto.

Per eseguire il debug della connessione, passare:

 -Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true -Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true 

alla JVM sulla riga di comando. Questo scriverà le informazioni dal codice di connessione http allo stdout (purtroppo NON a java.util.logging . Oracle, per favore!).

La mia soluzione era di modificare il servizio generato. Devi modificare wsdlLocation nell’intestazione dell’header e il blocco di installazione è simile al seguente:

  static { URL url = null; url = com.ups.wsdl.xoltws.ship.v1.ShipService.class.getResource("Ship.wsdl"); SHIPSERVICE_WSDL_LOCATION = url; } 

Metto il file wsdl nella directory bin accanto alla class ShipService

Sebbene tu possa farlo funzionare con alcune manipolazioni, ti raccomando di non farlo e di mantenerlo nel modo che hai adesso.

I provider di endpoint del servizio Web devono fornire un WSDL come parte del loro contratto. Il codice generato deve essere estratto dal WSDL dal server stesso.

Alla distribuzione su WebSphere è ansible modificare gli endpoint in altri endpoint dall’interfaccia utente di distribuzione. Altri server delle applicazioni potrebbero essere necessari per trovare l’XML di binding specifico del fornitore per farlo.

Succede solo durante l’inizializzazione, quindi l’impatto sull’applicazione complessiva dovrebbe essere trascurabile.