Come evitare la necessità di specificare la posizione WSDL in un client webservice generato da CXF o JAX-WS?


165

Quando generi un client di servizi web usando wsdl2java da CXF (che genera qualcosa di simile a wsimport), tramite Maven, i miei servizi iniziano con codici come questo:

@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;
    }

Il percorso assoluto hardcoded fa davvero schifo. La classe generata non funzionerà in nessun altro computer diverso dal mio.

La prima idea è quella di mettere il file WSDL (più tutto ciò che importa, altri WSDL e XSD) da qualche parte in un file jar e classificarlo. Ma vogliamo evitare questo. Dal momento che tutto ciò è stato generato da CXF e JAXB basati su WSDL e XSD, non vediamo il bisogno di conoscere il WSDL in fase di esecuzione.

L'attributo wsdlLocation ha lo scopo di sovrascrivere la posizione WSDL (almeno questo è quello che ho letto da qualche parte) e il suo valore predefinito è "". Dato che stiamo usando Maven, abbiamo cercato di includere <wsdlLocation></wsdlLocation>all'interno della configurazione di CXF per provare a forzare il generatore di sorgenti a lasciare vuoto wsdlLocation. Tuttavia, ciò fa semplicemente ignorare il tag XML perché è vuoto. Abbiamo fatto un brutto colpo vergognoso, usando <wsdlLocation>" + "</wsdlLocation>.

Questo cambia anche altri luoghi:

@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;
    }

Quindi, le mie domande sono:

  1. Abbiamo davvero bisogno di una posizione WSDL anche se tutte le classi sono state generate da CXF e JAXB? Se si, perché?

  2. Se non abbiamo davvero bisogno del percorso WSDL, qual è il modo corretto e pulito per fare in modo che CXF non lo generi ed evitarlo del tutto?

  3. Quali effetti collaterali negativi potremmo avere con quell'hack Non possiamo ancora testarlo per vedere cosa succede, quindi se qualcuno potesse dire in anticipo, sarebbe bello.

Risposte:


206

Oggi ho finalmente trovato la risposta giusta a questa domanda.

<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>

Si noti che ho anteposto il valore wsdlLocationcon classpath:. Questo dice al plugin che wsdl sarà sul percorso di classe invece che su un percorso assoluto. Quindi genererà un codice simile a questo:

@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;
    }

Nota che questo funziona solo con la versione 2.4.1 o successive del plugin cxf-codegen.


8
Quando si utilizza il plug-in JAX Maven anziché CXF, omettere classpath:nella <wsdlLocation...riga.
Twilite,

qualcuno sta affrontando un problema di spazio dei nomi con il codice generato dal metodo sopra?
Narendra Jaggi,

Devi elencare ogni wsdl singolarmente se ne hai diversi? È possibile evitarlo?
pitseeker,

21

Noi usiamo

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

In altre parole, utilizzare un percorso relativo al percorso di classe.

Credo che il WSDL possa essere necessario in fase di esecuzione per la convalida dei messaggi durante il marshal / unmarshal.


17

Per coloro che utilizzano org.jvnet.jax-ws-commons:jaxws-maven-pluginper generare un client da WSDL in fase di compilazione:

  • Posiziona il WSDL da qualche parte nel tuo src/main/resources
  • Evitare Non anteporre al wsdlLocationconclasspath:
  • Prefisso il wsdlLocationcon/

Esempio:

  • WSDL è archiviato in /src/main/resources/foo/bar.wsdl
  • Configurare jaxws-maven-plugincon <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>e<wsdlLocation>/foo/bar.wsdl</wsdlLocation>

perché non uso il prefisso "wsdlLocation with classpath", io lo uso e funziona
Mohammad Sadegh Rafiei

9

1) In alcuni casi, sì. Se WSDL contiene elementi come Politiche e tali che indirizzano il comportamento di runtime, potrebbe essere necessario WSDL in fase di runtime. Gli artefatti non vengono generati per cose relative alla politica e simili. Inoltre, in alcuni casi oscuri di RPC / Literal, non tutti gli spazi dei nomi necessari vengono emessi nel codice generato (per specifica). Pertanto, il wsdl sarebbe necessario per loro. Casi oscuri però.

2) Ho pensato che qualcosa del genere avrebbe funzionato. Quale versione di CXF? Sembra un bug. Puoi provare una stringa vuota lì (solo spazi). Non sono sicuro che funzioni o meno. Detto questo, nel tuo codice, puoi usare il costruttore che accetta l'URL WSDL e passa null. Il wsdl non sarebbe stato usato.

3) Solo le limitazioni sopra.


È il più recente CXF 2.3.1. Rilasciato solo 8 giorni fa. Passare null è una buona idea, prima avrei dovuto vedere questa ovvia risposta. Proverò comunque gli spazi.
Victor Stafusa,

