User eGarmin
eGarmin
41 уровень

Веб-сервисы. Шаг 2. Как упростить написание клиента?

Статья из группы Архив info.javarush.ru
В этой краткой заметке я бы хотел вернуться к коду клиента веб-сервиса, который мы написали на предыдущем шаге. При этом я буду предполагать, что у вас открыта IDEA, а в ней проект с Шага 1. В этом проекте должен быть запущен наш веб-сервис:

package ru.javarush.client;

// нужно, чтобы получить wsdl описание и через него
// дотянуться до самого веб-сервиса
import java.net.URL;
// такой эксепшн возникнет при работе с объектом URL
import java.net.MalformedURLException;

// классы, чтобы пропарсить xml-ку c wsdl описанием
// и дотянуться до тега service в нем
import javax.xml.namespace.QName;
import javax.xml.ws.Service;

// интерфейс нашего веб-сервиса (нам больше и нужно)
import ru.javarush.ws.HelloWebService;

public class HelloWebServiceClient {
    public static void main(String[] args) throws MalformedURLException {
        // создаем ссылку на wsdl описание
        URL url = new URL("http://localhost:1986/wss/hello?wsdl");

        // Параметры следующего конструктора смотрим в самом первом теге WSDL описания - definitions
        // 1-ый аргумент смотрим в атрибуте targetNamespace
        // 2-ой аргумент смотрим в атрибуте name
        QName qname = new QName("http://ws.javarush.ru/", "HelloWebServiceImplService");

        // Теперь мы можем дотянуться до тега service в wsdl описании,
        Service service = Service.create(url, qname);
        // а далее и до вложенного в него тега port, чтобы
        // получить ссылку на удаленный от нас объект веб-сервиса
        HelloWebService hello = service.getPort(HelloWebService.class);

        // Ура! Теперь можно вызывать удаленный метод
        System.out.println(hello.getHelloString("JavaRush"));
    }
}
Обратите внимание, сколько нам всего нужно знать заранее. Кроме того, что нужен доступ к wsdl описанию (без этого, уж извините, никак):

URL url = new URL("http://localhost:1986/wss/hello?wsdl");
нужно самому открыть этот xml файл и посмотреть тег definitions, а в нем атрибуты targetNamespace и name, чтобы вызвать конструктор QName:

QName qname = new QName("http://ws.javarush.ru/", "HelloWebServiceImplService");
потом нужно вручную подключиться к тегу service:

Service service = Service.create(url, qname);
а в нем к тегу port:

HelloWebService hello = service.getPort(HelloWebService.class);
и только после этого нам можно вызывать удаленный метод:

