web-dev-qa-db-fra.com

Comment éviter le besoin de spécifier l'emplacement WSDL dans un client de service Web généré par CXF ou JAX-WS?

Lorsque je génère un client de service Web à l'aide de wsdl2Java à partir de CXF (qui génère quelque chose de similaire à wsimport), via maven, mes services commencent par des codes tels que:

@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 dans aucun autre ordinateur que le mien.

La première idée consiste à placer le fichier WSDL (ainsi que tout ce qu'il importe, les autres WSDL et XSD) quelque part dans un fichier jar et le classpath le. Mais nous voulons éviter cela. Puisque tout cela a été généré par CXF et JAXB dans les WSDL et les 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é à redéfinir l'emplacement WSDL (du moins c'est ce que j'ai lu quelque part) et sa valeur par défaut est "". Comme nous utilisons maven, nous avons essayé d'inclure <wsdlLocation></wsdlLocation> dans la configuration de CXF pour essayer de forcer le générateur de source à laisser le blanc de wsdlLocation. Cependant, cela le fait simplement ignorer la balise XML car elle est vide. Nous avons fait un bidouillage honteux vraiment moche, 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:

  1. 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?

  2. Si nous n’avons pas vraiment besoin de l’emplacement WSDL, quelle est la méthode propre et appropriée pour que CXF ne le génère pas et l’évite complètement?

  3. Quels mauvais effets secondaires on pourrait avoir avec ce bidouillage? Nous ne pouvons toujours pas tester cela pour voir ce qui se passe, alors si quelqu'un pouvait dire à l'avance, ce serait Nice.

152
Victor Stafusa

J'ai enfin 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>

Notez que j'ai préfixé la valeur dans wsdlLocation avec classpath:. Cela indique au plug-in que le fichier WSDL sera sur le chemin de classe au lieu d'un chemin absolu. Ensuite, il générera un 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.

198
Kyle

Nous utilisons

wsdlLocation = "WEB-INF/wsdl/WSDL.wsdl"

En d'autres termes, utilisez un chemin relatif au chemin de classe.

Je pense que le WSDL peut être nécessaire au moment de l'exécution pour la validation des messages lors du marshal/unmarshal.

20
BPS

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
  • Do not préfixe la wsdlLocation avec classpath:
  • Ne préfixez la wsdlLocation avec /

Exemple:

  • WSDL est stocké dans /src/main/resources/foo/bar.wsdl
  • Configurez jaxws-maven-plugin avec <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory> et <wsdlLocation>/foo/bar.wsdl</wsdlLocation>
13
Martin Devillers

1) Dans certains cas, oui. Si le WSDL contient des éléments tels que les stratégies et que le comportement d'exécution est direct, 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. De plus, dans certains cas obscurs RPC/Literal, 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. Les cas obscurs cependant.

2) Je pensais que quelque chose fonctionnerait. Quelle version de CXF? Cela ressemble à un bug. Vous pouvez essayer une chaîne vide à l'intérieur (uniquement des espaces). Pas sûr que ça marche ou pas. Cela dit, dans votre code, vous pouvez utiliser le constructeur qui prend l’URL WSDL et ne passez que null. Le WSDL ne serait pas utilisé.

3) Juste les limitations ci-dessus.

9
Daniel Kulp

Est-il possible d'éviter d'utiliser wsdl2java? Vous pouvez immédiatement utiliser les API CXF FrontEnd pour appeler votre Webservice SOAP. Le seul problème est que vous devez créer votre SEI et vos VO 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/01/01/developing-soap-web-service-using-Apache-cxf/

4
Niraj Singh

J'ai pu générer

static {
    WSDL_LOCATION = null;
}

en configurant le fichier pom pour avoir un 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>
4
raisercostin

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 commandes dans Eclipse (clic droit sur Projet-> Chemin de construction -> Configurer le chemin de construction ...-> Source [Onglet] -> Ajouter un dossier ).

Voici à quoi ressemble mon fichier pom et, comme on peut le constater, il n’ya PAS d’option wsdlLocation 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 provient de 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;   
}
4
Mazy

Sérieusement, la meilleure réponse ne fonctionne pas pour moi. essayé cxf.version 2.4.1 et 3.0.10. et générez un chemin absolu avec wsdlLocation à chaque fois.

Ma solution consiste à 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
2
jeiao