Клієнт JAX-WS: який правильний шлях для доступу до локального WSDL?


86

Проблема полягає в тому, що мені потрібно створити клієнт веб-служби з файлу, який мені надали. Я зберігав цей файл у локальній файловій системі, і хоча я зберігаю файл WSDL у правильній папці файлової системи, все в порядку. Коли я розгортаю його на сервері або видаляю WSDL з папки файлової системи, проксі не може знайти WSDL і виникає помилка. Я здійснив пошук в Інтернеті, і поки що знайшов такі повідомлення, але мені не вдалося змусити це працювати:
JAX-WS Завантаження WSDL з jar
http://www.java.net/forum/topic/glassfish/metro -and-jaxb / client-jar-cant-find-local-wsdl-0
http://blog.vinodsingh.com/2008/12/locally-packaged-wsdl.html

Я використовую NetBeans 6.1 (це застаріла програма, яку я повинен оновити за допомогою цього нового клієнта веб-служби). Нижче наведено клас проксі JAX-WS:

    @WebServiceClient(name = "SOAService", targetNamespace = "http://soaservice.eci.ibm.com/", wsdlLocation = "file:/C:/local/path/to/wsdl/SOAService.wsdl")
public class SOAService
    extends Service
{

    private final static URL SOASERVICE_WSDL_LOCATION;
    private final static Logger logger = Logger.getLogger(com.ibm.eci.soaservice.SOAService.class.getName());

    static {
        URL url = null;
        try {
            URL baseUrl;
            baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
            url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
        } catch (MalformedURLException e) {
            logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
            logger.warning(e.getMessage());
        }
        SOASERVICE_WSDL_LOCATION = url;
    }

    public SOAService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public SOAService() {
        super(SOASERVICE_WSDL_LOCATION, new QName("http://soaservice.eci.ibm.com/", "SOAService"));
    }

    /**
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP() {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class);
    }

    /**
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP(WebServiceFeature... features) {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class, features);
    }

}


Це мій код для використання проксі-сервера:

   WebServiceClient annotation = SOAService.class.getAnnotation(WebServiceClient.class);
   // trying to replicate proxy settings
   URL baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("");//note : proxy uses "."
   URL url = new URL(baseUrl, "/WEB-INF/wsdl/client/SOAService.wsdl");
   //URL wsdlUrl = this.getClass().getResource("/META-INF/wsdl/SOAService.wsdl"); 
   SOAService serviceObj = new SOAService(url, new QName(annotation.targetNamespace(), annotation.name()));
   proxy = serviceObj.getSOAServiceSOAP();
   /* baseUrl;

   //classes\com\ibm\eci\soaservice
   //URL url = new URL(baseUrl, "../../../../wsdl/SOAService.wsdl");

   proxy = new SOAService().getSOAServiceSOAP();*/
   //updating service endpoint 
   Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
   ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
   ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WebServiceUrl);

NetBeans розмістив копію WSDL у web-inf / wsdl / client / SOAService , тому я не хочу також додавати її до META-INF . Класи служб знаходяться в WEB-INF / classes / com / ibm / eci / soaservice /, а змінна baseurl містить повний шлях до файлової системи до неї (c: \ path \ to \ the \ project ... \ soaservice). Наведений вище код викликає помилку:

javax.xml.ws.WebServiceException: Не вдалося отримати доступ до WSDL за адресою: file: /WEB-INF/wsdl/client/SOAService.wsdl. Помилка: \ WEB-INF \ wsdl \ client \ SOAService.wsdl (не вдається знайти шлях)

Отже, насамперед, чи слід оновлювати wsdllocation класу проксі? Тоді як я можу сказати класу SOAService у WEB-INF / classes / com / ibm / eci / soaservice шукати WSDL у \ WEB-INF \ wsdl \ client \ SOAService.wsdl?

ВИДАЛЕНО : Я знайшов це інше посилання - http://jianmingli.com/wp/?cat=41 , яке говорить про те, щоб розмістити WSDL у шляху до класу. Мені соромно запитувати: як я можу помістити його в шлях до класу веб-програми?


Подібне запитання: JAX-WS Завантаження WSDL з jar
sleske

Відповіді:


117

Найкращий варіант - використовувати jax-ws-catalog.xml

Коли ви компілюєте локальний файл WSDL, перевизначте розташування WSDL і встановіть для нього щось на зразок

http: //localhost/wsdl/SOAService.wsdl