hello.getHelloString("JavaRush")
Спрашивается: для этого наши прадеды гибли на полях сражений, чтобы мы теперь все это делали вручную? А если это даже не наш веб-сервис, а чужой. Тогда этот процесс будет еще неприятней. XML формат создан для чтения машиной, а не человеком. Так давайте же заставим машину выполнять грязную работу, а сами насладимся процессом. Для этого нам и делать особо ничего не надо, т.к. в состав нашего любимого SDK, который в Java называется JDK, входит специальная утилита wsimport. Но обо всем по порядку… Для начала давайте создадим новый проект в IDEA, выбрав в меню пункт File > New Project… и дав проекту имя HelloWS. Когда нас спросят, где открыть вновь созданный проект, то нужно ответить New Window, т.е. в новом окне, потому как еще раз замечу, что очень важно, чтобы предыдущий проект был открыт, т.к. мы помним еще с Шага 1, что у нас в том проекте запущен наш веб-сервис. Можно, конечно, его запустить просто через консоль windows, но я этого делать не люблю. Из нового проекта откроем консоль, выбрав View > Tool Windows > Terminal, либо просто нажав Alt+F12. Сейчас мы находимся в корне проекта, а нужно попасть в папку src, поэтому вводим в консоль следующую команду:cd src Теперь настало время воспользоваться утилитой wsimport. Она работает по следующему принципу: мы передаем ей WSDL описание, а она в ответ создает файлы заглушки (так называемые, Stub–классы), которые уже содержат всю необходимую нам функциональность для доступа к веб-сервису. Эти классы разместятся в пакете ru.javarush.ws. Если спросите, откуда берется имя пакета, то отвечу: имя пакета – это развернутое в обратную сторону целевое пространство имен из WSDL описания. Вспоминаем атрибут targetNamespace в теге definitions из WSDL. Там у нас было написано следующее http:// ws.javarush.ru/. И это не адрес сайта, это так принято в xml описывать пространства имен и если отбросить http:// и развернуть то, что останется, в обратном порядке, то получим наше имя пакета. Итак, запустим утилиту: wsimport -keep http://localhost:1986/wss/hello?wsdl Чтобы она сработала нужно, чтобы путь к ней был прописан в переменной окружения PATH, либо можно просто использовать полный путь к ней. У меня она располагается в папке C:\Program Files\Java\jdk1.8.0_31\bin. Обратите внимание, что все, что нужно сделать – это передать через ключ –keep файл WSDL, который у нас доступен удаленно по ссылке, если мы, конечно, не отключили веб-сервис. Что это за классы заглушки? Их всего два. Один из них – это HelloWebService, который по сути является тем же интерфейсом веб-сервиса, который мы создавали вручную на Шаге 1. Разница минимальна и она заключается в том, что немного иначе используются аннотации, с которыми мы уже встречались, а кроме того используются дополнительные аннотации, о которых я ничего не знаю, но раз у нас и без них ранее все работало, то они, очевидно, носят не обязательный характер. Второй класс заглушка – это HelloWebServiceImplService, который наследуется от класса Service. С классом Service мы уже сталкивались в нашем клиенте. Приводить код этого класса не буду, т.к. вряд ли готов пояснить все его строки, но суть класса сводится к тому, что все, что мы ранее писали в клиенте вручную, чтобы подключиться к веб-сервису, в этом классе создано автоматически и нам достаточно вызвать один его метод и все у нас будет в ажуре. Поэтому давайте перепишем код нашего клиента в новом проекте с использованием этих классов и убедимся, что код получается более лаконичным. Для начала в папке src нового проекта создадим пакет ru.javarush.client, а в нем класс HelloWebServiceClient с методом main:

package ru.javarush.client;

// подключаем классы-заглушки
import ru.javarush.ws.*;

