JAX-WS Chargement WSDL à partir de jar
j'écris un gros client qui utilise un service SOAP pour certaines fonctionnalités (rapports de bogues, etc.)
J'ai JAX-WS qui fonctionne bien, mais par défaut (dans netbeans au moins) il récupère la WSDL du serveur distant chaque fois que le service est initialisé. Je m'attends à ce que cela aide à fournir un certain soutien de version, etc. mais ce n'est pas ce que je veux.
j'ai ajouté l'arg wsdllocation
à wsimport pour pointer les classes générées vers une ressource locale. L'extrait suivant est le chargement D'URL pour la ressource WSDL de ApplicationService.Java.
baseUrl = net.example.ApplicationService.class.getResource(".");
url = new URL(baseUrl, "service.wsdl");
je suis assez sûr que ça ne devrait pas poser de problème de pointer vers une ressource stockée dans un bocal dans le paquet net/example/resources, et le bocal lui-même est construit comme prévu. Cependant le service ne se chargera pas... spécifiquement, je reçois une NullPointerException quand j'appelle ApplicationService.getPort ();
est-ce possible? ou juste une oie sauvage chase?
12 réponses
Oui c'est très certainement possible car je l'ai fait lors de la création de clients par javax.xml.ws.EndpointReference, a WS-a related class. J'ai ajouté une référence classpath à la WSDL à la WS-a EndPointReference et l'implémentation Metro de JAX-WS l'a chargée très bien. Que vous chargiez la WSDL à partir de L'URL WS-a EndPointReference ou à partir d'un fichier ou d'une URL http, votre implémentation JAX-WS devrait utiliser le même code de parsing WSDL que tout ce que vous faites est de résoudre des URLs.
la meilleure approche pour vous est probablement de faire quelque chose comme ce qui suit:
URL wsdlUrl = MyClass.class.getResource(
"/class/path/to/wsdl/yourWSDL.wsdl");
Service yourService= Service.create(
wsdlUrl,
...);
où ... représente le nom Q D'un service WSDL à l'intérieur de votre WSDL. Il est important de se rappeler que votre WSDL doit être complète et valide. Cela signifie que si votre WSDL importe des fichiers XSD ou d'autres WSDL, les URLs doivent être correctes. Si vous avez inclus votre WSDL et XSD importés dans le même bocal que le fichier WSDL, vous devez utiliser les URLs relatives pour les importations et garder toutes vos importations dans le même fichier JAR. Le gestionnaire D'URL JAR ne traite pas les URLs relatives en tant que relatives par rapport à classpath mais plutôt en tant que relatives dans le fichier JAR de sorte que vous ne pouvez pas avoir d'importations dans votre WSDL qui tournent à travers des JARs à moins que vous implémentiez un gestionnaire D'URL personnalisé et votre propre préfixe pour faire classpath basée résolution des importations. Si votre WSDL importe des ressources externes, C'est OK, mais vous vous inscrivez vous-même pour des problèmes de maintenance si ces ressources jamais déplacer. Même l'utilisation d'une copie statique de la WSDL de votre chemin de classe est contraire à l'esprit de la WSDL, des services Web, et de JAX-WS, mais il y a des moments où cela est nécessaire.
enfin, si vous intégrez une WSDL statique, je vous suggère de configurer au moins l'endpoint de service à des fins de test et de déploiement. Le code pour reconfigurer le point final de votre client de service Web est le suivant:
YourClientInterface client = yourService.getPort(
new QName("...", "..."),
YourClientInterface.class);
BindingProvider bp = (BindingProvider) client;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://localhost:8080/yourServiceEndpoint");
Atleast pour les récents JAX-WS vous n'avez pas besoin de faire des catalogues schema ou programmatique WSDL paramètre d'emplacement si "15198092020" vous mettez la WSDL dans le bocal et puis mettre wsimport wsdlLocation
au chemin de ressource relatif de la WSDL dans le bocal. C'est-à-dire que JAX-WS utilise le builtin de Java Class.getResource
pour charger la WSDL.
si vous utilisez Maven quelque chose comme:
<plugin>
<groupId>org.jvnet.jax-ws-commons</groupId>
<artifactId>jaxws-maven-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<goals>
<goal>wsimport</goal>
</goals>
<!-- Following configuration will invoke wsimport once for each wsdl. -->
<configuration>
<!--- VERY IMPORTANT THAT THE PATH START WITH '/' -->
<wsdlLocation>/com/adamgent/ws/blah.wsdl</wsdlLocation>
<wsdlDirectory>${basedir}/src/main/resources/com/adamgent/ws</wsdlDirectory>
<wsdlFiles><wsdlFile>blah.wsdl</wsdlFile></wsdlFiles>
</configuration>
</execution>
</executions>
</plugin>
pour l'exemple ci-dessus vous mettriez donc la WSDL en utilisant la mise en page du projet Maven ici src/main/resources/com/adamgent/ws
.
assurez-vous que le WSDL se trouve dans le bocal pour Maven comme:
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources> ....
maintenant votre code généré par wsimport et WSDL sont dans un bocal autonome. Pour utiliser le service, vous n'avez pas à régler l'emplacement WSDL et est aussi simple que:
BlahService myService = new BlayService_Service().getBlahServicePort();
il devrait être trivial de mapper ceci à ANT's wsimport.
peut-être un peu tard, mais j'ai trouvé une solution assez simple qui a fonctionné pour résoudre ce problème, mais cela a impliqué un changement dans le code généré de la classe de Service:
si la ligne suivante de la classe de Service
baseUrl = net.example.ApplicationService.class.getResource(".");
est remplacé par
baseUrl = net.example.ApplicationService.class.getResource("");
il fonctionne très bien même avec un WSDL qui est emballé dans un pot. Pas sûr de savoir exactement censé comportement de getResource() dans ce cas, mais j' Je n'ai eu aucun problème avec cette approche jusqu'à présent, sur plusieurs versions D'OS et de Java.
ce que vous décrivez est un bug dans JAX-WS: JAX_WS-888 - code erroné pour résoudre l'URL d'un wsdllocation personnalisé .
il a été fixé pour V2.2, donc juste le réglage wsdlLocation
, comme vous écrivez, devrait fonctionner maintenant.
une autre réponse est d'utiliser le
new Service(wsdllocation, servicename );
pour obtenir l'objet de Service.
voilà comment j'ai résolu le problème.
je suis tombé sur la même question. Le code client jaxws generate utilise le truc MyService.class.getResource(".")
pour charger le fichier wsdl... mais après des tests cela ne semble fonctionner si le fichier de classe est dans un répertoire sur le filesytem. Si le fichier de classe est dans un pot, cet appel renvoie null pour L'URL.
cela ressemble à un bug dans le JDK car si vous construisez votre URL comme ceci:
final URL url = new URL( MyService.class.getResource( MyService.class.getSimpleName() + ".class"), "myservice.wsdl");
alors cela fonctionne aussi si la classe et wsdl sont empaquetés dans un pot.
je suppose que la plupart des gens vont en fait mettre un paquet dans un bocal!
j'ai remplacé L'emplacement WSDL avant de construire le bocal du client.
- copier les WSDL dans les classes dir.
- remplacer la classe de Service se référer à WSDL en utilisant classpath.
- construisez les talons du client.
- pot les talons.
<copy todir="@{dest-dir}/@{dir-package}" verbose="@{verbose}">
<fileset dir="@{dir-wsdl}" includes="*.wsdl,*.xsd" />
</copy>
<echo message="Replacing Service to point to correct WSDL path..." />
<replaceregexp match="new URL(.*)" replace='Class.class.getResource("@{name-wsdl}");' flags="gm">
<fileset dir="@{source-dest-dir}">
<include name="@{dir-package}/*Service.java" />
</fileset>
</replaceregexp>
<replaceregexp match="catch (.*)" replace='catch (Exception ex) {' flags="gm">
<fileset dir="@{source-dest-dir}">
<include name="@{dir-package}/*Service.java" />
</fileset>
</replaceregexp>
voici ma solution de contournement.
je déballe la WSDL du bocal et je l'écris dans un fichier près du bocal:
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);
}
, puis file:../lib/service.wsdl
.
ça marche, mais j'aimerais que quelqu'un me montre une solution plus élégante.
En voici un qui fonctionne pour moi (en particulier, via http et https ). Cas de JAX-WS D'Oracle JDK 1.8.0_51 travaillant avec des classes créées par Apache CXF 3.1.1.
noter que la WSDL distante n'est obtenue que lors de la première invocation dans tous les cas. Selon le modèle d'Utilisation (programme à long terme) cela peut être tout à fait acceptable.
Les bases:
- Téléchargez la WSDL depuis l'hôte distant et stockez-la sous forme de fichier:
wget --output-document=wsdl_raw.xml $WSDL_URL
- Vous voudrez peut-être
xmllint --format wsdl_raw.xml > wsdl.xml
pour la belle mise en forme - générer des classes de clients en utilisant l'outil en ligne de commande :
./cxf/bin/wsdl2java -d output/ -client -validate wsdl.xml
et importer dans votre projet
vérifier que les définitions de service pour http et https existe dans le fichier WSDL. Dans mon cas, le fournisseur n'en avait pas pour https (mais n'accepter https de la circulation), et j'ai dû l'ajouter manuellement. Quelque chose dans ce sens devrait être dans le WSDL:
<wsdl:service name="fooservice">
<wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort">
<soap:address location="http://ws.example.com/a/b/FooBarWebService"/>
</wsdl:port>
</wsdl:service>
<wsdl:service name="fooservice-secured">
<wsdl:port binding="tns:fooserviceSoapBinding" name="FooBarWebServicePort">
<soap:address location="https://ws.example.com/a/b/FooBarWebService"/>
</wsdl:port>
</wsdl:service>
CXF devrait avoir généré une classe qui implémente javax.xml.ws.Service
appelé par exemple Fooservice
, avec des constructeurs:
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...
quelque part dans votre code (ici, certains Groovy pour faciliter la lecture), vous initialisez l'instance Service
ci-dessus, puis invoquez un port. Ici, en fonction du drapeau appelé secure
, nous utilisons https ou http :
static final String NAMESPACE = 'com.example.ws.a.b'
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'alternative, qui télécharge la WSDL sur une connexion qui est séparée de la connexion utilisée pour l'invocation de service (utiliser tcpdump -n -nn -s0 -A -i eth0 'tcp port 80'
pour observer le trafic) est de juste faire:
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()
Notez que ceci utilise en fait correctement https si le wsdlLocationUri
spécifie https , bien que le wsService
ait été initialisé avec SERVICE_NAME_HTTP
. (Pas sûr pourquoi - le service utilise-t-il le schéma utilisé pour récupérer la ressource WSDL?)
Et c'est tout.
pour déboguer la connexion, passer:
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
-Dcom.sun.xml.internal.ws.transport.http.HttpAdapter.dump=true
à la JVM sur ligne de commande. Ceci écrira les informations du code de connexion http à stdout (malheureusement pas à java.util.logging
. Oracle, s'il te plaît!).
ma solution était de modifier le Service généré. Vous devez changer wsdlLocation dans l'annotation de l'en-tête et le bloc instantion ressemble à ceci:
static {
URL url = null;
url = com.ups.wsdl.xoltws.ship.v1.ShipService.class.getResource("Ship.wsdl");
SHIPSERVICE_WSDL_LOCATION = url;
}
je place le fichier wsdl dans le répertoire bin à côté de la classe ShipService
bien que vous puissiez le faire fonctionner avec quelques manipulations, je recommande pas le faire et le garder comme vous l'avez en ce moment.
les fournisseurs de terminaux de service Web doivent fournir une DSSF dans le cadre de leur contrat. Le code que vous générez devrait être tiré de la WSDL du serveur lui-même.
lors du déploiement sur WebSphere, vous pouvez changer les paramètres à d'autres paramètres de l'interface utilisateur de déploiement. Autre serveurs d'application vous devrez peut-être trouver le XML de liaison propre au fournisseur pour le faire.
cela ne se produit que lors de l'initialisation de sorte que l'impact sur votre application globale devrait être négligeable.