Не хвилюйтеся, це лише URI, а не URL-адреса, тобто вам не потрібно мати WSDL, доступний за цією адресою.
Ви можете зробити це, передавши параметр wsdllocation до wsdl компілятору Java.

Це змінить ваш код проксі з

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

до

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "http://localhost/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'http://localhost/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

Файл повідомлення: // змінено на http: // у конструкторі URL.

Тепер поставляється у jax-ws-catalog.xml. Без jax-ws-catalog.xml jax-ws справді спробує завантажити WSDL з місця

http: //localhost/wsdl/SOAService.wsdl
і не вдасться, оскільки такий WSDL не буде доступний.

Але за допомогою jax-ws-catalog.xml ви можете перенаправляти jax-ws на локально упакований WSDL, коли він намагається отримати доступ до WSDL @

http: //localhost/wsdl/SOAService.wsdl
.

Ось jax-ws-catalog.xml

<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
        <system systemId="http://localhost/wsdl/SOAService.wsdl"
                uri="wsdl/SOAService.wsdl"/>
    </catalog>

Ви робите, кажучи jax-ws, що коли-небудь йому потрібно завантажити WSDL

http: //localhost/wsdl/SOAService.wsdl
, він повинен завантажити його з локального шляху wsdl / SOAService.wsdl.

Тепер куди слід покласти wsdl / SOAService.wsdl та jax-ws-catalog.xml? Це питання на мільйон доларів, чи не так?
Він повинен знаходитись у каталозі META-INF вашої програми.

так щось подібне

ABCD.jar  
| __ МЕТА-ІНФ    
    | __ jax-ws-catalog.xml  
    | __ wsdl  
        | __ SOAService.wsdl  

Таким чином, вам навіть не потрібно перевизначати URL-адресу у вашому клієнті, яка отримує доступ до проксі-сервера. WSDL береться з вашого JAR, і ви уникаєте необхідності мати жорстко закодовані шляхи до файлової системи у коді.

Більше інформації про jax-ws-catalog.xml http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

Сподіваюся, це допомагає


Гаразд, я не зміг вирішити проблему таким чином у веб-застосунку: я намагався ввести wsdl всередину web-inf без успіху, можливо, через відсутність знань. У будь-якому випадку це працює з jar, тому я зроблю бібліотеку обгортки, як це мало бути зроблено з самого початку. Дякуємо за вашу підтримку

Я зміг успішно використати цю відповідь, і я вважаю, що це найкраще рішення, ніж усі інші варіанти, які зафіксовані в інших статтях та навчальних посібниках. Тож для мене це найкраща практика. Мені просто цікаво, чому це рішення не задокументоване в інших офіційних статтях та навчальних посібниках, які висвітлюють тему JAX-WS.
Рахул Хімасія

19

Ще одним підходом, який ми успішно застосували, є створення проксі-коду клієнта WS за допомогою wsimport (від Ant, як завдання Ant) і вказати атрибут wsdlLocation.

<wsimport debug="true" keep="true" verbose="false" target="2.1" sourcedestdir="${generated.client}" wsdl="${src}${wsdl.file}" wsdlLocation="${wsdl.file}">
</wsimport>

Оскільки ми запускаємо це для проекту з декількома WSDL, сценарій динамічно визначає значення $ (wsdl.file}), яке має значення /META-INF/wsdl/YourWebServiceName.wsdl щодо розташування JavaSource (або / src, залежно від того, як налаштовано ваш проект). Під час збирання файли WSDL та XSD копіюються до цього місця та упаковуються у файл JAR (подібне до рішення, описаного Бхасакаром вище)

MyApp.jar
|__META-INF
   |__wsdl
      |__YourWebServiceName.wsdl
      |__YourWebServiceName_schema1.xsd
      |__YourWebServiceName_schmea2.xsd

Примітка: переконайтесь, що файли WSDL використовують відносні посилання на будь-які імпортовані XSD, а не на URL-адреси http:

  <types>
    <xsd:schema>
      <xsd:import namespace="http://valueobject.common.services.xyz.com/" schemaLocation="YourWebService_schema1.xsd"/>
    </xsd:schema>
    <xsd:schema>
      <xsd:import namespace="http://exceptions.util.xyz.com/" schemaLocation="YourWebService_schema2.xsd"/>
    </xsd:schema>
  </types>

У сформованому коді ми знаходимо таке:

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2-b05-
 * Generated source version: 2.1
 * 
 */