public class HelloWebServiceClient {
    public static void main(String[] args) {
        // подключаемся к тегу service в wsdl описании
        HelloWebServiceImplService helloService = new HelloWebServiceImplService();
        // получив информацию из тега service подключаемся к самому веб-сервису
        HelloWebService hello = helloService.getHelloWebServiceImplPort();

        // обращаемся к веб-сервису и выводим результат в консоль
        System.out.println( hello.getHelloString("JavaRush Community") );
    }
}
Разбор кода элементарный и вполне достаточно того, что я описал в комментариях. Запустив клиента, мы должны увидеть строку: Hello, JavaRush Community! При этом клиент из проекта с Шага 1 будет продолжать работать и выводить свой текст, который мы в нем прописали, а именно: Hello, JavaRush! На этом, пожалуй, можно и закончить этот Шаг, т.к. цель его достигнута. Мы поняли, что если есть WSDL описание веб-сервиса, то jdk готово предоставить нам автоматическую генерацию stub-классов-заглушек для упрощения написания клиента к данному веб-сервису. На мой взгляд, это очень полезная возможность на случай, когда хочется потестировать чужой веб-сервис и не залезать глазками в его WSDL описание. Взгляд в будущее В следующей статейке о веб-сервисах я бы хотел изложить идеи, как задеплоить веб-сервис в контейнер сервлетов Tomcat и в разные сервера приложений, чтобы не нужно было запускать веб-сервис, как отдельное приложение, как мы это делали на первых 2-х Шагах. Но перед этим, полагаю, лучше будет сделать небольшое отступление на тему, что такое сервлеты, контейнеры сервлетов и чем они отличаются от серверов приложений и обычных веб-с.... Кроме того, придется сделать краткий обзор серверов приложений, которые, на мой взгляд, заслуживают нашего с вами внимания.
Комментарии (17)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Алишка Гасымов Уровень 1
25 июня 2019
Здравствуйте, Я генерирую java классы используя плагин mvn generated-sources классы генерирются успешно. Но при попытке вызова метода вылезает ошибка 500 "class stream.RegisterResponse don't have a property of the name serial" RegisterResponse это класс который был сгенерирован из wsdl и в этом классе есть поле с названием serial с модификатором доступа protected String. Прошу помочь очень срочно
Anonymous #1155361 Уровень 21, Новосибирск, Россия
12 марта 2019
Здравствуйте! Под впечатлением статьи, используя утилиту wsimport, решил потренироваться на вэб-сервисе Яндекс. Для чего использую http://speller.yandex.net/services/spellservice?WSDL Генерирую java-классы: wsimport -d C:\soap_project3\src http://speller.yandex.net/services/spellservice?WSDL -Xnocompile Классы получаются нормальныее. Уже в своем методе подключение к вэб-сервису происходит успешно, Но не удается от вэб-сервиса получить данные для SpellResult, он равен null, и это при том, что тестирование через SoapUI происходит успешно, SpellResult в SoapUI заполняется данными возвращаемыми Яндексом. В чем моя ошибка? Вот код метода, где я делаю запроса к веб-сервису Яндекс:

    private void callWebService() {
        SpellService service = new SpellService();
        SpellServiceSoap port = service.getSpellServiceSoap();

        ObjectFactory objectFactory = new ObjectFactory();

        CheckTextRequest checkTextRequest = objectFactory.createCheckTextRequest();
        checkTextRequest.setLang("en");
        checkTextRequest.setFormat(null);
        checkTextRequest.setOptions(0);
        checkTextRequest.setText("I lov Java");
        //этот запрос отрабатывается успешно
        CheckTextResponse checkTextResponse = port.checkText(checkTextRequest);
        //успешно выводим на экран результат
        System.out.println(checkTextResponse.getSpellResult());

        CheckTextsRequest checkTextsRequest = objectFactory.createCheckTextsRequest();
        checkTextsRequest.setLang("en");
        checkTextsRequest.setFormat(null);
        checkTextsRequest.setOptions(0);
        //этот запрос тоже отрабатывается успешно, исключения не возникает
        CheckTextsResponse checkTextsResponse = port.checkTexts(checkTextsRequest);
        //Но spellResult получается null
        System.out.println(checkTextsResponse.getArrayOfSpellResult().spellResult.size());
    }
Abdulaziz Уровень 23, Узбекистан
7 декабря 2018
кто может показать в терминале что писать? Полный
Abdulaziz Уровень 23, Узбекистан
7 декабря 2018
Я эту часть не очень понял -- Чтобы она сработала нужно, чтобы путь к ней был прописан в переменной окружения PATH, либо можно просто использовать полный путь к ней. У меня она располагается в папке C:\Program Files\Java\jdk1.8.0_31\bin.
Abdulaziz Уровень 23, Узбекистан
7 декабря 2018
Подскажите пожалуйста, у меня не получается wsimport -keep http://localhost:1986/wss/hello?wsdl. "wsimport" не является внутренней или внешней командой, исполняемой программой или пакетным файлом.
Williamcore Уровень 23, Россия
12 апреля 2015
Хорошая статейка. Автор молодец, твои начинания не останутся без внимания!
myromeu Уровень 11
26 марта 2015
Спасибо за такое простое объяснение) Где я могу почитать зачем вообще городить весь этот огород c XML и почему не сделать просто вызов http (например site.ru/get/last/news, а по этому адресу лежит класс с методом, который работает с базой данных и отдает нам последние новости). И последний вопрос, но тоже очень важный и интересный для меня, как из этих знаний сайт построить? Т.е. результат нашего метода
getHelloString
вставить в шаблон?
Gradus Уровень 27, Санкт-Петербург, Польша
23 марта 2015
Thnx!