JavaRush/Java блог/Архив info.javarush/Веб-сервисы. Шаг 2. Как упростить написание клиента?
eGarmin
41 уровень

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

Статья из группы Архив info.javarush
участников
В этой краткой заметке я бы хотел вернуться к коду клиента веб-сервиса, который мы написали на предыдущем шаге. При этом я буду предполагать, что у вас открыта 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)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Anonymous #1155361
Уровень 21
12 марта 2019, 11:47
Здравствуйте! Под впечатлением статьи, используя утилиту 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, 05:56
кто может показать в терминале что писать? Полный
Abdulaziz
Уровень 23
7 декабря 2018, 05:55
Я эту часть не очень понял -- Чтобы она сработала нужно, чтобы путь к ней был прописан в переменной окружения PATH, либо можно просто использовать полный путь к ней. У меня она располагается в папке C:\Program Files\Java\jdk1.8.0_31\bin.
Abdulaziz
Уровень 23
7 декабря 2018, 05:55
Подскажите пожалуйста, у меня не получается wsimport -keep http://localhost:1986/wss/hello?wsdl. "wsimport" не является внутренней или внешней командой, исполняемой программой или пакетным файлом.
23 декабря 2023, 18:16
у меня "старая" Java не прописана в PATH, я делал так:
C:\"Program Files"\BellSoft\LibericaJDK-8\bin\wsimport.exe -keep http://localhost:1986/wss/hello?wsdl
Williamcore
Уровень 23
12 апреля 2015, 00:27
Хорошая статейка. Автор молодец, твои начинания не останутся без внимания!
myromeu
Уровень 11
26 марта 2015, 21:45
Спасибо за такое простое объяснение) Где я могу почитать зачем вообще городить весь этот огород c XML и почему не сделать просто вызов http (например site.ru/get/last/news, а по этому адресу лежит класс с методом, который работает с базой данных и отдает нам последние новости). И последний вопрос, но тоже очень важный и интересный для меня, как из этих знаний сайт построить? Т.е. результат нашего метода
getHelloString
вставить в шаблон?
eGarmin
Уровень 41
26 марта 2015, 22:33
Слушай, твои вопросы, что называется «в точку»!

По поводу первого вопроса и всего этого огорода, я так понимаю, ответом будут, так называемые, RESTful веб-сервисы, которые и есть способ отхода от SOAP протокола в сторону обычных для HTTP post-запросов.

По поводу второго вопроса, тут несколько сложнее будет объяснить, т.к. все зависит от твоего уровня знаний по поводу сервлетов. Но я бы для решения этой ситуации взял бы какой-нибудь веб-фреймворк на основе паттерна MVC (Model-View-Controller – Модель – Вид — Контроллер) типа Struts или SpringMVC и в java-классе (т.е. в классе-сервлете или, иначе говоря, контроллере) сделал бы вызов веб-сервиса, а потом через модель (или атрибуты запроса или сеанса) передал бы эту информацию на html-страницу (т.е. на вид по модели MVC).
Надеюсь что-нибудь проянилось. Но тема сайтов – это отдельный вопрос и на него сложно ответить в двух словах
Litle
Уровень 9
26 марта 2015, 23:04
Вэбсервисы — это способ для реализации платформонезависимого взаимодействия распределённых объектов. Это не сайты, это гораздо сложнее и масштабнее… :)
почему не сделать просто вызов http

потому, что часто этого недостаточно. К тому же лучше делать это не так как Вы предложели, а согласно REST подходу.
REST подходит для CRUD операций над ресурсами, но в корпоративном сегменте сильно актуален RPC. Это особенность взаимодействия распределённых систем. Если в кратце, то в чем основные отличия RPC от REST:

RPC(SOAP) vs REST
— REST легко маштабируется, т.к. stateless!
— REST автоматом кешируется, т.к. stateless!
— Для SOAPа на уровне протокола реализована валидация запросов, безопасность и обработка ошибок!
— SOAP тяжелее по трафику.
— Для соапа нужны генераторы прокси и wsdl. Иначе хрен реализуешь.
— Из-за того что SOAP statefull — можно работать по транзакционной модели. Делать откаты!

Следование REST стилю позволяет удешевить разработку клиента и сервера и упростить масштабирование приложения. Время разработки, а значит и стоимость SOAP сервиса дороже из-за сложности разработки api операций.

всё имхо!!!
Litle
Уровень 9
26 марта 2015, 23:13
И последний вопрос, но тоже очень важный и интересный для меня, как из этих знаний сайт построить?
На основе этих знаний строится api для удаленного взаимодействия распределенных объектов! Т.е. это юрисдикция приложений. Приложения — это не сайты. :) вебсервисы — это альтернатива RMI, который тоже является RPC подходом — но только для java компонентов.

Что бы сайты делать — ява не нужна…
myromeu
Уровень 11
26 марта 2015, 23:29
Спасибо за ответ. Ваш совет про фреймворк я попробую, но сначала поиграю с JAX-WS.
myromeu
Уровень 11
26 марта 2015, 23:35
Спасибо за помощь.
Не по теме вопрос… Вот допустим я хочу работать java-программистом и как же мне набираться опыта и набивать руку, чтобы было что показать работодателю? Вот я понял, что эту технологию используют в корпоративных приложениях, то есть потенциальный работодатель, видя что у меня нет опыта, обрадовался бы тому, что я умею такие приложения делать. Так вроде технология не сложная, реализация на java очень понятная, но немного смущает этот XML и незнание куда дальше hello-world'а двигаться(
eGarmin
Уровень 41
26 марта 2015, 23:46
А что тебя смущает в хэлловорде в данном конкретном случае? Это лишь пример. Метод может возвращать что угодно. Ты можешь в эту удаленную функцию/метод/процедуру запихать что хочешь. Хочешь вычисление интеграла, а хочешь искусственный интеллект))). Здесь речь лишь о том, что обычное java-приложение при помощи веб-сервисов можно разбить на блоки, которые работают пусть даже в разных уголках мира, но при этом могут при необходимости передавать друг другу информацию для выполнения общей задачи
myromeu
Уровень 11
26 марта 2015, 23:47
Это я как раз таки понял) Но как сделать так что бы с этим приложением легко было взаимодействовать пользователю. GUI другими словами как приделать, так что бы правильно и просто?
Litle
Уровень 9
27 марта 2015, 00:00
Так вроде технология не сложная
Технология не сложная только за счет непонимания всего объема и фрейворков подобных jax-ws, spring-ws и т.д. которые всего лишь сняли с разработчика гемор работы с авто-отправкой и генерацией soap сообщений. Т.е. программист не должен ручками ничего генерить… Однако вся сложность нашего кодерского бытия как была так и осталась. :)
При этом тот же soap — это куча спецификаций… Безопасность, шифрование и т.д.
eGarmin
Уровень 41
27 марта 2015, 00:21
Ну, ГУИ — это вообще отдельная тема. Хочешь для десктопа делать софт — осваивай Swing, Awt, SWT (на выбор, но awt — это уж точно старьё). Хочешь делать софт для веба, тогда изучай сервера приложений, сервлеты и всякие там надстройки над сервлетами типа фронт-энд-фреймворков, которые я уже называл: struts, springMVC, gwt, vaadin и прочее. Тут выбор очень большой и самое сложное для новичка — это понять с какого фреймворка начать.
Gradus
Уровень 27
23 марта 2015, 09:11
Thnx!