@WebServiceClient(name = "YourService", targetNamespace = "http://test.webservice.services.xyz.com/", wsdlLocation = "/META-INF/wsdl/YourService.wsdl")
public class YourService_Service
    extends Service
{

    private final static URL YOURWEBSERVICE_WSDL_LOCATION;
    private final static WebServiceException YOURWEBSERVICE_EXCEPTION;
    private final static QName YOURWEBSERVICE_QNAME = new QName("http://test.webservice.services.xyz.com/", "YourService");

    static {
        YOURWEBSERVICE_WSDL_LOCATION = com.xyz.services.webservice.test.YourService_Service.class.getResource("/META-INF/wsdl/YourService.wsdl");
        WebServiceException e = null;
        if (YOURWEBSERVICE_WSDL_LOCATION == null) {
            e = new WebServiceException("Cannot find '/META-INF/wsdl/YourService.wsdl' wsdl. Place the resource correctly in the classpath.");
        }
        YOURWEBSERVICE_EXCEPTION = e;
    }

    public YourService_Service() {
        super(__getWsdlLocation(), YOURWEBSERVICE_QNAME);
    }

    public YourService_Service(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    /**
     * 
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort() {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class);
    }

    /**
     * 
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort(WebServiceFeature... features) {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class, features);
    }

    private static URL __getWsdlLocation() {
        if (YOURWEBSERVICE_EXCEPTION!= null) {
            throw YOURWEBSERVICE_EXCEPTION;
        }
        return YOURWEBSERVICE_WSDL_LOCATION;
    }

}

Можливо, це теж може допомогти. Це просто інший підхід, який не використовує підхід "каталог".


Мені подобається такий підхід ... але чому каталог META-INF?
IcedDante

1
Зверніть увагу, що для цього потрібно використовувати JAX-WS RI 2.2, а не 2.1, який за замовчуванням поставляється з JDK 6
ᄂ ᄀ

4

Дякую за тонну за відповідь Бхаскара Карамбелкара, яка детально пояснює та виправляє мою проблему. Але також я хотів би переформулювати відповідь у три простих кроки для того, хто поспішає виправити

  1. Зробіть посилання на локальне розташування wsdl як wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. Створіть папку META-INF прямо під src. Покладіть файли wsdl у папку під META-INF, скажімо META-INF / wsdl
  3. Створіть xml-файл jax-ws-catalog.xml у розділі META-INF, як показано нижче

    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="http://localhost/wsdl/yourwsdlname.wsdl" uri="wsdl/yourwsdlname.wsdl" /> </catalog>

Тепер упакуйте банку. Немає більше посилань на локальний каталог, він все упакований і посилається всередині


4

Для тих, хто все ще шукає рішення тут, найпростішим рішенням буде використання <wsdlLocation>, не змінюючи жодного коду. Робочі кроки наведені нижче:

  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. Для повноти, я надаю тут повну частину генерації коду, з вільним 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>
    


2

Для тих, хто використовує Spring, ви можете просто посилатися на будь-який ресурс classpath, використовуючи протокол classpath. Тож у випадку wsdlLocation це стає:

<wsdlLocation>classpath:META-INF/webservice.wsdl</wsdlLocation>

Зверніть увагу, що це не є стандартною поведінкою Java. Див. Також: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html


0

Мав точно таку ж проблему, яка описана тут. Незалежно від того, що я зробив, слідуючи наведеним вище прикладам, щоб змінити розташування мого файлу WSDL (у нашому випадку з веб-сервера), він все ще посилався на вихідне розташування, вбудоване в дерево джерела процесу сервера.

Після БАГАТО годин , намагаючись налагодження це, я помітив , що виключення завжди бути викинутий з тієї ж самої лінії (в моєму випадку 41). Нарешті сьогодні вранці я вирішив просто надіслати свій вихідний клієнтський код нашому торговому партнеру, щоб вони могли принаймні зрозуміти, як виглядає код, але, можливо, створити свій власний. На мій шок і жах, я знайшов купу файлів класів, змішаних з моїми .java-файлами, у моєму дереві джерела клієнта. Як дивно !! Я підозрюю, що це був побічний продукт інструмента побудови клієнта JAX-WS.

Після того, як я заблокував ці безглузді файли .class і провів повне очищення та відновлення коду клієнта, все працює ідеально !! Редонкульозний !!

YMMV, Андрій

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