No, gli spazi vuoti non fanno nulla. vale a dire: il tag XML è completamente ignorato.
Victor Stafusa,

5

Sono stato in grado di generare

static {
    WSDL_LOCATION = null;
}

configurando il file pom in modo che abbia un null per 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>

2
Questa soluzione non ha funzionato per me con CXF 3.1.0. ho ricevuto un errore org.apache.cxf.tools.common.toolspec.parser.BadUsageException: opzione imprevista: -wsdlLocation
Chandru

4

È possibile evitare di usare wsdl2java? Puoi subito utilizzare le API CXF FrontEnd per richiamare il tuo servizio web SOAP. L'unico problema è che è necessario creare SEI e VO sul lato client. Ecco un codice di esempio.

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());

    }
}

Puoi vedere il tutorial completo qui http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/


2
I file WSDL erano estremamente complicati, quindi abbiamo usato l'autogenerazione come modo per garantire la compatibilità. L'autogenerazione ha creato alcuni VO e SEI altrettanto complicati. Abbiamo scelto di utilizzare un set separato di oggetti di dominio completamente disaccoppiato con quelli generati automaticamente, quindi non abbiamo interferito con la generazione automatica né ne siamo stati limitati o guidati. I VO generati automaticamente sono stati utilizzati solo nel contesto delle comunicazioni di servizi e li abbiamo mantenuti il ​​più breve possibile. In altre parole, una delle nostre preoccupazioni è quella di evitare la necessità di codificare e gestire manualmente tutti i VO.
Victor Stafusa,

2
sono d'accordo con Victor, poiché mantenere VO manualmente può essere una perdita di tempo e un rischio di differenze, più o meno visibili e qualificate .. questo è esattamente lo scopo di wsdl2java, ecco perché è utile e sicuro!
Donatello,

4

Aggiornamento per CXF 3.1.7

Nel mio caso ho inserito i file WSDL src/main/resourcese ho aggiunto questo percorso a Srouces in Eclipse (tasto destro del mouse su Progetto-> Percorso build -> Configura percorso build ...-> Sorgente [scheda] -> Aggiungi cartella).

Ecco come pomappare il mio file e come si può vedere non è wsdlLocation necessaria alcuna opzione:

       <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>

Ed ecco il servizio generato. Come si può vedere, l'URL viene prelevato da ClassLoader e non dal percorso file assoluto

@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;   
}

<configuration> <sourceRoot>${basedir}/src/main/java/</sourceRoot> <wsdlRoot>${basedir}/src/main/resources/</wsdlRoot> <includes> <include>*.wsdl</include> </includes> </configuration> Includo tutti i file .wsdl nel percorso della classe, quindi come posso specificare la posizione di wsdl in modo che ogni file .java generato contenga il rispettivo percorso .wsdl? Grazie in anticipo. @Mazy
Khalid Shah,

2

Seriamente, la risposta migliore non funziona per me. provato cxf.version 2.4.1 e 3.0.10. e genera ogni volta un percorso assoluto con wsdlLocation.

La mia soluzione è usare il wsdl2javacomando in apache-cxf-3.0.10\bin\ con -wsdlLocation classpath:wsdl/QueryService.wsdl.

Dettaglio:

    wsdl2java -encoding utf-8 -p com.jeiao.boss.testQueryService -impl -wsdlLocation classpath:wsdl/testQueryService.wsdl http://127.0.0.1:9999/platf/testQueryService?wsdl

0

La soluzione @Martin Devillers funziona bene. Per completezza, fornendo i passaggi seguenti:

  1. Metti il ​​tuo wsdl nella directory delle risorse come: src/main/resource
  2. Nel file pom, aggiungi sia wsdlDirectory che wsdlLocation (da non perdere / all'inizio di wsdlLocation), come di seguito. Mentre wsdlDirectory viene utilizzato per generare codice e wsdlLocation viene utilizzato in fase di esecuzione per creare proxy dinamico.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. Quindi nel tuo codice java (con costruttore no-arg):

    MyPort myPort = new MyPortService().getMyPort();
  4. Ecco la parte di generazione del codice completo nel file pom, con API fluente nel codice generato.

    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.5</version>
    
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-fluent-api</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-tools</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>
    
    <executions>
        <execution>
            <id>wsdl-to-java-generator</id>
            <goals>
                <goal>wsimport</goal>
            </goals>
            <configuration>
                <xjcArgs>
                    <xjcArg>-Xfluent-api</xjcArg>
                </xjcArgs>
                <keep>true</keep>
                <wsdlDirectory>src/main/resources/package</wsdlDirectory>
                <wsdlLocation>/package/my.wsdl</wsdlLocation>
                <sourceDestDir>${project.build.directory}/generated-sources/annotations/jaxb</sourceDestDir>
                <packageName>full.package.here</packageName>
            </configuration>
        </execution>
    </executions>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.