Обращение к Web-сервису

Материал из BiTel WiKi

(Различия между версиями)
Перейти к: навигация, поиск
(Генерация кода и обращение к сервису)
 
(3 промежуточные версии не показаны)
Строка 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();
// вызов собственно функции сервиса
// вызов собственно функции сервиса
Строка 33: Строка 52:
Название класса-сервиса и класса-порта (WebService и WebServicePortType) может различаться для разных сервисов.
Название класса-сервиса и класса-порта (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" );

Здесь:

  1. http://bumer.core.ufanet.ru/ufanettest/ws/ws1c.1cws - URL, по которому доступен сервис.
  2. http://ws1c.ufanet.ru - targetNamespace Web-сервиса.
  3. 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 );

Способ плох тем, что:

  1. При наличии обращений к нескольким сервисам придётся в аунтификаторе анализировать URL ендпоинта сервиса и по нему предлагать логины-пароли, придётся создать глобальное хранилище этих паролей.
  2. При изменении пароля придётся дорабатывать аунтификатор, чтобы пароли не кэшировались (по-умолчанию они запрашиваются только один раз), либо кэш сбрасывать.
  3. Аунтификатор устанавливается на всю Java машину и при обращении к нескольким Web-сервисам в разных местах приложения постоянно придётся следить за данным глобальным аунтификатором, чтобы никто его не перетёр.
  4. Потоконебезопасный способ, теоретически в момент между установкой аунтификатора и созданием сервиса может вклиниться другой поток со своим аунтификатором.

Способ второй (предпочтительный)

В создание сервиса передать 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>--------------------
Личные инструменты