Обращение к Web-сервису
Материал из BiTel WiKi
(→Генерация кода и обращение к сервису) |
|||
(5 промежуточных версий не показаны.) | |||
Строка 1: | Строка 1: | ||
+ | == Генерация кода и обращение к сервису == | ||
1. Получение WSDL файла с описанием сервиса. | 1. Получение WSDL файла с описанием сервиса. | ||
Загрузка браузером либо иным способом по URL вида: '''http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl''' WSDL файла. | Загрузка браузером либо иным способом по URL вида: '''http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl''' WSDL файла. | ||
Строка 7: | Строка 8: | ||
/opt/java/jdk/bin/wsimport ws1c.wsdl -Xnocompile -keep -extension -d ~/workspace/CRM/src -p ru.bgcrm.plugin.ocu.ws | /opt/java/jdk/bin/wsimport ws1c.wsdl -Xnocompile -keep -extension -d ~/workspace/CRM/src -p ru.bgcrm.plugin.ocu.ws | ||
</source> | </source> | ||
+ | |||
+ | По вновь полученным исходным кодам можно глобальной заменой заменить javax.xml.datatype.XMLGregorianCalendar на java.util.Date, затем XMLGregorianCalendar на Date. | ||
+ | Всё работает и со стандартным классом дата и время. | ||
+ | |||
+ | Возможна проблема при импорте вида: | ||
+ | <pre> | ||
+ | [ERROR] Two declarations cause a collision in the ObjectFactory class. | ||
+ | line 290 of http://demo.local:80/executer/ru.bitel.bgbilling.kernel.tariff.option/TariffOptionService?xsd=1 | ||
+ | |||
+ | [ERROR] (Related to above error) This is the other declaration. | ||
+ | line 103 of http://demo.local:80/executer/ru.bitel.bgbilling.kernel.tariff.option/TariffOptionService?xsd=1 | ||
+ | </pre> | ||
+ | |||
+ | Проблема возникает при генерации класса ObjectFactory в котором реализуется множество методов create.. | ||
+ | Возможно совпадание названий методов создания какого-нибудь поля в объекте и вызова сервиса. В этом случае необходимо либо что-нибудь переименовать в классе сервиса на стороне сервиса | ||
+ | либо использовать конфигурационный файл в котором явно указать имя создающей функции для чего-либо. | ||
+ | |||
+ | [http://archive.is/FQBq7#stackoverflow.com/questions/13422253/xjc-two-declarations-cause-a-collision-in-the-objectfactory-class Пример использования XML биндинга] | ||
3. Пример вызова Web сервиса: | 3. Пример вызова Web сервиса: | ||
Строка 14: | Строка 33: | ||
.... | .... | ||
// код где-то внутри функции | // код где-то внутри функции | ||
- | WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); | + | WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); |
WebServicePortType port = service.getWebServiceSoap(); | WebServicePortType port = service.getWebServiceSoap(); | ||
// вызов собственно функции сервиса | // вызов собственно функции сервиса | ||
Строка 31: | Строка 50: | ||
... | ... | ||
</source> | </source> | ||
+ | |||
+ | Название класса-сервиса и класса-порта (WebService и WebServicePortType) может различаться для разных сервисов. | ||
+ | |||
+ | == Http Basic авторизация == | ||
+ | Для Basic авторизации при вызове сервиса нужно дополнить вызов следующим кодом: | ||
+ | <source lang="java"> | ||
+ | .... | ||
+ | // код где-то внутри функции | ||
+ | WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); | ||
+ | WebServicePortType port = service.getWebServiceSoap(); | ||
+ | |||
+ | Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext(); | ||
+ | requestContext.put( BindingProvider.USERNAME_PROPERTY, username ); | ||
+ | requestContext.put( BindingProvider.PASSWORD_PROPERTY, pswd ); | ||
+ | |||
+ | // вызов собственно функции сервиса | ||
+ | String client = port.getClientByINN( "1234567890" ); | ||
+ | </source> | ||
+ | |||
+ | == Http Basic авторизация включая WSDL описание == | ||
+ | Если и WSDL описание сервиса возвращается только по Basic авторизации, то предыдущий пример упадёт на строке создания самого сервиса: | ||
+ | <source lang="java"> | ||
+ | WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); | ||
+ | </source> | ||
+ | |||
+ | Найдено два способа обойти проблему: | ||
+ | |||
+ | === Способ первый === | ||
+ | <source lang="java"> | ||
+ | Authenticator.setDefault( new Authenticator() | ||
+ | { | ||
+ | protected PasswordAuthentication getPasswordAuthentication() | ||
+ | { | ||
+ | // можно дополнительно анализировать this.getRequestingURL().toString() | ||
+ | return new PasswordAuthentication( username, pswd.toCharArray() ); | ||
+ | } | ||
+ | } ); | ||
+ | |||
+ | WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); | ||
+ | WebServicePortType port = service.getWebServiceSoap(); | ||
+ | |||
+ | // вызов собственно функции сервиса | ||
+ | String client = port.getClientByINN( "1234567890" ); | ||
+ | </source> | ||
+ | |||
+ | При этом данный код становится ненужным: | ||
+ | <source lang="java"> | ||
+ | Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext(); | ||
+ | requestContext.put( BindingProvider.USERNAME_PROPERTY, username ); | ||
+ | requestContext.put( BindingProvider.PASSWORD_PROPERTY, pswd ); | ||
+ | </source> | ||
+ | |||
+ | Способ плох тем, что: | ||
+ | # При наличии обращений к нескольким сервисам придётся в аунтификаторе анализировать URL ендпоинта сервиса и по нему предлагать логины-пароли, придётся создать глобальное хранилище этих паролей. | ||
+ | # При изменении пароля придётся дорабатывать аунтификатор, чтобы пароли не кэшировались (по-умолчанию они запрашиваются только один раз), либо кэш сбрасывать. | ||
+ | # Аунтификатор устанавливается на всю Java машину и при обращении к нескольким Web-сервисам в разных местах приложения постоянно придётся следить за данным глобальным аунтификатором, чтобы никто его не перетёр. | ||
+ | # Потоконебезопасный способ, теоретически в момент между установкой аунтификатора и созданием сервиса может вклиниться другой поток со своим аунтификатором. | ||
+ | |||
+ | === Способ второй (предпочтительный) === | ||
+ | В создание сервиса передать WSDL из файла либо classpath (предпочтительнее). Т.к. в WSDL описании получается и адрес самого сервиса (endpoint), то далее он переопределяется с помощью опции '''ENDPOINT_ADDRESS_PROPERTY'''. | ||
+ | <source lang="java"> | ||
+ | WebService service = new WebService( "".getClass().getResource( "ru.bgcrm.plugin.ocu.wsdl.ws1c.wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); | ||
+ | |||
+ | WebServicePortType port = service.getWebServiceSoap(); | ||
+ | |||
+ | Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext(); | ||
+ | requestContext.put( BindingProvider.USERNAME_PROPERTY, username ); | ||
+ | requestContext.put( BindingProvider.PASSWORD_PROPERTY, pswd ); | ||
+ | requestContext.put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws" ); | ||
+ | |||
+ | // вызов собственно функции сервиса | ||
+ | String client = port.getClientByINN( "1234567890" ); | ||
+ | </source> | ||
+ | |||
+ | == Отладка HTTP обмена == | ||
+ | Для отладки установить системное свойство: | ||
+ | <pre> | ||
+ | -Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true | ||
+ | </pre> | ||
+ | |||
+ | Пример отладки: | ||
+ | <pre> | ||
+ | ---[HTTP request - http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws]--- | ||
+ | Accept: [text/xml, multipart/related] | ||
+ | Authorization: [Basic d2ViOjExMTExMQ==] | ||
+ | Content-Type: [text/xml; charset=utf-8] | ||
+ | SOAPAction: ["http://ws1c.ufanet.ru#WebService:GetClientByName"] | ||
+ | User-Agent: [JAX-WS RI 2.2.4-b01] | ||
+ | <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:GetClientByName xmlns="http://ws1c.ufanet.ru/objects" xmlns:ns2="http://ws1c.ufanet.ru"><ns2:Name>ЭнергоСервис</ns2:Name></ns2:GetClientByName></S:Body></S:Envelope>-------------------- | ||
+ | ---[HTTP response - http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws - 200]--- | ||
+ | null: [HTTP/1.1 200 OK] | ||
+ | Content-Length: [887] | ||
+ | Content-Type: [text/xml; charset=utf-8] | ||
+ | Date: [Wed, 17 Jul 2013 09:39:39 GMT] | ||
+ | Server: [Microsoft-IIS/6.0] | ||
+ | X-Powered-By: [ASP.NET] | ||
+ | <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> | ||
+ | <soap:Header/> | ||
+ | <soap:Body> <m:GetClientByNameResponse xmlns:m="http://ws1c.ufanet.ru"> | ||
+ | <m:return xmlns:xs="http://www.w3.org/2001/XMLSchema" | ||
+ | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||
+ | <Client xmlns="http://ws1c.ufanet.ru/objects"> | ||
+ | <ID>000016932</ID> | ||
+ | <Name>ЭнергоСервис</Name> | ||
+ | <INN>5614023760</INN> | ||
+ | <KPP>561401001</KPP> | ||
+ | </Client> | ||
+ | <Client xmlns="http://ws1c.ufanet.ru/objects"> | ||
+ | <ID>000004159</ID> | ||
+ | <Name>ЭнергоСервис</Name> | ||
+ | <INN>0274136211</INN> | ||
+ | <KPP>027401001</KPP> | ||
+ | </Client> | ||
+ | <Client xmlns="http://ws1c.ufanet.ru/objects"> | ||
+ | <ID>000013871</ID> | ||
+ | <Name>Энергосервис</Name> | ||
+ | <INN>0231005870</INN> | ||
+ | <KPP>023101001</KPP> | ||
+ | </Client> | ||
+ | </m:return> | ||
+ | </m:GetClientByNameResponse></soap:Body> | ||
+ | </soap:Envelope>-------------------- | ||
+ | </pre> |
Текущая версия на 10:57, 22 февраля 2014
Содержание |
Генерация кода и обращение к сервису
1. Получение WSDL файла с описанием сервиса. Загрузка браузером либо иным способом по URL вида: http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl WSDL файла. Сохраняем в файл, назовём его ws1c.wsdl.
2. Генерация Java классов на основании WSDL описания с помощью утилиты wsimport Oracle JDK, исходные файлы генерируются в пакет ru.bgcrm.plugin.ocu.ws, корневой каталог исходников ~/workspace/CRM/src.
/opt/java/jdk/bin/wsimport ws1c.wsdl -Xnocompile -keep -extension -d ~/workspace/CRM/src -p ru.bgcrm.plugin.ocu.ws
По вновь полученным исходным кодам можно глобальной заменой заменить javax.xml.datatype.XMLGregorianCalendar на java.util.Date, затем XMLGregorianCalendar на Date. Всё работает и со стандартным классом дата и время.
Возможна проблема при импорте вида:
[ERROR] Two declarations cause a collision in the ObjectFactory class. line 290 of http://demo.local:80/executer/ru.bitel.bgbilling.kernel.tariff.option/TariffOptionService?xsd=1 [ERROR] (Related to above error) This is the other declaration. line 103 of http://demo.local:80/executer/ru.bitel.bgbilling.kernel.tariff.option/TariffOptionService?xsd=1
Проблема возникает при генерации класса ObjectFactory в котором реализуется множество методов create.. Возможно совпадание названий методов создания какого-нибудь поля в объекте и вызова сервиса. В этом случае необходимо либо что-нибудь переименовать в классе сервиса на стороне сервиса либо использовать конфигурационный файл в котором явно указать имя создающей функции для чего-либо.
Пример использования XML биндинга
3. Пример вызова Web сервиса:
import ru.bgcrm.plugin.ocu.ws.WebService; import ru.bgcrm.plugin.ocu.ws.WebServicePortType; .... // код где-то внутри функции WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); WebServicePortType port = service.getWebServiceSoap(); // вызов собственно функции сервиса String client = port.getClientByINN( "1234567890" );
Здесь:
- http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws - URL, по которому доступен сервис.
- http://ws1c.ufanet.ru - targetNamespace Web-сервиса.
- WebService - name Web-сервиса.
Параметры targetNamespace и name отображаются в начале WSDL описания, в данном случае так:
<definitions name="WebService" targetNamespace="http://ws1c.ufanet.ru"> <types> ...
Название класса-сервиса и класса-порта (WebService и WebServicePortType) может различаться для разных сервисов.
Http Basic авторизация
Для Basic авторизации при вызове сервиса нужно дополнить вызов следующим кодом:
.... // код где-то внутри функции WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); WebServicePortType port = service.getWebServiceSoap(); Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext(); requestContext.put( BindingProvider.USERNAME_PROPERTY, username ); requestContext.put( BindingProvider.PASSWORD_PROPERTY, pswd ); // вызов собственно функции сервиса String client = port.getClientByINN( "1234567890" );
Http Basic авторизация включая WSDL описание
Если и WSDL описание сервиса возвращается только по Basic авторизации, то предыдущий пример упадёт на строке создания самого сервиса:
WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) );
Найдено два способа обойти проблему:
Способ первый
Authenticator.setDefault( new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { // можно дополнительно анализировать this.getRequestingURL().toString() return new PasswordAuthentication( username, pswd.toCharArray() ); } } ); WebService service = new WebService( new URL( "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws?wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); WebServicePortType port = service.getWebServiceSoap(); // вызов собственно функции сервиса String client = port.getClientByINN( "1234567890" );
При этом данный код становится ненужным:
Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext(); requestContext.put( BindingProvider.USERNAME_PROPERTY, username ); requestContext.put( BindingProvider.PASSWORD_PROPERTY, pswd );
Способ плох тем, что:
- При наличии обращений к нескольким сервисам придётся в аунтификаторе анализировать URL ендпоинта сервиса и по нему предлагать логины-пароли, придётся создать глобальное хранилище этих паролей.
- При изменении пароля придётся дорабатывать аунтификатор, чтобы пароли не кэшировались (по-умолчанию они запрашиваются только один раз), либо кэш сбрасывать.
- Аунтификатор устанавливается на всю Java машину и при обращении к нескольким Web-сервисам в разных местах приложения постоянно придётся следить за данным глобальным аунтификатором, чтобы никто его не перетёр.
- Потоконебезопасный способ, теоретически в момент между установкой аунтификатора и созданием сервиса может вклиниться другой поток со своим аунтификатором.
Способ второй (предпочтительный)
В создание сервиса передать WSDL из файла либо classpath (предпочтительнее). Т.к. в WSDL описании получается и адрес самого сервиса (endpoint), то далее он переопределяется с помощью опции ENDPOINT_ADDRESS_PROPERTY.
WebService service = new WebService( "".getClass().getResource( "ru.bgcrm.plugin.ocu.wsdl.ws1c.wsdl" ), new QName( "http://ws1c.ufanet.ru", "WebService" ) ); WebServicePortType port = service.getWebServiceSoap(); Map<String, Object> requestContext = ((BindingProvider)port).getRequestContext(); requestContext.put( BindingProvider.USERNAME_PROPERTY, username ); requestContext.put( BindingProvider.PASSWORD_PROPERTY, pswd ); requestContext.put( BindingProvider.ENDPOINT_ADDRESS_PROPERTY, "http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws" ); // вызов собственно функции сервиса String client = port.getClientByINN( "1234567890" );
Отладка HTTP обмена
Для отладки установить системное свойство:
-Dcom.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true
Пример отладки:
---[HTTP request - http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws]--- Accept: [text/xml, multipart/related] Authorization: [Basic d2ViOjExMTExMQ==] Content-Type: [text/xml; charset=utf-8] SOAPAction: ["http://ws1c.ufanet.ru#WebService:GetClientByName"] User-Agent: [JAX-WS RI 2.2.4-b01] <?xml version="1.0" ?><S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/"><S:Body><ns2:GetClientByName xmlns="http://ws1c.ufanet.ru/objects" xmlns:ns2="http://ws1c.ufanet.ru"><ns2:Name>ЭнергоСервис</ns2:Name></ns2:GetClientByName></S:Body></S:Envelope>-------------------- ---[HTTP response - http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws - 200]--- null: [HTTP/1.1 200 OK] Content-Length: [887] Content-Type: [text/xml; charset=utf-8] Date: [Wed, 17 Jul 2013 09:39:39 GMT] Server: [Microsoft-IIS/6.0] X-Powered-By: [ASP.NET] <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Header/> <soap:Body> <m:GetClientByNameResponse xmlns:m="http://ws1c.ufanet.ru"> <m:return xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Client xmlns="http://ws1c.ufanet.ru/objects"> <ID>000016932</ID> <Name>ЭнергоСервис</Name> <INN>5614023760</INN> <KPP>561401001</KPP> </Client> <Client xmlns="http://ws1c.ufanet.ru/objects"> <ID>000004159</ID> <Name>ЭнергоСервис</Name> <INN>0274136211</INN> <KPP>027401001</KPP> </Client> <Client xmlns="http://ws1c.ufanet.ru/objects"> <ID>000013871</ID> <Name>Энергосервис</Name> <INN>0231005870</INN> <KPP>023101001</KPP> </Client> </m:return> </m:GetClientByNameResponse></soap:Body> </soap:Envelope>--------------------