Як уникнути необхідності вказувати розташування WSDL у клієнтському веб-сервісі, створеному CXF або JAX-WS?


165

Коли я генерую клієнта веб-сервісу за допомогою wsdl2java від CXF (який генерує щось подібне до wsimport), через maven, мої послуги починаються з таких кодів:

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

Твердо закодований абсолютний шлях справді смокче. Створений клас не працюватиме на будь-якому іншому комп'ютері, крім мого.

Перша ідея - помістити файл WSDL (плюс усе, що імпортується, інші WSDL та XSD) кудись у jar-файл та класифікувати його. Але ми хочемо цього уникнути. Оскільки все це було створено CXF та JAXB, що базуються на WSDL та XSD, ми не бачимо сенсу знати WSDL під час виконання.

Атрибут wsdlLocation призначений для заміщення місця розташування WSDL (принаймні, це я десь читав), а значення за замовчуванням - "". Оскільки ми використовуємо maven, ми намагалися включити <wsdlLocation></wsdlLocation>всередину конфігурацію CXF, щоб спробувати змусити генератор джерела залишити wsdlLocation порожнім. Однак це просто змушує ігнорувати тег XML, оскільки він порожній. Ми зробили по-справжньому ганебний ганебний злом, використовуючи <wsdlLocation>" + "</wsdlLocation>.

Це також змінює інші місця:

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

Отже, мої запитання:

  1. Чи справді нам потрібне місце розташування WSDL, навіть якщо всі класи були створені CXF та JAXB? Якщо так, то чому?

  2. Якщо нам не потрібне місце розташування WSDL, то який правильний та чистий спосіб змусити CXF не генерувати його та уникати його повністю?

  3. Які погані побічні ефекти ми могли б отримати із цим злом? Ми все ще не можемо перевірити це, щоб побачити, що трапиться, тому якби хтось міг сказати заздалегідь, було б добре.

Відповіді:


206

Я нарешті з’ясував правильну відповідь на це питання сьогодні.

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

Зверніть увагу , що я приставка значення в wsdlLocationс classpath:. Це говорить плагіну про те, що wsdl буде знаходитись на classpath замість абсолютного шляху. Тоді він генерує код, подібний до цього:

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

Зауважте, що це працює лише з версією 2.4.1 або новішою з плагіна cxf-codegen.


8
Якщо ви використовуєте плагін JAX Maven замість CXF, опустіть classpath:у <wsdlLocation...рядку.
Twilite

хтось стикається з проблемою простору імен з кодом, згенерованим вищевказаним методом?
Нарендра Яггі

Чи потрібно перераховувати кожен wsdl окремо, якщо у вас їх є кілька? Чи можна цього уникнути?
пітсейкер

21

Ми використовуємо

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

Іншими словами, використовуйте шлях щодо класного шляху.

Я вважаю, що WSDL може знадобитися під час виконання для перевірки повідомлень під час маршалу / маршалу.


17

Для тих, хто використовує org.jvnet.jax-ws-commons:jaxws-maven-pluginдля створення клієнта з WSDL під час збирання:

  • Розмістіть WSDL десь у своєму src/main/resources
  • Ви НЕ префікс wsdlLocationзclasspath:
  • Зробити префікс wsdlLocationс/

Приклад:

  • WSDL зберігається в /src/main/resources/foo/bar.wsdl
  • Налаштуйте за jaxws-maven-pluginдопомогою <wsdlDirectory>${basedir}/src/main/resources/foo</wsdlDirectory>та<wsdlLocation>/foo/bar.wsdl</wsdlLocation>

чому не використовуйте префікс "wsdlLocation with classpath", я використовую його, і він працює
Mohammad Sadegh Rafiei

9

1) У деяких випадках так. Якщо WSDL містить такі речі, як Політика та такі, що спрямовують поведінку виконання, тоді WSDL може знадобитися під час виконання. Артефакти не створюються для речей, що стосуються політики, тощо. Також у деяких незрозумілих випадках RPC / Literal не всі необхідні простори імен виводяться в згенерований код (за специфікацією). Таким чином, wsdl знадобився б їм. Незрозумілі випадки, хоча.

