Wie kann vermieden werden, dass der WSDL-Speicherort in einem von CXF oder JAX-WS generierten Webservice-Client angegeben werden muss?

165

Wenn ich einen Webservice-Client mit wsdl2java aus CXF (der etwas Ähnliches wie wsimport generiert) über maven generiere, beginnen meine Dienste mit folgenden Codes:

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

Der fest codierte absolute Pfad ist wirklich scheiße. Die generierte Klasse funktioniert auf keinem anderen Computer als meinem.

Die erste Idee ist, die WSDL-Datei (plus alles, was sie importiert, andere WSDLs und XSDs) irgendwo in eine JAR-Datei zu legen und sie zu klassifizieren. Aber das wollen wir vermeiden. Da all das von CXF und JAXB basierend auf den WSDLs und XSDs generiert wurde, sehen wir keinen Grund, die WSDL zur Laufzeit kennen zu müssen.

Das wsdlLocation-Attribut soll den WSDL-Speicherort überschreiben (zumindest habe ich dies irgendwo gelesen), und der Standardwert ist "". Da wir maven verwenden, haben wir versucht, <wsdlLocation></wsdlLocation>in die Konfiguration von CXF aufzunehmen, um den Quellgenerator zu zwingen, die wsdlLocation leer zu lassen. Dadurch wird das XML-Tag jedoch einfach ignoriert, da es leer ist. Wir haben einen wirklich hässlichen, beschämenden Hack gemacht <wsdlLocation>" + "</wsdlLocation>.

Dies ändert auch andere Orte:

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

Meine Fragen sind also:

  1. Benötigen wir wirklich einen WSDL-Speicherort, auch wenn alle Klassen von CXF und JAXB generiert wurden? Wenn ja, warum?

  2. Wenn wir den WSDL-Speicherort nicht wirklich benötigen, wie kann CXF ihn dann ordnungsgemäß und sauber nicht generieren und vollständig vermeiden?

  3. Welche schlimmen Nebenwirkungen könnten wir mit diesem Hack bekommen? Wir können das immer noch nicht testen, um zu sehen, was passiert. Wenn also jemand im Voraus sagen könnte, wäre es schön.

Victor Stafusa
quelle

Antworten:

206

Ich habe heute endlich die richtige Antwort auf diese Frage gefunden.

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

Beachten Sie, dass ich den Wert wsdlLocationmit vorangestellt habe classpath:. Dies teilt dem Plugin mit, dass sich die wsdl anstelle eines absoluten Pfads im Klassenpfad befindet. Dann wird ähnlicher Code generiert:

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

Beachten Sie, dass dies nur mit Version 2.4.1 oder neuer des cxf-codegen-Plugins funktioniert.

Kyle
quelle
8
Wenn Sie das JAX Maven Plugin anstelle von CXF verwenden, lassen Sie es classpath:in der <wsdlLocation...Zeile weg .
Twilite
Hat jemand ein Namespace-Problem mit Code, der mit der oben genannten Methode generiert wurde?
Narendra Jaggi
Müssen Sie jede WSDL einzeln auflisten, wenn Sie mehrere haben? Kann man das vermeiden?
Pitseeker
21

Wir gebrauchen

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

Verwenden Sie mit anderen Worten einen Pfad relativ zum Klassenpfad.

Ich glaube, dass die WSDL zur Laufzeit zur Validierung von Nachrichten während des Marschalls / Unmarschalls benötigt wird.

BPS
quelle
17

Für diejenigen, die org.jvnet.jax-ws-commons:jaxws-maven-pluginzum Erstellen eines Clients aus WSDL zur Erstellungszeit Folgendes verwenden:

  • Platzieren Sie die WSDL irgendwo in Ihrem src/main/resources
  • Stellen Sie das nicht voranwsdlLocation mitclasspath:
  • Stellen Sie das wsdlLocationmit voran/

Beispiel:

  • WSDL wird in gespeichert /src/main/resources/foo/bar.wsdl
  • Konfigurieren Sie jaxws-maven-pluginmit <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>und<wsdlLocation>/foo/bar.wsdl</wsdlLocation>
Martin Devillers
quelle
Warum nicht das Präfix "wsdlLocation with classpath" verwenden, ich benutze es und es funktioniert
Mohammad Sadegh Rafiei
9

1) In einigen Fällen ja. Wenn die WSDL beispielsweise Richtlinien enthält, die das Laufzeitverhalten steuern, ist die WSDL möglicherweise zur Laufzeit erforderlich. Artefakte werden nicht für politikbezogene Dinge und dergleichen generiert. In einigen obskuren RPC / Literal-Fällen werden nicht alle benötigten Namespaces im generierten Code ausgegeben (pro Spezifikation). Somit würde die wsdl für sie benötigt. Obskure Fälle.

2) Ich dachte so etwas würde funktionieren. Welche Version von CXF? Das klingt nach einem Fehler. Sie können dort eine leere Zeichenfolge versuchen (nur Leerzeichen). Ich bin mir nicht sicher, ob das funktioniert oder nicht. In Ihrem Code können Sie jedoch den Konstruktor verwenden, der die WSDL-URL verwendet und einfach null übergibt. Die wsdl würde nicht verwendet.

