Содержание


Программные интерфейсы JAX-WS Сlient API в Web Services Feature Pack for WebSphere Application Server V6.1

Часть 3. Использование модели асинхронного программирования JAX-WS

Comments

Серия контента:

Этот контент является частью # из серии # статей: Программные интерфейсы JAX-WS Сlient API в Web Services Feature Pack for WebSphere Application Server V6.1

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Программные интерфейсы JAX-WS Сlient API в Web Services Feature Pack for WebSphere Application Server V6.1

Следите за выходом новых статей этой серии.

В части 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.


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

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