2) Я думав, що щось подібне спрацює. Яка версія CXF? Це звучить як помилка. Ви можете спробувати порожню рядок там (просто пробіли). Не впевнений, працює це чи ні. Однак, у своєму коді ви можете використовувати конструктор, який приймає URL-адресу WSDL і просто передає null. Wsdl не буде використовуватися.

3) Просто обмеження вище.


Це найновіший CXF 2.3.1. Вийшов лише 8 днів тому. Перехід нуля - це гарна ідея, я мав би бачити цю очевидну відповідь раніше. Я все одно спробую пробіли.
Віктор Стафуса

Ні, порожні пробіли роблять те саме, що нічого. тобто: тег XML повністю ігнорується.
Віктор Стафуса

5

Я зміг генерувати

static {
    WSDL_LOCATION = null;
}

конфігуруючи файл pom для встановлення нуля для 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
Це рішення не працювало для мене із CXF 3.1.0. отримала помилку org.apache.cxf.tools.common.toolspec.parser.BadUsageException: Неочікуваний варіант: -wsdlLocation
Чандру

4

Можливо, ви можете уникнути використання wsdl2java? Ви можете одразу використовувати API CXF FrontEnd для виклику вашої веб-служби SOAP. Єдина увага полягає в тому, що вам потрібно створити свої SEI та VO на своєму клієнтському кінці. Ось зразок коду.

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

    }
}

Повний підручник ви можете побачити тут http://weblog4j.com/2012/05/01/developing-soap-web-service-using-apache-cxf/


2
Файли WSDL були дуже складними, тому ми використовували автогенерацію як спосіб забезпечити сумісність. Автогенерація створила кілька не менш складних VO та SEI. Ми вирішили використовувати окремий набір об’єктів домену, повністю пов'язаних з автогенерованими, тому ми не втручалися в автогенерацію, не обмежувались і не керували нею. Автогенеровані VO використовувались лише в контексті комунікаційних служб, і ми зберігали їх якомога коротше. Іншими словами, одне з наших проблем - уникнути необхідності вручну кодувати та керувати всіма VO.
Віктор Стафуса

2
Я погоджуюсь з Віктором, оскільки підтримка VO вручну може бути марною тратою часу, і ризик різниць, більш-менш видимих ​​і кваліфікованих .. саме це і є метою wsdl2java, ось чому це корисно і безпечно!
Донателло

4

Оновлення для CXF 3.1.7

У моєму випадку я помістив файли WSDL src/main/resourcesі додав цей шлях до моїх Srouces in Eclipse (Клацніть правою кнопкою миші на Project-> Build Path -> Configure Build Path ...-> Source [Tab] -> Add Folder).

Ось як pomвиглядає мій файл, і як видно, немає wsdlLocation потрібного варіанта:

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

І ось створений Сервіс. Як видно, URL отримується від ClassLoader, а не з 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;   
}

<configuration> <sourceRoot>${basedir}/src/main/java/</sourceRoot> <wsdlRoot>${basedir}/src/main/resources/</wsdlRoot> <includes> <include>*.wsdl</include> </includes> </configuration> Я включаю всі .wsdl файли в шлях до класу, то як я можу вказати розташування wsdl, щоб кожен згенерований файл .java контактував із відповідним .wsdl шляхом? Заздалегідь спасибі. @Mazy
Халід Шах

2

Серйозно, верхня відповідь не працює для мене. спробував cxf.version 2.4.1 та 3.0.10. і генерувати абсолютний шлях за допомогою wsdlLocation кожен раз.

Моє рішення полягає у використанні wsdl2javaкоманди в apache-cxf-3.0.10\bin\ с -wsdlLocation classpath:wsdl/QueryService.wsdl.

Детальніше:

    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

Рішення @Martin Devillers чудово працює. Для повноти надайте наведені нижче дії.

  1. Помістіть свій wsdl в каталог ресурсів: src/main/resource
  2. У файл Pom додайте і wsdlDirectory, і wsdlLocation (не пропустіть / на початку wsdlLocation), як нижче. У той час як wsdlDirectory використовується для генерації коду, а wsdlLocation використовується під час виконання для створення динамічного проксі.

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
  3. Потім у вашому коді Java (з конструктором no-arg):

    MyPort myPort = new MyPortService().getMyPort();
  4. Ось повна частина генерації коду у файлі pom, з вільним api у створеному коді.

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.