Россия, Омск |
Улучшение работы приложения фирмы ITSO Electronics с помощью Web-сервисов
3.8 Импортирование документа WSDL
Если вы импортируете документ WSDL, описательный класс генерируется на основе содержания документа WSDL. Общедоступные прототипы методов, функций и подстановок в классе соответствуют операциям Web-сервиса, определенным для первого элемента <port> первого элемента <service> в документе WSDL. Другие классы или типы ссылаются на прототипы методов, которые, возможно, также будут сгенерированы на основании содержимого документа WSDL вместе с общедоступными элементами данных. Если вы измените прототип интерфейса, соответствующий документ WSDL также изменится, когда Web-сервис будет сохранен. Вы можете отслеживать подобные изменения с помощью свойства "Warn if the WSDL interface is modified" (Предупреждать, если интерфейс WSDL был изменен).
Бизнес-партнеры компании ITSO Electronics, а, возможно, и покупатели, в свою очередь, могут обеспечивать компанию документом WSDL, который она может использовать для создания Web-сервисов. Вы можете импортировать существующий документ WSDL, чтобы сгенерировать каркас Web-сервиса. Каркасный код соответствует описанию Web-сервиса. Затем вам необходимо добавить код реализации.
Для того чтобы импортировать документ WSDL, выполните следующее:
- Откройте базу данных ITSO Web Services в Domino Designer, затем выберите Shared Code => Web Services.
- Щелкните по кнопке Import WSDL и выберите файл WSDL, который хотите импортировать. В нашем сценарии мы импортировали файл, который экспортировали в предыдущем разделе (см. рис. 3.14).
- В результате импортирования документа WSDL будет сгенерирован каркасный код, который в нашем сценарии сгенерирован на языке LotusScript, как показано в примере 3.11. Затем мы открыли окно свойств Web Service, поставили галочку рядом с опцией Warn if the WSDL interface is modified (
рис.
3.15).
%INCLUDE "lsxsd.lss" Class ArrayOfProduct_Holder As INOUT_HOLDER Public Value() As Product End Class Class Product Public description As XSD_STRING Public name As XSD_STRING Public number As Long Public price As Double Sub NEW End Sub End Class Class ProductService Sub NEW End Sub Function getAllProducts() As ArrayOfProduct_Holder End Function Function getProduct(productNumber As Double) As Product End Function End Class
Пример 3.11. Каркасный код, сгенерированный после импортирования документа WSDL
3.9 Работа с исключениями и ошибками в Web-сервисах
Операции также могут возвращать ошибки посредством подкласса ошибок, который соответствует некоторому сообщению <wsdl:fault>, выбранному для определенной операции в документе WSDL, или посредством прямого использования базового класса WS_FAULT (LotusScript), или класса lotus.domino.types.Fault, или класса java.lang.Exception(Java).
Ошибки для метода описания сервиса на языке Java возникают в стандартных предложениях Java.
Для получения дополнительной информации о том, как работать с ошибками, используя язык LotusScript см. "Улучшение работы приложения фирмы ITSO Electronics с помощью Web-сервисов" , "Использование Web-сервисов с помощью LS2J".
3.10 Безопасность в Web-сервисах
Угрозы безопасности Web-сервисов, определенных в приложении ITSO Electronics, подразумевают угрозу всей системе, на которой базируется Web-сервис, приложению и инфраструктуре сети в целом. Для обеспечения защиты Web-сервисов необходим целый спектр механизмов защиты, основанных на языке XML, так как только благодаря им становится возможным решение проблем, касающихся аутентификации, ролевого контроля доступа к базам данных, реализации принципов обеспечения безопасности, а также безопасности уровня сообщений, которая выполняет роль посредника.
В настоящий момент не существует общепринятых спецификаций безопасности Web-сервисов. Поэтому разработчики могут либо создавать службы, которые не используют подобных возможностей, либо разрабатывать специальные узкопрофильные программы, которые предположительно смогут решать проблемы функциональной совместимости.
Для применения Web-сервисов могут потребоваться механизмы защиты "точка-точка" или сквозные механизмы защиты, или оба механизма в зависимости от степени угрозы или риска. Традиционные механизмы безопасности, построенные на основе соединений "точка-точка", возможно, не смогут справиться с потребностями в сквозной защите Web-служб. Тем не менее, безопасность – это баланс между оцененным риском и затратами на противодействие. В зависимости от допускаемого риска использования защита, обеспечиваемая соединением "точка-точка", работающая на уровне переноса данных, позволяет обеспечить достаточными мерами безопасности.
В архитектуре приложения ITSO Electronics существует три фундаментальных принципа, относящихся к безопасности: ресурсы, которые должны быть защищены; механизмы, благодаря которым обеспечивается эта защита (т. е. политика защиты); и политики, которые представляют собой обрабатываемые компьютерами документы, которые, описывают ограничения на этих ресурсах. Для продолжения дискуссии о безопасности применения Web-сервисов обратитесь к руководству Web Service Architecture, которое доступно по следующему адресу в Интернете: http://www.w3.org/TR/2004/NOTE-ws-arch-20040211
Web-сервис Lotus Domino 7 обладает теми же возможностями защиты, что и агент. Уровень защиты Web-сервиса устанавливается в закладке Security (Безопасность) окна свойств Web service. В приведенном ниже списке описываются параметры, доступные в закладке Security.
- Run as web user (Запускать от лица Web-пользователя): код Java или LotusScript запускается с реальным именем пользователя, который запустил Web-сервис.
- Run on behalf of (Запускать от лица): код Java или LotusScript запускается на полномочиях определенного пользователя. Тем не менее, подписчик Web-сервиса должен обладать неограниченными правами на сервер или правом запускать Web-сервисы от лица любого пользователя.
-
Set runtime security level (Установить уровень защиты выполнения) (один из перечисленных ниже):
- Do not allow restricted operations (Не разрешать выполнение операций с ограниченным доступом);
- Allow restricted operations (Разрешать выполнение операций с ограниченным доступом);
- Allow restricted operations with full administration rights (Разрешать выполнение операций с ограниченным доступом с полными правами администрирования).
-
Default access for this web service (Доступ по умолчанию к этому Web-сервису):
- All readers and above (Все читатели и выше). Вы можете выбрать или снять этот параметр.
- Enumerated (Перечисленные). Если вы убрали галочку рядом с параметром All readers and above, то с помощью этого параметра вы можете выбрать тех, кому вы хотите предоставить доступ по умолчанию.
- Allow Public Access users to use this web service (Разрешить пользователям открытого доступа применять этот Web-сервис). Если выбран этот параметр, пользователи, обладающие открытым доступом к документам в базе данных, получают доступ по умолчанию.
3.11 Использование Web-сервисов с помощью Java
На момент написания этой книги Lotus Domino 7.0 обеспечивал, хотя и не в полном объеме, использование Web-сервисов. Тем не менее, вспомогательным бизнес-требованием, предъявляемым к приложению ITSO Electronics, является предоставление доступа к ценовым данным, располагающимся за границами Lotus Domino, что, в свою очередь, диктует необходимость использования Web-сервисов в этом приложении. В нашем сценарии компания ITSO Electronics решила применить программный продукт с открытым исходным кодом Apache SOAP, чтобы использовать Web-сервисы с помощью Java. Apache SOAP представляет собой реализацию стандарта SOAP в W3C. Он заменяет собой разработку IBM SOAP4J. Последующий, улучшенный проект Apache Axis доступен по следующему адресу: http://ws.apache.org/axis/
Для того чтобы проиллюстрировать использование Web-сервисов, в нашем сценарии мы создали отдельную базу данных в приложении ITSO Electronics, которую назвали WS Consumer. В этой базе данных содержится агент, который будет использовать сложный Web-сервис, созданный нами ранее.
Приведенная ниже последовательность действий наглядно иллюстрирует создание агента, использующего Web-сервис.
- Загрузите самую последнюю версию Apache SOAP по следующей ссылке: http://ws.apache.org/soap/
- Извлеките загруженный файл во временную директорию, в нашем сценарии в качестве такой папки мы использовали папку c:\soap.
- Экспортируйте сложный Web-сервис ProductService (находящийся в базе данных Web Services) в документ WSDL. В экспортированном документе WSDL содержится вся информация, необходимая для использования Web-сервиса.
- Откройте документ WSDL в текстовом редакторе. Документ WSDL содержит информацию, описывающую сложный тип данных, возвращаемых этим Web-сервисом, как видно из примера 3.12. В нашем случае этот сложный тип данных называется Product. Сложный элемент Type показывает свойства объекта Product, к которым относятся описание, название, номер и цена.
<wsdl:types> <schema targetNamespace="urn:DefaultNamespace" xmlns="http://www.w3.org/2001/ XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"> <import namespace="http://schemas.xmlsoap.org/soap/encoding/"/> <complexType name="Product"> <sequence> <element name="description" nillable="true" type="xsd:string"/> <element name="name" nillable="true" type="xsd:string"/> <element name="number" type="xsd:int"/> <element name="price" type="xsd:double"/> </sequence> </complexType> <complexType name="ArrayOfProduct"> <complexContent> <restriction base="soapenc:Array"> <attribute ref="soapenc:arrayType" wsdl:arrayType="impl:Product[]"/> </restriction> </complexContent> </complexType> </schema> </wsdl:types>
Пример 3.12. Тип WSDL для Product - Откройте новую базу данных WS Consumer в Domino Designer, выберите Shared Code => Agents (Общий код Агенты), после чего щелкните по кнопке New agent (Создать агент).
- В поле Name окна свойств Agent введите ConsumeWS и в разделе Runtime в выпадающем меню Target (Цель) выберите None (Отсутствует), как показано на рис. 3.16.
- Закройте окно свойств Agent, затем выберите язык кода Java.
- Нам необходимо создать класс JavaBean, представляющий сложный тип данных Product, определенный в документе WSDL. Для облегчения этой операции мы воспользуемся возможностью импортирования WSDL для автоматической генерации класса JavaBean. После этого нам необходимо выбрать Shared Code => Web Service (Общий код Web-сервис) и щелкнуть по кнопке New Web Service (Создать Web-сервис).
- Закройте окно свойств Web Service и убедитесь в том, что язык описания Web-сервиса установлен на Java. Щелкните по кнопке Import WSDL (Импортировать WSDL), затем выберите созданный ранее документ WSDL. В результате импортирования этого документа будут автоматически созданы классы JavaBeansTM.
- Выделите и скопируйте код Java для Product.java.
- Вернитесь к агенту и щелкните по кнопке New Class (Создать класс). Затем замените класс Untitled.java скопированным кодом Product.java, так же, как это сделано в примере 3.13.
- Сохраните агент ConsumeWS и убедитесь в том, что он был успешно скомпилирован.
Щелкните по кнопке Edit Project (Изменить проект), откроется окно Java Agent Files (см. рис. 3.17). Перейдите к нужной директории библиотеки SOAP LIB, в нашем случае эта директория находилась по следующему пути: c:\soap\soap-2_3_1\lib. В качестве типа файла выберите Archive (Архивный), щелкните по кнопке Add/Replace File(s) (Добавить/Заменить файлы), далее нажмите ОК.
- Вам необходимо код JavaAgent.java по умолчанию заменить своим собственным, чтобы приступить к использованию Web-сервиса, который в нашем сценарии представлен в примере 3.14. Убедитесь в том, что в классе JavaAgent.java есть переменные URL и soapAction. Эти переменные должны указывать на расположение вашего приложения.
- Сохраните агент ConsumeWS.
- Вернитесь к новому Web-сервису и закройте его без сохранения.
- Откройте клиент Lotus Notes и выберите базу данных WS Consumer. Перейдите к Actions => ConsumeWS (Действия ConsumeWS) для того, чтобы запустить только что созданный агент. Агент отобразит набор объектов Product, используемых в Web-сервисе, в окне Java, как видно на рис. 3.18. Если окно не открылось, выберите File => Tools => Show Java Debug Console (Файл Сервис Показать консоль отладки Java) для получения дополнительной информации.
public class Product { private java.lang.String description; private java.lang.String name; private int number; private double price; public Product() { } public java.lang.String getDescription() { return description; } public void setDescription(java.lang.String description) this.description = description; } public java.lang.String getName() { return name; } public void setName(java.lang.String name) { this.name = name; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } private java.lang.Object __equalsCalc = null; public synchronized boolean equals(java.lang.Object obj) { if (!(obj instanceof Product)) return false; Product other = (Product) obj; if (obj == null) return false; if (this == obj) return true; if (__equalsCalc != null) { return (__equalsCalc == obj); } __equalsCalc = obj; boolean _equals; _equals = true && ((this.description==null && other.getDescription()==null) | (this.description!=null && this.description.equals(other.getDescription()))) && ((this.name==null && other.getName()==null) || (this.name!=null && this.name.equals(other.getName()))) && this.number == other.getNumber() && this.price == other.getPrice(); __equalsCalc = null; return _equals; } private boolean __hashCodeCalc = false; public synchronized int hashCode() { if (__hashCodeCalc) { return 0; } __hashCodeCalc = true; int _hashCode = 1; if (getDescription() != null) { _hashCode += getDescription().hashCode(); } if (getName() != null) { _hashCode += getName().hashCode(); } _hashCode += getNumber(); _hashCode += new Double(getPrice()).hashCode(); __hashCodeCalc = false; return _hashCode; } }Пример 3.13. Код класса Product
import lotus.domino.*; import java.io.*; import java.util.*; import java.net.*; import org.w3c.dom.*; import org.apache.soap.util.xml.*; import org.apache.soap.*; import org.apache.soap.encoding.*; import org.apache.soap.encoding.soapenc.*; import org.apache.soap.rpc.*; import javax.swing.*; public class JavaAgent extends AgentBase { public void NotesMain() { try { Session session = getSession(); AgentContext agentContext = session.getAgentContext(); URL url = new URL("http://domino7appdev.cam.itso.ibm.com/itso/webservices.nsf/ ProductService?WSDL"); String soapAction ="http://domino7appdev.cam.itso.ibm.com/itso/webservices.nsf/ ProductService?WSDL"; Call call = new Call(); BeanSerializer beanSerializer = new BeanSerializer(); SOAPMappingRegistry smRegistry = new SOAPMappingRegistry(); smRegistry.mapTypes(Constants.NS_URI_SOAP_ENC, new QName( "urn:DefaultNamespace", "Product"), Product.class, beanSerializer, beanSerializer); String targetNamespace = "urn:DefaultNamespace"; call.setSOAPMappingRegistry(smRegistry); call.setTargetObjectURI(targetNamespace); call.setMethodName("getAllProducts"); call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC); Response resp = null; try { resp = call.invoke(url, soapAction); } catch (SOAPException e) { e.printStackTrace(); System.err.println("SOAP Exception code: " + e.getFaultCode() + " message: " + e.ge return; } if (!resp.generatedFault()) { Parameter ret = resp.getReturnValue(); Product[] products = (Product[]) ret.getValue(); buildGUI(products); } else { Fault fault = resp.getFault(); System.err.println("SOAP Fault, " + fault.getFaultCode() + " " + fault. getFaultS } } catch (Exception e) { e.printStackTrace(); } } private void buildGUI(Product[] products) { StringBuffer sb = new StringBuffer(); String newLine = "<br>"; for(int i = 0; i < products.length; i++) { sb.append(newLine + newLine); sb.append(products[i].getName() + " (" + products[i].getNumber() + ")" + newLine); sb.append( " * " + products[i].getDescription()); sb.append(newLine + " * " + products[i].getPrice()); } JFrame frame = new JFrame("Web service content"); frame.getContentPane().add(new JScrollPane(new JLabel("<html>" + sb. toString() + "</html>"))); frame.pack(); frame.setVisible(true); } }Пример 3.14. Код, необходимый для использования Web-сервиса с помощью Java