On-Line Библиотека www.XServer.ru - учебники, книги, статьи, документация, нормативная литература.
       Главная         В избранное         Контакты        Карта сайта   
    Навигация XServer.ru








 

SOAP

Brett McLaughlin Перевод Илья Чекменев

SOAP - это простой протокол доступа к объектам (Simple Object Access Protocol). Если вы никогда прежде о нем не слышали, то должно быть вы живете в какой-нибудь глуши, вдали от цивилизации. Он стал последним писком моды в web программировании, и неотъемлемой частью web сервисов, которые с таким фанатизмом используются в web разработках последнего поколения. Если вы слышали о .NET, детище Microsoft, или peer-to-peer "революции", то вы слышали о технологиях, которые основаны на использовании SOAP (даже если вы не знаете что это такое). Существует не одна, а две реализации SOAP, от Apache и от Microsoft, которой посвящены тысячи страниц на их сайте технической поддержки MSDN (http://msdn.microsoft.com/).

В этой статье я расскажу вам что такое SOAP и почему он является такой важной частью в процессе развития парадигмы web программирования. Это поможет вам опустить фундаментальные основы и сразу приступить непосредственно к работе с инструментарием SOAP. Затем я дам беглый обзор существующих SOAP проектов и углубляюсь в реализацию от Apache. Эта статья не претендует на воссоздание полной картины SOAP, в моей книге "Java & XML, 2-я редакция" восполнено множество пробелов. Ответы на многие из вопросов, возникших после прочтения этой статьи вы найдете в книге.

Вступление

Для начала нужно разобраться в том, что же такое SOAP. Вы можете прочитать полное (и весьма длинное) заключение W3C по адресу http://www.w3.org/TR/SOAP. Затем, разобравшись и отбросив всю шелуху, вы поймете что SOAP это всего лишь протокол. Это простой протокол (для его использования, нет необходимости в написании нового), основанный на идее, что в некоторый момент в распределенной архитектуре возникает необходимость обмена информацией. Кроме того, для систем, в которых существует вероятность перегрузок и затруднений при обработке процессов, этот протокол весьма выгоден тем, что он легковесен и требует минимального количества ресурсов. Наконец, он позволяет осуществлять все операции через HTTP, что дает возможность обойти стороной такие хитрые штуки как firewall и уберечься от прослушивания при помощи сокетов немыслимого числа портов. Главное чтобы вы осознали это, а все остальное - детали.

Разумеется, вы хотели бы знать эти детали, и я не обойду их вниманием. В спецификации SOAP существует три базовых компонента: конверт SOAP (SOAP envelope), набор правил шифровки и средства взаимодействия между запросом и ответом. Давайте думать о сообщении SOAP как об обычном письме. Вы еще помните эти древние штуки в конвертах с почтовой маркой и адресом, записанном на лицевой стороне? Такая аналогия поможет более наглядно представить себе концепцию SOAP как "конверт". Рисунок 12-1 изображает SOAP процессы в форме этой аналогии.

Рисунок 12-1. Процесс сообщений SOAP

Запомните эту картинку и давайте рассмотрим три компонента спецификации SOAP. Я коротко расскажу о каждом из них, приводя примеры, наиболее полно представляющие эту концепцию. Эти три ключевые компонента делают SOAP столь важным и значимым. Обработка ошибок, поддержка различных шифровок, сериализация параметров, и тот факт, что SOAP работает через HTTP в большинстве случаев делают его привлекательнее прочих решений для распределенных протоколов. [1]  SOAP обеспечивает высокую степень взаимодействия с другими приложениями, которые я рассмотрел более подробно в своей книге. А сейчас я хочу сосредоточиться на основных элементах SOAP.

Конверт

Конверт SOAP аналогичен конверту обычного письма. Он содержит информацию о письме, которое будет шифровано в основном разделе SOAP, включая данные о получателе и отправителе, а также информация о самом сообщении. Например, заголовок конверта SOAP может указывать на то, как должно обрабатываться сообщение. Прежде чем приложение начнет обработку сообщения, оно анализирует информацию о сообщении, включая информацию о том, сможет ли оно вообще обработать это сообщение. В отличие от ситуации со стандартными XML-RPC вызовами (припоминаете? XML-RPC сообщения, шифровка и прочее, все объединяется в единый XML фрагмент), с SOAP текущая обработка происходит для того, чтобы узнать что-то о сообщении. Типичное SOAP сообщение может также включать стиль шифровки, которая поможет получателю в обработке сообщения. Пример 12-1 демонстрирует конверт SOAP, который завершается указанием кодировки.

Пример 12-1: Конверт SOAP

<soap:Envelope
   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
   soap:encodingStyle="http://myHost.com/encodings/secureEncoding"
>
  <soap:Body>
    <article xmlns="http://www.ibm.com/developer">
      <name>Soapbox</name>
      <url>
        http://www-106.ibm.com/developerworks/library/x-soapbx1.html
      </url>
    </article>
  </soap:Body>
</soap:Envelope>

Как вы видите, шифровка задана внутри конверта, что позволяет приложению определить (используя значение атрибута encodingStyle), сможет ли оно прочитать входящее сообщение, расположенное в элементе Body. Убедитесь в правильности пространства имен (namespace) конверта SOAP, или серверы SOAP, которые получат ваше сообщение, выдадут сообщение об ошибке из-за несоответствия версий, и вы не сможете взаимодействовать с ними.

Шифровка

Второй важный элемент SOAP - это возможность шифровки пользовательских типов данных. В RPC (и XML-RPC) шифровка может выполняться лишь для заранее определенных типов данных, которые поддерживаются в скачанном вами XML-RPC инструментарии. Шифровка других типов данных требует от вас самостоятельной модификации RPC сервера и клиента. С SOAP схема XML может быть довольно легко использована для указания новых типов данных (при помощи структуры complexType, рассмотренной во 2-й главе моей книги), и эти новые типы могут быть представлены в XML как часть основного раздела SOAP. Благодаря интеграции со схемой XML, вы можете шифровать любой тип данных в SOAP сообщении, логически описав его в схеме XML.

Вызов

Лучший способ понять как работает вызов SOAP - это сравнить его с чем-нибудь, что вам знакомо, например с XML-RPC. Если вы помните, XML-RPC вызов выглядит подобно фрагменту кода, представленному в Примере 12-2.

Пример 12-2. Вызов в XML-RPC

// Указание используемого обработчика (парсера) XML
XmlRpc.setDriver("org.apache.xerces.parsers.SAXParser");
 
// Указание сервера, к которому выполняется подключение
XmlRpcClient client =
    new XmlRpcClient("http://rpc.middleearth.com");
 
// Создание параметров
Vector params = new Vector(  );
params.addElement(flightNumber);
params.addElement(numSeats);
params.addElement(creditCardType);
params.addElement(creditCardNum);
 
// Запрос
Boolean boughtTickets =
    (Boolean)client.execute("ticketCounter.buyTickets", params);
 
// Обработка ответа

Я создал простейшую программу для заказа авиабилетов. Теперь взгляните на Пример 12-3, который демонстрирует вызов в SOAP.

Пример 12-3. Вызов в SOAP

// Создание параметров
Vector params = new Vector(  );
params.addElement(
    new Parameter("flightNumber", Integer.class, flightNumber, null));
params.addElement(
    new Parameter("numSeats", Integer.class, numSeats, null));
params.addElement(
    new Parameter("creditCardType", String.class, creditCardType, null));
params.addElement(
    new Parameter("creditCardNumber", Long.class, creditCardNum, null));
 
// Создание объекта Call
Call call = new Call(  );
call.setTargetObjectURI("urn:xmltoday-airline-tickets");
call.setMethodName("buyTickets");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
call.setParams(params);
 
// Вызов
Response res = call.invoke(new URL("http://rpc.middleearth.com"), "");
 
// Обработка ответа

Как вы видите, собственно вызов, представленный объектом Call, резидентен в памяти. Он позволяет вам задать цель вызова, метод вызова, стиль шифровки, параметры, и многие другие параметры, не представленное в этом примере. Это более гибкий механизм, чем метод XML-RPC, позволяющий вам явно задавать набор различных параметров, которые косвенно определяются в XML-RPC. Далее в этой статье вы узнаете больше о процессе вызова, в том числе вы узнаете как SOAP обрабатывает неверные запросы, иерархию ошибок и, разумеется, возвращаемые результаты вызова.

После такого краткого введения вы уже знаете достаточно, чтобы заинтересоваться этой забавной штукой. А теперь позвольте мне представить вам реализацию SOAP, которую я собираюсь использовать. Я объясню причины по которым остановил свой выбор именно на ней и рассмотрю некоторые примеры кода.

Настройка

Теперь, когда вы познакомились с основами концепции, настало время для самой интересной части: программирования. Для этого вам потребуется удобный проект или продукт, найти который проще чем может показаться на первый взгляд. Если вам нужен Java проект, предоставляющий возможности SOAP, то его не надо долго искать. Существует две группы продуктов: коммерческие и бесплатные. Как и в своей книге, я буду избегать упоминания коммерческих продуктов. Это вовсе не потому что они плохи (даже наоборот, некоторые из них прекрасны), а потому что я хотел бы чтобы любой читатель мог попробовать любой из приведенных примеров. Это связано с доступностью, которой многие коммерческие продукты не обладают. Вы должны заплатить за их использование, или временно использовать их в течении ограниченного периода времени после скачивания.

Таким образом мы плавно подошли к проектам с открытым источником (open source). Из этой области я могу назвать лишь один продукт: Apache SOAP. Он расположен по адресу http://xml.apache.org/soap и предоставляет инструментарий SOAP для Java. На момент написания книги вышла версия 2.2, которую вы можете скачать с web сайта Apache. Именно эту версию я и буду использовать в примерах для этой статьи.

Другие альтернативы

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

Как насчет IBM SOAP4J?

Первым в списке альтернатив значится реализация от IBM: SOAP4J. Работа IBM легла в основу проекта Apache SOAP, также как IBM XML4J переросла в то, что теперь известно как проект XML парсера Apache Xerces. Предполагается что реализация от IBM будет переработана, объединившись с Apache SOAP. Примерно то же случилось с IBM'овским XML4J: теперь он лишь обеспечивает пакетирование в Xerces. Это только лишь подчеркивает тенденции - крупные производители зачастую поддерживают и используют OpenSource проекты, в данном случае оба проекта (Apache и IBM) используют одну и ту же кодовую базу.

Разве Microsoft вне игры?

Разумеется нет. Microsoft и его реализация SOAP, равно как и все направление .NET (более подробно рассмотренное в моей книге), весьма значительны. В действительности я хотел уделить большую часть времени детальному рассмотрению реализации SOAP от Microsoft, но она поддерживает только COM объекты иже с ними и не поддерживает Java. Ввиду этих причин подобное описание не могло войти в статью про Java и XML. Тем не менее, Microsoft (несмотря на все претензии, которые мы, как разработчики, имеем к этой компании) проделала важную работу в сфере web сервисов, и вы совершите ошибку если без раздумий ее отвергнете, руководствуясь лишь голыми эмоциями. Если у вас есть необходимость работать с COM или компонентами Visual Basic, я настоятельно рекомендую вам попробовать использовать инструментарий Microsoft SOAP, доступный по адресу http://msdn.microsoft.com/library/default.asp?url=/nhp/Default.asp?contentid=28000523 наряду с множеством других ресурсов SOAP.

Что такое Axis?

Те из вас, кто следит за деятельностью Apache, должно быть слышали об Apache Axis. Axis это инструментарий SOAP следующего поколения, разрабатываемый также под эгидой Apache XML. За SOAP (спецификацией, а не специфической реализацией), стремительно и радикально развивающейся в последнее время, следить весьма трудно. Попытаться создать версию SOAP, полностью соответствующую текущим требованиям, изменяющимся в ходе развития, также довольно сложно. В результате, текущая версия Apache SOAP предлагает некое решение, ограниченное его конструкцией. Решив что не стоит пытаться полностью перепроектировать существующий инструмент, разработчики Apache приступили к созданию проекта на базе нового кода. Так родился Axis. Название SOAP также претерпело изменение, сначала из SOAP превратившись в XP, а затем в XMLP. Затем из имени нового SOAP было исключено название спецификации и родилось название "Axis". Но сейчас похоже что W3C вновь возвращается к названию спецификации SOAP (версии 1.2 или 2.0), так что все еще может поменяться и путаницы будет еще больше!

Думайте об IBM SOAP4J как об архитектуре ?1 инструментария SOAP. А об Apache SOAP (рассматриваемой в этой статье), как об архитектуре ?2. А Axis представляет собой архитектуру ?3, архитектуру нового поколения. Этот проект использует SAX, тогда как Apache SOAP базируется на DOM. Кроме того, Axis, в отличие от Apache SOAP, предоставляет более дружественный подход при взаимодействии с пользователем. После перечисления этих достоинств вы наверное придете в недоумение, почему я не выбрал в качестве предмета изучения Axis. Просто это было бы несколько преждевременно. В настоящее время готовится к выходу лишь версия 0.51 Axis. Это еще не бета, и даже не альфа версия. Я с удовольствием мог бы расписывать новые возможности Axis, но у вас не было бы никаких шансов убедить ваше руководство в возможности использования программного обеспечения с открытым источником версии ниже альфы для нужд вашей сверх важной системы. Поэтому я решил сфокусироваться на чем-то, что вы реально можете использовать уже сегодня - Apache SOAP. Я думаю что к моменту выхода финальной версии Apache Axis, я обновлю этот материал в следующем издании своей книги. А до тех пор давайте сосредоточимся на решении, которое уже доступно.

Установка

Возможны две формы установки SOAP. Первая - это запуск клиента SOAP, используя SOAP API для связи с сервером, который может принимать сообщения SOAP. Второй способ - запуск сервера SOAP, который может получать сообщения от клиента SOAP. В этом разделе я описал обе процедуры.

Клиент

Для использования клиента SOAP вам необходимо сначала скачать Apache SOAP, доступный на http://xml.apache.org/dist/soap. Я скачал версию 2.2 в бинарном формате (из подкаталога version-2.2). Затем вы должны разархивировать содержимое архива в директорию на вашем компьютере. В моем случае это была директория javaxml2 (c:\javaxml2 на моем компьютере с Windows, /javaxml2 на моем компьютере с Mac OS X). В результате файлы разархивировались в /javaxml2/soap-2_2. Вам также потребуется скачать пакет JavaMail, доступный на сервере Sun http://java.sun.com/products/javamail/. Он потребуется для поддержки протокола передачи SMTP, используемого в Apache SOAP. Затем скачайте Java Beans Activation Framework (JAF), также доступный на сервере Sun http://java.sun.com/products/beans/glasgow/jaf.html. Исходя из предположения что у вас уже установлен и готов к использованию Xerces или другой XML парсер.

Примечание: Убедитесь в том, что ваш XML парсер JAXP-совместимый и корректно использует пространство имен. Скорее всего ваш парсер удовлетворяет этим требованиям.Если у вас возникли проблемы, лучше вернуться к использованию Xerces.

Примечание: Используйте последние версии Xerces. Версия 1.4 и выше подойдет. Существует ряд ошибок при работе с SOAP и Xerces 1.3(.1), поэтому я советую не использовать такое сочетание.

Разархивируйте JavaMail и JAF пакеты, а затем включите их jar файлы в ваш classpath, а также библиотеку soap.jar. Каждый из этих jar файлов должен находиться либо в корневом каталоге соответствующей программы, или в подкаталоге /lib. По завершении ваша переменная classpath должна выглядеть примерно следующим образом:

$ echo $CLASSPATH
/javaxml2/soap-2_2/lib/soap.jar:/javaxml2/lib/xerces.jar:
/javaxml2/javamail-1.2/mail.jar:/javaxml2/jaf-1.0.1/activation.jar

Для Windows она будет выглядеть так:

c:\>echo %CLASSPATH%
c:\javaxml2\soap-2_2\lib\soap.jar;c:\javaxml2\lib\xerces.jar;
c:\javaxml2\javamail-1.2\mail.jar;c:\javaxml2\jaf-1.0.1\activation.jar

И, наконец, добавьте директорию javaxml2/soap-2_2/ в ваш classpath для запуска примеров SOAP. Я описал настройку для нескольких примеров, приведенных в этой главе.

Сервер

Чтобы создать SOAP-совместимый набор серверных компонент вам для начала потребуется движок сервлета. Как и в предыдущих главах, в качестве примера для этой главы я использовал Apache Tomcat (доступный на http://jakarta.apache.org/). Вам потребуется добавить все необходимое клиенту в classpath сервера. Самый простой способ сделать это - сбросить soap.jar, activation.jar и mail.jar, а также ваш парсер, в каталог библиотек вашего движка сервлетов. Для Tomcat это каталог /lib, в котором содержатся библиотеки для автоматической загрузки. Если вы хотите обеспечить поддержку скриптов (которые не обсуждаются в этой главе, но есть в примерах Apache SOAP), вам нужно поместить bsf.jar (доступный на http://oss.software.ibm.com/developerworks/projects/bsf) и js.jar (доступный на http://www.mozilla.org/rhino/) в тот же каталог.

Примечание: Если вы используете Xerces c Tomcat, вам нужно будет повторить фокус, который я описывал в главе 10. Переименуйте parser.jar в z_parser.jar, а jaxp.jar в z_jaxp.jar, чтобы убедиться в том, что xerces.jar и включенная версия JAXP загружается перед каким-либо другим парсером или реализацией JAXP.

Затем перезагрузите ваш движок сервлетов, после чего вы будете готовы к написанию серверных компонент SOAP. 

Сервлет маршрутизатора и клиент администратора

Помимо основных операций, Apache SOAP включает сервлет маршрутизатора, а также клиент администратора. Даже если вы не собираетесь их использовать, я рекомендую вам установить их, чтобы протестировать правильность установки SOAP. Этот процесс зависит от того, какой движок сервлетов вы используете, поэтому я ограничусь описанием процесса установки для Tomcat. Инструкции по установке для некоторых других движков сервлетов вы найдете на http://xml.apache.org/soap/docs/index.html.

Установка под Tomcat очень проста: просто возьмите файл soap.war из директории soap-2_2/webapps и сбросьте его в директорию $TOMCAT_HOME/webapps - и все! Чтобы проверить установку укажите в броузере адрес http://localhost:8080/soap/servlet/rpcrouter. Вы должны получить ответ, наподобие указанного на рис.12-2

Рисунок 12-2. Сервлет RPC маршрутизатора

Несмотря на то что сообщение похоже на сообщение об ошибке, оно свидетельствует о том что все работает правильно. Вы должны получить такой же отклик, указав в броузере адрес клиента администратора: http://localhost:8080/soap/servlet/messagerouter.

В завершение тестирования сервера и клиента, убедитесь в том, что вы полностью следовали всем инструкциям. Затем запустите следующий класс Java, как это показано ниже, для поддержки URL вашего сервлета для сервлета маршрутизатора RPC:

C:\>java org.apache.soap.server.ServiceManagerClient
         http://localhost:8080/soap/servlet/rpcrouter list
Deployed Services:

Вы должны получить пустой список сервисов, как это показано выше. Если вы получите какие-либо сообщения, ознакомьтесь с длинным перечнем возможных ошибок, доступным на http://xml.apache.org/soap/docs/trouble/index.html. Это наиболее полный перечень проблем, с которыми вы могли столкнуться. Если вы получили пустой список, это значит что настройка завершена и вы готовы начать рассмотрение примеров, приведенных в этой главе.

Приступим

Существует три основных этапа в написании любых систем на базе SOAP. Перечислив, я коротко остановлюсь на каждой из них:

  • Выбор между SOAP-RPC и SOAP сообщениями;
  • Написание или получение доступа к SOAP сервису;
  • Написание или получение доступа к SOAP клиенту.

Первым этапом является выбор, будете ли вы использовать SOAP для RPC вызовов (при которых удаленная процедура выполняется на сервере), или сообщения (когда клиент просто посылает фрагменты информации серверу). Я подробно рассматриваю эти процессы далее. После того как вы приняли это решение, вам потребуется получить доступ или создать собственный сервис. Разумеется, поскольку все мы - профессионалы в Java, в этой главе рассказывается о том как создать свой собственный. И, наконец, вам нужно написать клиента для этого сервиса, вот и все дела!

RPC или Messaging?

Ваша первая задача не имеет отношения к программированию и носит скорее дизайнерский характер. Вам нужно выбрать, будете ли вы использовать сервис RPC или сообщения. Будем считать что с RPC вы близко познакомились (например, прочитав одну из глав моей книги). Клиент выполняет удаленную процедуру на сервере, а затем получает отклик. При таком сценарии, SOAP выступает в роли XML-RPC системы с расширенными возможностями, обеспечивающей лучшую обработку ошибок и передачу сложных типов данных по сети. Вы уже знакомы с этой концепцией, и, поскольку в SOAP проще писать именно RPC системы, я начну с них. В этой статье описывается создание RPC сервиса, RPC клиента, и запуск системы в действие.

Другой стиль работы SOAP основан на обмене сообщениями. Вместо выполнения удаленных процедур, он используется только для обмена информацией. Как вы можете догадаться, это мощный инструмент, не требующий знания клиентом отдельных методов какого-либо сервера. Он также делает моделирование удаленных систем более изолированным, позволяя использовать пакеты данных (пакеты в фигуральном смысле, а не в сетевом) для передачи другим системам. При этом другим системам не обязательно знать об операциях, которые совершались с этими данными. Такой стиль сложнее чем RPC программирование, поэтому я не буду риводить его здесь. Вы найдете его в моей книге, наряду с другими деталями бизнес-бизнес взаимодействия. А для начала познакомьтесь с SOAP-RPC программированием.

Как и большинство дизайнерских проблем, принятие этого решения зависит только от вас. Проанализируйте ваше приложение и постарайтесь определить для чего вам нужно использовать SOAP. Если у вас есть сервер и набор клиентов, которые выполняют специфические бизнес функции по запросу, то вам более подходит RPC. В сложных системах, в которых обмен данными - нечто более чем просто выполнение специфических бизнес функций по запросу, гораздо предпочтительней использование сообщений SOAP.

RPC сервис

Теперь, когда с формальностями покончено, настало время действовать. Как вы знаете, в RPC вам потребуются классы, методы которых будут выполняться удаленно.

Фрагменты кода

Я начну с рассмотрения фрагментов кода для сервера. Эти фрагменты представляют собой классы с методами, выполняемыми для RPC клиентов [2]. В качестве примеров я использовал код из своей книги. Вместо того, чтобы использовать простые классы, я выбрал более сложный пример чтобы как можно наглядней продемонстрировать возможности SOAP. Итак, в качестве примера я использовал класс CD. Сначала определяем элемент map для каждого нестандартного типа параметров. Для атрибута encodingStyle, по крайней мере в Apache SOAP 2.2. вы должны указать значение http://schemas.xmlsoap.org/soap/encoding/. На данный момент это единственная поддерживаемая кодировка. Вам нужно указать пространство имен для типа, определяемого пользователем, а затем имя класса с префиксом пространства имен для этого типа. В нашем случае для этих целей я использовал выдуманное пространство имен и простой префикс "x". Затем, используя атрибут javaType, задал реальное имя Java класса (для этого случая - javaxml2.CD). И, наконец, куралесил с атрибутами java2XMLClassName и xml2JavaClassName. С их помощью задается класс, конвертируемый из Java в XML и наоборот. Я использовал потрясающе удобный класс BeanSerializer, также входящий в комплект поставки Apache SOAP. Если ваш пользовательский параметр в формате JavaBean, этот сериализатор и десериализатор избавит вас от необходимости писать свой собственный. Вам нужен класс с конструктором по умолчанию (вспомните, для класса CD я задал простой, без каких-либо параметров, конструктор), и публикация всех данных этого класса при помощи методов setXXX и getXXX. Поскольку класс CD прекрасно удовлетворяет всем этим требованиям, BeanSerializer работает идеально.

Примечание: То, что класс CD соответствует требованиям BeanSerializer.  не имеет большого значения. Большинство классов легко приводятся к этому формату. Поэтому я советую избегать написания собственных сериализаторов и десериализаторов. Это лишняя головная боль (ничего сложного, но слишком кропотливо) и рекомендую вам сэкономить силы и использовать в своих пользовательских параметрах конвертацию бинов. Во многих случаях преобразования бинов требуют лишь наличия в вашем классе конструктора по умолчанию (без параметров).

Теперь повторно воссоздадим jar файл и переразместим наш сервис:

(gandalf)/javaxml2/Ch12$ java org.apache.soap.server.ServiceManagerClient
    http://localhost:8080/soap/servlet/rpcrouter xml/CDCatalogDD.xml

Внимание: Если вы оставите запущенным свой движок сервлета и в это же время переразмещаете сервис, вам потребуется перезапустить движок сервлета, чтобы активировать новые классы для сервиса SOAP и переразместить сервис.

Теперь осталось модифицировать клиент для использования новых классов и методов. Пример 12-10 содержит модифицированную версию клиентского класса CDAdder. Изменения, внесенные в предыдущую версию, выделены.

Пример 12-10: Обновленный класс CDAdder

package javaxml2;
 
import java.net.URL;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.util.xml.QName;
 
public class CDAdder {
 
    public void add(URL url, String title, String artist, String label)
        throws SOAPException {
 
        System.out.println("Добавление CD с названием '" + title + "' исполнителя '" +
            artist + "' студии " + label);
 
        CD cd = new CD(title, artist, label);
 
        // Отображение этого типа, чтобы его можно было использовать с SOAP
        SOAPMappingRegistry registry = new SOAPMappingRegistry(  );
        BeanSerializer serializer = new BeanSerializer(  );
        registry.mapTypes(Constants.NS_URI_SOAP_ENC,
            new QName("urn:cd-catalog-demo", "cd"),
            CD.class, serializer, serializer);
 
        // Создание объекта вызова Call
        Call call = new Call(  );
        call.setSOAPMappingRegistry(registry);
        call.setTargetObjectURI("urn:cd-catalog");
        call.setMethodName("addCD");
        call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
 
        // Установка параметров
        Vector params = new Vector(  );
        params.addElement(new Parameter("cd", CD.class, cd, null));
        call.setParams(params);
 
        // Обработка Invoke вызова
        Response response;
        response = call.invoke(url, "");
 
        if (!response.generatedFault(  )) {
            System.out.println("Добавление CD успешно завершено.");
        } else {
            Fault fault = response.getFault(  );
            System.out.println(Ошибка: " + fault.getFaultString(  ));
        }
    }
 
    public static void main(String[] args) {
        if (args.length != 4) {
            System.out.println("Шаблон: java javaxml2.CDAdder [URL SOAP сервера] " +
                "\"[Заголовок CD]\" \"[Имя исполнителя]\" \"[Студия CD]\"");
            return;
        }
 
        try {
            // URL SOAP сервера, к которому осуществляется подключение
            URL url = new URL(args[0]);
 
            // Get values for new CD
            String title = args[1];
            String artist = args[2];
            String label = args[3];
 
            // Add the CD
            CDAdder adder = new CDAdder(  );
            adder.add(url, title, artist, label);
        } catch (Exception e) {
            e.printStackTrace(  );
        }
    }
}

Единственное по-настоящему интересное изменение связано с отображением класса CD:

        // Отображение этого типа, чтобы его можно было использовать с SOAP
        SOAPMappingRegistry registry = new SOAPMappingRegistry(  );
        BeanSerializer serializer = new BeanSerializer(  );
        registry.mapTypes(Constants.NS_URI_SOAP_ENC,
            new QName("urn:cd-catalog-demo", "cd"),
            CD.class, serializer, serializer);

Вот каким образом пользовательский параметр может быть кодирован и передан по сети. Я уже рассказывал каким образом класс BeanSerializer может быть использован для обработки параметров в формате JavaBean, например таких как класс CD. Для их указания серверу я использовал дискриптор размещения, несмотря на это теперь мне нужно сообщить клиенту, что нужно использовать этот сериализатор и десериализатор. Эту функцию и выполняет класс SOAPMappingRegistry. Метод mapTypes() берет зашифрованную строку (и вновь для этого лучше использовать константу NS_URI_SOAP_ENC), и информацию о типе параметра, для которого должна использоваться специальная сериализация. Сначала указывается QName. Вот почему в дискрипторе размещения было использовано странное пространство имен. Вам нужно указать здесь тот же URN, а также локальное имя элемента (для этого примера "CD"), затем Java объект Class класса, который будет сериализован (CD.class) и, наконец, экземпляр класса для сериализации и десериализации. Для этого примера в обоих случаях будет фигурировать экземпляр BeanSerializer. После того как все эти настройки будут внесены в реестр, сообщите об этом объекту Call при помощи метода setSOAPMapping-Registry().

Вы можете запустить этот класс, как было показано раньше, добавив CD, и все должно работать как положено:

C:\javaxml2\build>java javaxml2.CDAdder
    http://localhost:8080/soap/servlet/rpcrouter
    "Tony Rice" "Manzanita" "Sugar Hill"
Добавление CD с заголовком 'Tony Rice' исполнителя 'Manzanita' студии Sugar Hill
Успешное добавление CD.

Я оставил модификацию класса CDLister для вас. Все производится по тому же шаблону. Чтобы проверить себя можете обратиться к файлам с примерами к моей книге, которые уже содержат эти обновленные классы.

Примечание: Вы можете решить что, поскольку класс CDLister напрямую не взаимодействует с объектом CD (возвращаемое методом list() значение имеет тип Hashtable), то вам не нужно вносить каких-либо изменений. Тем не менее, возвращаемый класс Hashtable содержит экземпляры объекта CD. Если SOAP не знает как десериализовать их, клиент выдаст сообщение об ошибке. В этом случае для решения проблемы вы должны указать в объекте Call экземпляр SOAPMappingRegistry.

Эффективная обработка ошибок

Теперь, когда вы познакомились с пользовательскими объектами, сделали RPC вызовы и прочее, позвольте мне рассказать о менее увлекательной теме: обработке ошибок. При любой сетевой транзакции может произойти множество сбоев. Не запускается сервис, ошибка в работе сервера, не удается найти объект, отсутствуют классы и множество прочих проблем. До сих пор я просто использовал метод fault.getString() для генерации сообщений об ошибках. Но этот метод не всегда может оказаться полезным. Чтобы увидеть его в действии, снимите комментарий в конструкторе CDCatalog:

public CDCatalog(  ) {
        //catalog = new Hashtable(  );
 
        // Создание каталога
        addCD(new CD("Nickel Creek", "Nickel Creek", "Sugar Hill"));
        addCD(new CD("Let it Fall", "Sean Watkins", "Sugar Hill"));
        addCD(new CD("Aerial Boundaries", "Michael Hedges", "Windham Hill"));
        addCD(new CD("Taproot", "Michael Hedges", "Windham Hill"));
    }

Перекомпилируйте его, перезапустите движок сервлета и переразместите его. В результате этого возникнет исключительная ситуация NullPointerException при попытке конструктора класса добавить CD в неинициализированный Hashtable. При запуске клиента появится сообщение об ошибке, но оно будет не очень информативным:

(gandalf)/javaxml2/build$ java javaxml2.CDLister
    http://localhost:8080/soap/servlet/rpcrouter
Просмотр текущего каталога CD.
Ошибка: Не удается разрешить целевой объект(Unable to resolve target object): null

Это совсем не та информация, которая может помочь при обнаружении и исправлении ошибки. Тем не менее фреймуорк исправно справляется с обработкой ошибок. Вы помните DOMFaultListener, который вы задавали как значение элемента faultListener? Настал его час вступить в игру. Возвращаемый в случае ошибки объект Fault содержит DOM (объектную модель документа) org.w3c.dom.Element с детальной информацией об ошибке. Сначала добавьте в исходный код выражение для импортирования java.util.Iterator:

import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.util.xml.QName;

Теперь внесем изменения для обработки ошибок в методе list():

        if (!response.generatedFault(  )) {
            Parameter returnValue = response.getReturnValue(  );
            Hashtable catalog = (Hashtable)returnValue.getValue(  );
            Enumeration e = catalog.keys(  );
            while (e.hasMoreElements(  )) {
                String title = (String)e.nextElement(  );
                CD cd = (CD)catalog.get(title);
                System.out.println("  '" + cd.getTitle(  ) + "' исполнителя " +
cd.getArtist( ) +
                    " студии " + cd.getLabel(  ));
            }
        } else {
            Fault fault = response.getFault(  );
            System.out.println("Ошибка: " + fault.getFaultString(  ));
 
            Vector entries = fault.getDetailEntries(  );
            for (Iterator i = entries.iterator(); i.hasNext(  ); ) {
                org.w3c.dom.Element entry = (org.w3c.dom.Element)i.next(  );
                System.out.println(entry.getFirstChild().getNodeValue(  ));
            }
        }

Используя метод getDetailEntries() вы получаете доступ к поддерживаемым сервисом SOAP и сервером с необработанным данным, с информацией о проблеме. Код повторно обрабатывает их (как правило существует только один элемент, но он требует пристального внимания) и перехватывает DOM Element, содержащийся в каждой записи. По сути дела, вот XML с которым вы работаете:

<SOAP-ENV:Fault>
  <faultcode>SOAP-ENV:Server.BadTargetObjectURI</faultcode>
  <faultstring>Не удается разрешить целевой объект: null</faultstring>
  <stacktrace>Вот то, чего мы хотим!</stackTrace>
</SOAP-ENV:Fault>

Иными словами, объект Fault предоставляет вам доступ к части конверта SOAP, которая содержит ошибки. Кроме того, Apache SOAP обеспечивает трассировку стека Java в случае возникновения ошибок, предоставляющую детализованную информацию, необходимую для их исправления. Перехватив элемент stackTrace и распечатав значение узла Text из этого элемента ваш клиент может распечатать трассировку стека сервера. Скомпилировав эти изменения и перезапустив клиент вы получите следующий результат:

C:\javaxml2\build>java javaxml2.CDLister http://localhost:8080/soap/servlet/rpcr
outer
Просмотр текущего каталога CD.
Ошибка: Не удается разрешить целевой объект: null
java.lang.NullPointerException
        в javaxml2.CDCatalog.addCD(CDCatalog.java:24)
        в javaxml2.CDCatalog.<init>(CDCatalog.java:14)
        в java.lang.Class.newInstance0(Native Method)
        в java.lang.Class.newInstance(Class.java:237)

Это немногим лучше, но по крайней мере вы можете видеть лакомную информацию о том, что возникла исключительная ситуация NullPointerException и даже узнать номера строк в классах сервера, в которых возникла эта проблема. Результат этих последних изменений позволил вам получить наглядное представление о проблеме обработки ошибок. Теперь вы должны проверить на наличие ошибок классы вашего сервера. Да, чуть не забыл, перед этим не забудьте обратно изменить ваш класс CDCatalog, чтобы избавиться от намеренно внесенных нами для наглядности ошибок!


  1. Ведется множество разговоров относительно запуска SOAP через другие протоколы, например SMTP (или даже Jabber). Пока стандарт SOAP не предусматривает этого, но подобные возможности могут быть добавлены в будущем. Поэтому не удивляйтесь если встретитесь с активными обсуждениями этой темы.


    Языки программирования: разное