Comment éviter la nécessité de spécifier L'emplacement WSDL dans un client webservice généré CXF ou JAX-WS?
Lorsque je génère un client webservice en utilisant WSDL2JAVA à partir de CXF (qui génère quelque chose de similaire à wsimport), via maven, mes services commencent par des codes comme ceci:
@WebServiceClient(name = "StatusManagement",
wsdlLocation = "c:/some_absolute_path_to_a_wsdl_file.wsdl",
targetNamespace = "http://tempuri.org/")
public class StatusManagement extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
static {
URL url = null;
try {
url = new URL("c:/some_absolute_path_to_a_wsdl_file.wsdl");
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from c:/some_absolute_path_to_a_wsdl_file.wsdl");
// e.printStackTrace();
}
WSDL_LOCATION = url;
}
Le chemin absolu codé en dur est vraiment nul. La classe générée ne fonctionnera pas dans un autre ordinateur que le mien.
La première idée est de placer le fichier WSDL (plus Tout ce qu'il importe, d'autres WSDLs et XSDs) quelque part dans un fichier jar et classpath. Mais nous voulons éviter cela. Puisque tout ce truc était généré par CXF et JAXB basé dans les WSDLs et XSD, nous ne voyons aucun intérêt à avoir besoin de connaître le WSDL au moment de l'exécution.
L'attribut wsdlLocation est destiné à remplacer L'emplacement WSDL (au moins c'est ce que j'ai lu quelque part), et sa valeur par défaut est "". Puisque nous utilisons maven, nous avons essayé d'inclure <wsdlLocation></wsdlLocation>
dans la configuration de CXF pour essayer de forcer le générateur source à laisser le wsdllocation vide. Cependant, cela le fait simplement ignorer la balise XML car elle est vide. Nous avons fait un hack honteux vraiment laid, en utilisant <wsdlLocation>" + "</wsdlLocation>
.
Cela change aussi d'autres endroits:
@WebServiceClient(name = "StatusManagement",
wsdlLocation = "" + "",
targetNamespace = "http://tempuri.org/")
public class StatusManagement extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://tempuri.org/", "StatusManagement");
public final static QName WSHttpBindingIStatus = new QName("http://tempuri.org/", "WSHttpBinding_IStatus");
static {
URL url = null;
try {
url = new URL("" + "");
} catch (MalformedURLException e) {
System.err.println("Can not initialize the default wsdl from " + "");
// e.printStackTrace();
}
WSDL_LOCATION = url;
}
Donc, mes questions sont:
Avons-nous vraiment besoin d'un emplacement WSDL même si toutes les classes ont été générées par CXF et JAXB? Si oui, pourquoi?
Si nous n'avons pas vraiment besoin de l'emplacement WSDL, Quelle est la manière correcte et propre de faire en sorte que CXF ne le génère pas et de l'éviter complètement?
Quels sont les mauvais effets secondaires que nous pourrions obtenir avec ce hack? Nous ne pouvons toujours pas tester pour voir ce qui se passe, donc si quelqu'un pouvait dire à l'avance, ce serait sympa.
8 réponses
J'ai finalement trouvé la bonne réponse à cette question aujourd'hui.
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated-sources/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${project.basedir}/src/main/resources/wsdl/FooService.wsdl</wsdl>
<wsdlLocation>classpath:wsdl/FooService.wsdl</wsdlLocation>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Remarquez que j'ai préfixé la valeur dans wsdlLocation
avec classpath:
. Cela indique au plugin que le wsdl sera sur le classpath au lieu d'un chemin absolu. Ensuite, il va générer du code similaire à ceci:
@WebServiceClient(name = "FooService",
wsdlLocation = "classpath:wsdl/FooService.wsdl",
targetNamespace = "http://org/example/foo")
public class Foo_Service extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://org/example/foo", "Foo");
public final static QName FooSOAPOverHTTP = new QName("http://org/example/foo", "Foo_SOAPOverHTTP");
static {
URL url = Foo_Service.class.getClassLoader().getResource("wsdl/FooService.wsdl");
if (url == null) {
java.util.logging.Logger.getLogger(Foo_Service.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "classpath:wsdl/FooService.wsdl");
}
WSDL_LOCATION = url;
}
Notez que cela ne fonctionne qu'avec la version 2.4.1 ou plus récente du plugin cxf-codegen.
Nous utilisons
wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"
En d'autres termes, utilisez un chemin relatif au chemin de classe.
Je crois que le WSDL peut être nécessaire à l'exécution pour la validation des messages pendant marshal/unmarshal.
1) Dans certains cas, oui. Si le WSDL contient des éléments tels que des stratégies et qui dirigent le comportement d'exécution, le WSDL peut être requis au moment de l'exécution. Les artefacts ne sont pas générés pour des choses liées à la Politique et autres. En outre, dans certains cas RPC/littéraux obscurs, tous les espaces de noms nécessaires ne sont pas générés dans le code généré (par spécification). Ainsi, le wsdl serait nécessaire pour eux. Obscur cas.
2) je pensais que quelque chose comme fonctionnerait. Quelle version de CXF? Que sonne comme un bug. Vous pouvez essayer une chaîne vide (juste des espaces). Pas sûr si cela fonctionne ou pas. Cela dit, dans votre code, vous pouvez utiliser le constructeur qui prend L'URL WSDL et juste passer null. Le wsdl ne serait pas utilisé.
3) Juste les limitations ci-dessus.
Pour ceux qui utilisent org.jvnet.jax-ws-commons:jaxws-maven-plugin
pour générer un client à partir de WSDL au moment de la construction:
- placez le WSDL quelque part dans votre
src/main/resources
- faire Pas préfixer le
wsdlLocation
avecclasspath:
- préfixez le
wsdlLocation
avec/
Exemple:
- WSDL est stocké dans
/src/main/resources/foo/bar.wsdl
- Configurer
jaxws-maven-plugin
avec<wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>
et<wsdlLocation>/foo/bar.wsdl</wsdlLocation>
Est-il possible que vous puissiez éviter d'utiliser wsdl2java? Vous pouvez tout de suite utiliser les API frontend CXF pour appeler votre service Web SOAP. Le seul hic est que vous devez créer votre SEI et VOs sur votre client. Voici un exemple de code.
package com.aranin.weblog4j.client;
import com.aranin.weblog4j.services.BookShelfService;
import com.aranin.weblog4j.vo.BookVO;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
public class DemoClient {
public static void main(String[] args){
String serviceUrl = "http://localhost:8080/weblog4jdemo/bookshelfservice";
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(BookShelfService.class);
factory.setAddress(serviceUrl);
BookShelfService bookService = (BookShelfService) factory.create();
//insert book
BookVO bookVO = new BookVO();
bookVO.setAuthor("Issac Asimov");
bookVO.setBookName("Foundation and Earth");
String result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
bookVO = new BookVO();
bookVO.setAuthor("Issac Asimov");
bookVO.setBookName("Foundation and Empire");
result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
bookVO = new BookVO();
bookVO.setAuthor("Arthur C Clarke");
bookVO.setBookName("Rama Revealed");
result = bookService.insertBook(bookVO);
System.out.println("result : " + result);
//retrieve book
bookVO = bookService.getBook("Foundation and Earth");
System.out.println("book name : " + bookVO.getBookName());
System.out.println("book author : " + bookVO.getAuthor());
}
}
, Vous pouvez voir le tutoriel complet ici http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/
J'ai pu générer
static {
WSDL_LOCATION = null;
}
En configurant le fichier pom pour qu'il ait une valeur null pour wsdlurl:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${basedir}/target/generated/src/main/java</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>${basedir}/src/main/resources/service.wsdl</wsdl>
<extraargs>
<extraarg>-client</extraarg>
<extraarg>-wsdlLocation</extraarg>
<wsdlurl />
</extraargs>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Mise à Jour pour CXF 3.1.7
Dans mon cas, j'ai mis les fichiers WSDL dans src/main/resources
et ajouté ce chemin à mes Srouces dans Eclipse (clic droit sur Project - > Build Path -> Configure Build Path...- >Source [Onglet] - > Ajouter Un Dossier).
Voici à quoi ressemble mon fichier pom
et comme on peut le voir, il n'y a pas de wsdlLocation
option nécessaire:
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-codegen-plugin</artifactId>
<version>${cxf.version}</version>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<configuration>
<sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>
<wsdlOptions>
<wsdlOption>
<wsdl>classpath:wsdl/FOO_SERVICE.wsdl</wsdl>
</wsdlOption>
</wsdlOptions>
</configuration>
<goals>
<goal>wsdl2java</goal>
</goals>
</execution>
</executions>
</plugin>
Et voici le Service généré. Comme on peut le voir, l'URL est get from ClassLoader et non du chemin de fichier absolu
@WebServiceClient(name = "EventService",
wsdlLocation = "classpath:wsdl/FOO_SERVICE.wsdl",
targetNamespace = "http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/")
public class EventService extends Service {
public final static URL WSDL_LOCATION;
public final static QName SERVICE = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventService");
public final static QName EventPort = new QName("http://www.sas.com/xml/schema/sas-svcs/rtdm-1.1/wsdl/", "EventPort");
static {
URL url = EventService.class.getClassLoader().getResource("wsdl/FOO_SERVICE.wsdl");
if (url == null) {
java.util.logging.Logger.getLogger(EventService.class.getName())
.log(java.util.logging.Level.INFO,
"Can not initialize the default wsdl from {0}", "classpath:wsdl/FOO_SERVICE.wsdl");
}
WSDL_LOCATION = url;
}
Sérieusement, la meilleure réponse ne fonctionne pas pour moi. essayé cxf.version 2.4.1 et 3.0.10. et générer un chemin absolu avec wsdlLocation à chaque fois.
Ma solution est d'utiliser la commande wsdl2java
dans le apache-cxf-3.0.10\bin\
avec -wsdlLocation classpath:wsdl/QueryService.wsdl
.
Détail:
wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl