Программные интерфейсы JAX-WS Сlient API в Web Services Feature Pack for WebSphere Application Server V6.1: Часть 3. Использование модели асинхронного программирования JAX-WS

Последняя часть серии статей об интерфейсах JAX-WS 2.0 в WebSphere Application Server V6.1 Feature Pack for Web Services знакомит с созданием асинхронного Web-клиента и использованием моделей polling (поллинг или опрос) и callback (обратный вызов).

Нихил Такер, старший инженер-программист, IBM

Нихил Такер (Nikhil Thaker) - фотографияНихил Такер (Nikhil Thaker) работает старшим инженером-программистом IBM Software Group в группе разработки WebSphere, занимающейся созданием Web Services Feature Pack. Он имеет более чем 9-летний опыт работы в области Enterprise Application Integration (интеграция корпоративных приложений), а в последние 2 года основное внимание уделяет Web-сервисам. Нихил работал с различными клиентами IBM в качестве ИТ-специалиста IBM Global Services по Enterprise Application Integration. Имеет опыт работы в автомобильной отрасли, здравоохранении, телекоммуникациях и сфере коммунальных услуг. С ним можно связаться по адресу nikhil.v.thaker@us.ibm.com.



23.01.2012

Введение

В части 1 и части 2 данной серии описаны программные интерфейсы JAX-WS Client API и использование модели программирования JAX-WS для создания клиента Dispatch и динамического прокси-клиента. В настоящей статье мы продолжим обсуждение клиентских программных интерфейсов и продемонстрируем создание асинхронного клиента Web-сервиса.

В данной статье:

  1. Представлена модель асинхронного программирования.
  2. Приведены примеры моделей polling и callback.
  3. Описан шаблон асинхронного обмена сообщениями (Message Exchange Pattern - MEP).

Модель асинхронного программирования JAX-WS 2.0

В части 2 (EN) вы познакомились с динамическим прокси-клиентом JAX-WS. В этой статье вы познакомитесь с моделью асинхронного программирования JAX-WS. Модель асинхронного программирования - это главное отличие JAX-WS от JAX-RPC, которая предоставляла только синхронные программные интерфейсы.

В нашей предыдущей статье рассматривалось упрощенное банковское приложение. Вот сигнатура метода createAccount:

int createAccount(String customerName, double initialBalance);

В случае синхронного вызова этого метода запрос, вероятно, будет завершен в течение нескольких секунд. Однако современные приложения, особенно в сервис-ориентированной архитектуре, могут потребовать значительно больше времени. Типичные требования к созданию нового банковского счета могут включать проверки кредитоспособности, подтверждение адреса, проверки различными правительственными учреждениями и даже ручную обработку.

Модель программирования JAX-WS предусматривает два метода асинхронного вызова операций - polling и callback. Оба метода позволяют клиенту продолжать обработку во время ожидания ответа.

Асинхронные интерфейсы конечной точки сервиса (Service Endpoint Interfaces - SEI)

В листинге 1 приведен пример WSDL-файла из части 2 , который мы используем и в данной статье. Обратите внимание, что мы ничего не меняли в WSDL, чтобы сделать его асинхронным.

Листинг 1. WSDL-определение BankingService.wsdl
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="BankingService"
   xmlns="http://schemas.xmlsoap.org/wsdl/"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns="http://www.example.com/services/Banking"
   xmlns:types="http://www.example.com/schemas/Banking"
   targetNamespace="http://www.example.com/services/Banking">

   <types>
      <schema
		xmlns="http://www.w3.org/2001/XMLSchema"
	 	targetNamespace="http://www.example.com/schemas/Banking">

		<!--
		определение bean-компонентов запроса и ответа операции createAccount
		-->
		<element name="createAccount">
			<complexType>
				<sequence>
					<element name="owner" type="string" />
				<element name="initialBalance" type="double" />
				</sequence>
			</complexType>
		</element>

		<element name="createAccountResponse">
			<complexType>
				<sequence>
					<element name="accountNumber" type="long" />
				</sequence>
			</complexType>
		</element>


		<!--
		определение bean-компонентов запроса, ответа и ошибки операции withdraw
		-->
		<element name="withdraw">
			<complexType>
				<sequence>
					<element name="accountNumber" type="long" />
					<element name="amount" type="double" />
				</sequence>
			</complexType>
		</element>

		<element name="withdrawResponse">
			<complexType>
				<sequence>
					<element name="amount" type="double" />
				</sequence>
			</complexType>
		</element>

		<element name="InsufficientFunds">
			<complexType>
				<sequence>
					<element name="errorMessage" type="string" />
					<element name="errorCode" type="int" />
				</sequence>
			</complexType>
		</element>

		<!--
		определение bean-компонентов запроса и ответа операции getAccountInfo
		-->
		<element name="getAccountInfo">
			<complexType>
				<sequence>
					<element name="accountNumber" type="long" />
				</sequence>
			</complexType>
		</element>

		<element name="getAccountInfoResponse">
			<complexType>
				<sequence>
				  <element name="balance" type="double" />
				  <element name="owner" type="string" />
				</sequence>
			</complexType>
		</element>
	</schema>
   </types>

   <message name="createAccountRequest">
      <part name="request" element="types:createAccount"/>
   </message>

   <message name="createAccountResponse">
      <part name="response" element="types:createAccountResponse"/>
   </message>

   <message name="withdrawRequest">
      <part name="request" element="types:withdraw "/>
   </message>

   <message name="withdrawResponse">
      <part name="response" element="types:withdrawResponse"/>
   </message>

   <message name="InsufficientFunds">
      <part name="error" element="types:InsufficientFunds"/>
   </message>

   <message name="getAccountInfoRequest">
      <part name="request" element="types:getAccountInfo"/>
   </message>

   <message name="getAccountInfoResponse">
      <part name="response" element="types:getAccountInfoResponse"/>
   </message>

   <portType name="BankingSEI">
      <operation name="createAccount">
         <input message="tns:createAccountRequest"/>
         <output message="tns:createAccountResponse"/>
      </operation>

      <operation name="withdraw">
         <input message="tns:withdrawRequest"/>
         <output message="tns:withdrawResponse"/>
	   <fault message="tns:InsufficientFunds"/>
      </operation>

      <operation name="getAccountInfo">
         <input message="tns:getAccountInfoRequest"/>
         <output message="tns:getAccountInfoResponse"/>
      </operation>
   </portType>
   
   <binding name="BankingSoap11Binding" type="tns:BankingSEI">
      <soap:binding
		style="document" 
		transport="http://schemas.xmlsoap.org/soap/http"/>

      <operation name="createAccount">
         <soap:operation soapAction="createAccount"/>
         <input>	
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
      </operation>

      <operation name=" withdraw ">
         <soap:operation soapAction="withdraw"/>
         <input>
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
         <fault>
            <soap:fault name="InsufficientFunds" use="literal"/>
         </fault>
      </operation>

      <operation name="getAccountInfo">
         <soap:operation soapAction="getAccountInfo"/>
         <input>	
            <soap:body use="literal"/>
         </input>
         <output>
            <soap:body use="literal"/>
         </output>
      </operation>

   </binding>

   <service name="BankingService">
      <port binding="tns: BankingSoap11Binding" name="AccountsPort">
         <soap:address
             location="http://localhost:8080/banking/services
		/BankingService"/>
      </port>
   </service>
</definitions>

Для создания переносимых артефактов при помощи WSDL из листинга 1 можно использовать различные инструментальные средства, такие как WebSphere Application Server Toolkit или утилита командной строки wsimport, расположенная по адресу WAS_HOME/bin/wsimport. В данной статье мы применим wsimport. Чтобы использовать wsimport для создания асинхронных артефактов, необходим отдельный файл настройки связывания, как показано в листинге 2.

Листинг 2. Файл настройки связывания binding.xml
<bindings 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    wsdlLocation="BankingService.wsdl"
    xmlns="http://java.sun.com/xml/ns/jaxws">    
    <bindings node="wsdl:definitions">
        <enableAsyncMapping>true</enableAsyncMapping>
    </bindings>
</bindings>

В листинге 2 показана настройка простого связывания для создания асинхронных версий всех методов в WSDL-файле. Обратите внимание, что элемент wsdlLocation должен совпадать с именем WSDL-файла. Вызовите утилиту wsimport следующим образом:
%WAS_HOME%\bin\wsimport.bat –b binding.xml BankingService.xml

Созданный интерфейс конечной точки сервиса будет иметь следующие операции, определенные для createAccount:

Листинг 3. Интерфейс конечной точки сервиса для createAccount
// Синхронный метод
int createAccount(String customerName, double initialBalance);

// Polling-метод
javax.xml.ws.Response<CreateAccountResponse> createAccount(String customerName, 
double initialBalance);

// Сallback-метод
javax.concurrent.Future<?> createAccount(String customerName, double initialBalance, 
AsyncHandler<CreateAccountResponse> handler);

Пример polling-метода

Асинхронные polling-клиенты полезны для реализации основанных на Web-сервисах потоков работ. После инициирования запроса polling-клиент вправе выполнять другую работу до завершения запроса. Это позволяет клиенту выполнять другие задачи, не зависящие от результатов запроса. После завершения запроса ответ становится доступным для клиента.

В таблице 1 описывается поведение объекта javax.xml.ws.Response.

Таблица 1. Поведение объекта javax.xml.ws.Response
cancel()Отменяет обработку ответа. Обработка запроса сервером не прекращается.
get()Получает ответ, если он есть. Если ответа нет, метод блокируется.
get(long timeout, TimeUnit unit)Получает ответ, но блокирует только на определенный период времени.
isDone()Проверяет, есть ли ответ.
isCancelled()Проверяет, была ли отменена обработка ответа.
getContext()Возвращает BindingProvider responseContext, ассоциированный с запросом.

В листинге 4 приведен пример клиента, выполняющего асинхронный polling-запрос.

Листинг 4. Пример polling-метода
// Polling-метод
Response<CreateAccountResponse> response = createAccount
("Joe Customer", 10.00);

// ожидание ответа
while (!response.isDone()){
   // выполнение работы, не зависящей от создания нового счета
   …
}

// получения номера счета
try {
int accountNumber = response.get().getAccountNumber();
} catch (CancellationException ce) {
	// обработка отменена при помощи response.cancel()

} catch (ExecutionException ee} {
	// ошибка обработки запроса
	// getCause() возвращает текущую исключительную ситуацию
	Systemout.println(ee.getCause());
}

Одним из преимуществ polling-метода является то, что ответ не обрабатывается до вызова метода response.get(), т.е. клиент полностью управляет обработкой ответа. Таким образом, он очень похож на свой синхронный аналог.


Пример callback-метода

Асинхронные callback-клиенты полезны для реализации пакетной обработки данных и программ уведомления потребителя. После инициирования запроса callback-клиент вправе выполнять другую работу до завершения запроса, однако ответ недоступен для клиента. Текущий ответ обрабатывает указанная реализация AsyncHandler. Поток кода после попадания ответа в обработчик обратного вызова такой же, как и для polling-клиента.

В таблице 2 описывается поведение javax.concurrent.Future в асинхронном callback-клиенте.

Таблица 2. Поведение javax.concurrent.Future в асинхронном callback-клиенте
cancel()Отменяет обработку ответа. Обработка запроса сервером не прекращается.
get()Клиенты не должны вызывать этот метод, поскольку его поведение не определено.
get(long timeout, TimeUnit unit)Клиенты не должны вызывать этот метод, поскольку его поведение не определено.
isDone()Проверяет, есть ли ответ.
isCancelled()Проверяет, была ли отменена обработка ответа.

В листинге 5 приведен клиент, инициирующий асинхронный callback-метод. Обратите внимание, что цикл while совершенно не обязателен, поскольку текущий ответ (включая сбои при доставке или обработке запроса) будет предоставлен обработчику обратного вызова.

Листинг 5. Инициирование callback-метода
// Сallback-метод
CreateAcountHandler handler = new CreateAcountHandler();
Future>?< monitor = createAccount("Joe Customer", 10.00, handler);

// ожидание ответа
while (!monitor.isDone()){
   // выполнение работы, не зависящей от создания нового счета
   …
}

В листинге 6 показана реализация обработчика AsyncHandler.

Листинг 6. Реализация AsyncHandler
public class CreateAcountHandler implements AsyncHandler<CreateAccountResponse> {
	public void handleResponse(Response<CreateAccountResponse> response) {

		// получения номера счета
		try {
		int accountNumber = response.get().getAccountNumber();
	} catch (CancellationException ce) {
		// обработка отменена при помощи response.cancel()

	} catch (ExecutionException ee} {
		// ошибка обработки запроса
		// getCause() возвращает текущую исключительную ситуацию
		Systemout.println(ee.getCause());
	}
	}
}

Соображения по поточной обработке

Callback-вызовы обрабатывают ответ в потоке, отличном от того, в котором выполняется клиент. Это дает клиенту определенную возможность контролировать производительность в случае большого количества одновременных асинхронных запросов. Подобную возможность можно получить, создав исполнителя (Executor) при помощи метода BankingService.setExecutor(). Исполнители предоставляют как пул потоков, так и средства среды исполнения JAX-WS для организации очереди асинхронных ответов, подлежащих обработке. В листинге 7 приведен пример использования фабрики исполнителей для создания пула из 10 потоков.

Листинг 7. Пример исполнителя
BankingService svc = new BankingService();
svc.setExecutor(java.util.concurrent.Executors.newFixedThreadPool(10));

В WebSphere есть исполнитель по умолчанию, поэтому предоставлять собственного исполнителя не обязательно.


Шаблон асинхронного обмена сообщениями

До сих пор мы говорили только о модели асинхронного программирования, в которой текущие запросы являются асинхронными и зависят от таймаутов чтения и записи сокета. Чтобы сделать эти запросы асинхронными на уровне HTTP-соединения, необходимо настроить дополнительные свойства в контексте запроса. Чтобы включить асинхронный обмен сообщениями, в контексте запроса установите com.ibm.websphere.webservices.use.async.mep в true.

Листинг 8. Включение асинхронного обмена сообщениями
// Создание динамического прокси-клиента
BankingService service = new BankingService();
BankingSEI port = service.getAccountsPort();

// Использование экземпляра прокси как BindingProvider
BindingProvider bp = (BindingProvider) port;

Map<String, Object> rc = bp.getRequestContext();
rc.put("com.ibm.websphere.webservices.use.async.mep", Boolean.TRUE);

// асинхронный вызов операции

При включении асинхронного обмена сообщениями включается поддержка спецификации WS-Addressing и в SOAP-запрос добавляется заголовок wsa:ReplyTo. HTTP-запрос немедленно подтверждается, и текущее соединение закрывается. Затем клиент запускает локальный HTTP-сервер и прослушивает ответ конечной точки.

Обратите внимание, что это свойство является специфичным для IBM-реализации JAX-WS и может не работать в реализациях других производителей. Однако оно совместимо с любой конечной точкой, поддерживающей спецификацию WS-Addressing, например, с другим WebSphere Application Server или с конечными точками Microsoft .NET.


Заключение

JAX-WS представляет модель асинхронного программирования при помощи программных интерфейсов polling и callback. Асинхронные вызовы Web-сервисов являются неблокирующими, т.е. приложение может вызвать Web-сервис и продолжать выполнение бизнес-логики, не дожидаясь ответа. Такая мощная модель программирования, отсутствующая в JAX-RPC, является достаточным основанием для перехода с JAX-RPC на JAX-WS.

Ресурсы

Научиться

Получить продукты и технологии

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=WebSphere, SOA и web-сервисы
ArticleID=788644
ArticleTitle=Программные интерфейсы JAX-WS Сlient API в Web Services Feature Pack for WebSphere Application Server V6.1: Часть 3. Использование модели асинхронного программирования JAX-WS
publish-date=01232012