3) Nur die oben genannten Einschränkungen.

Daniel Kulp
quelle
Es ist das neueste CXF 2.3.1. Vor nur 8 Tagen veröffentlicht. Null zu übergeben ist eine gute Idee, ich sollte diese offensichtliche Antwort schon einmal sehen. Ich werde immer noch die Räume ausprobieren.
Victor Stafusa
Nein, Leerzeichen machen dasselbe wie nichts. dh: das XML-Tag wird vollständig ignoriert.
Victor Stafusa
5

Ich konnte generieren

static {
    WSDL_LOCATION = null;
}

indem Sie die pom-Datei so konfigurieren, dass sie eine Null für wsdlurl hat:

    <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>
Rosercostin
quelle
2
Diese Lösung hat bei CXF 3.1.0 bei mir nicht funktioniert. habe einen Fehler org.apache.cxf.tools.common.toolspec.parser.BadUsageException: Unerwartete Option: -wsdlLocation
Chandru
4

Ist es möglich, dass Sie die Verwendung von wsdl2java vermeiden können? Sie können sofort CXF FrontEnd-APIs verwenden, um Ihren SOAP-Webservice aufzurufen. Der einzige Haken ist, dass Sie Ihre SEI und VOs auf Ihrer Client-Seite erstellen müssen. Hier ist ein Beispielcode.

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

    }
}

Das vollständige Tutorial finden Sie hier http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/

Niraj Singh
quelle
2
Die WSDL-Dateien waren sehr kompliziert, daher haben wir die automatische Generierung verwendet, um die Kompatibilität sicherzustellen. Die automatische Generierung führte zu einigen ebenso äußerst komplizierten VOs und SEIs. Wir haben uns für die Verwendung eines separaten Satzes von Domänenobjekten entschieden, die vollständig von den automatisch generierten Objekten entkoppelt sind, sodass wir die automatische Generierung weder gestört noch eingeschränkt oder von ihr gesteuert wurden. Die automatisch generierten VOs wurden nur im Rahmen der Dienstkommunikation verwendet und wir haben sie so kurzlebig wie möglich gehalten. Mit anderen Worten, eines unserer Anliegen ist es, die Notwendigkeit zu vermeiden, alle VOs manuell zu codieren und zu verwalten.
Victor Stafusa
2
Ich stimme Victor zu, da die manuelle Wartung von VO Zeitverschwendung und das Risiko von Unterschieden sein kann, die mehr oder weniger sichtbar und qualifiziert sind. Genau das ist der Zweck von wsdl2java. Deshalb ist es nützlich und sicher!
Donatello
4

Update für CXF 3.1.7

In meinem Fall habe ich die WSDL-Dateien abgelegt src/main/resources und diesen Pfad zu meinen Srouces in Eclipse hinzugefügt (Rechtsklick auf Projekt-> Erstellungspfad -> Erstellungspfad konfigurieren ...-> Quelle [Tab] -> Ordner hinzufügen).

So sieht meine pomDatei aus und wie zu sehen ist, ist KEINE wsdlLocation Option erforderlich:

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

Und hier ist der generierte Service. Wie zu sehen ist, stammt die URL von ClassLoader und nicht vom Absolute File-Path

@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;   
}
Mazy
quelle
<configuration> <sourceRoot>${basedir}/src/main/java/</sourceRoot> <wsdlRoot>${basedir}/src/main/resources/</wsdlRoot> <includes> <include>*.wsdl</include> </includes> </configuration> Ich füge alle .wsdl-Dateien in den Klassenpfad ein. Wie kann ich dann den wsdl-Speicherort angeben, damit jede generierte .java-Datei den jeweiligen .wsdl-Pfad enthält? Danke im Voraus. @ Mazy
Khalid Shah
2

Im Ernst, die beste Antwort funktioniert bei mir nicht. versuchte cxf.version 2.4.1 und 3.0.10. und generiere jedes Mal einen absoluten Pfad mit wsdlLocation.

Meine Lösung besteht darin, den wsdl2javaBefehl in der apache-cxf-3.0.10\bin\ mit zu verwenden -wsdlLocation classpath:wsdl/QueryService.wsdl.

Detail:

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

@ Martin Devillers Lösung funktioniert gut. Geben Sie der Vollständigkeit halber die folgenden Schritte an:

  1. Legen Sie Ihre WSDL in ein Ressourcenverzeichnis wie: src/main/resource
  2. Fügen Sie in der POM-Datei sowohl wsdlDirectory als auch wsdlLocation hinzu (nicht verpassen / am Anfang von wsdlLocation), wie unten. Während wsdlDirectory zum Generieren von Code verwendet wird und wsdlLocation zur Laufzeit zum Erstellen eines dynamischen Proxys verwendet wird.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. Dann in Ihrem Java-Code (mit No-Arg-Konstruktor):

    MyPort myPort = new MyPortService().getMyPort();
  4. Hier ist der vollständige Teil der Codegenerierung in der POM-Datei mit einer fließenden API im generierten Code.

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

Shafiul
quelle