Главная » Java, Web, XML » Создание Web-службы средствами JAX-RPC

0

Из предыдущего описания видно, что для создания Web-службы надо, прежде всего, написать удаленный интерфейс, называемый в спецификации JAX-RPC "Service Endpoint Interface". Мы будем называть его адресатом или SEI-интерфейсом Web-службы. В WSDL-описании Web-службы адресату соответствует элемент <portType>. Поэтому SEI-интерфейс вместе с его реализацией часто называется портом (port) Web-службы. Описанные в нем методы как раз и составляют Web-услуги создаваемой Web-службы. На этот интерфейс накладываются следующие условия:

?           он должен быть открытым (public);

?           он должен прямо или косвенно расширять интерфейс Remote;

?           он не должен содержать константы;

?           его методы должны выбрасывать исключение класса RemoteException;

?         другие исключения, выбрасываемые методами, могут быть только проверяемого (checked) типа;

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

SEI-интерфейс Web-службы может выглядеть примерно так, как показано в листинге 6.8.

Шистинг 6.8.Удаленный интерфейс Web-службы (порт)

package bookinfo;

import j ava.rmi.*;

public interface BooklnfoPort extends Remote{

Book getlnfо(String isbn) throws RemoteException; void setInfo(Book book) throws RemoteException;

}

К допустимым типам аргументов и возвращаемых значений относятся:

?         примитивные типы Java, кроме типа char, то есть типы boolean, byte, short, int, long, float, double;

?         соответствующие классы-оболочки Boolean, Byte, Short, Integer, Long, Float, Double;

?       классы String, Date, Calendar, Biglnteger, BigDecimal, Object;

?           массивы, элементы которых относятся к перечисленным выше типам;

?           классы-коллекции типасоИес^оп, Map, set;

?           классы Java, называемые в спецификации JAX-RPC "value types".

Классы, названные "value types" — это обычные классы Java, удовлетворяющие трем требованиям. Они не реализуют прямо или косвенно интерфейс Remote, имеют открытый (public) конструктор по умолчанию, все их открытые (public) поля могут быть только допустимых для JAX-RPC типов. Например, класс Book, использованный в листинге 6.8, может выглядеть так:

package bookinfo;

public class Book{

private String isbn; private String [ ] authors; private String title;

public

public String getlsbn () { return isbn; }

public void setlsbn(String isbn){ this. isbn = isbn; }

public String [ ] getAuthors (){ return authors; }

public void                                                                                                   = authors; }

public String getTitle()( return title; }

public void setTitle(String title){ this.title = title; }

}

Ограничения типов данных вызваны тем, что аргументы и возвращаемые значения сериализуются значениями типов языка XSD (см. главу Г). Например, ТИПЫ int И Integer сериализуются ТИПОМ xsd: int, ТИП Biglnteger сериализуется типом xsd: integer, типы Date и calendar сериализуются

ТИПОМ xsd: dateTime, массивы ТИПОВ byte[] И Byte [) — ТИПОМ xsd:base64Binary. Типы классов-коллекций требуют наличия специальных классов, преобразующих их в структуры и массивы языка XSD.

После создания SEI-интерфейса надо его реализовать классом, который и будет представлять Web-службу. Этот класс в спецификации JAX-RPC называется "Service Endpoint Class". Часто его называют служителем (servant).

Очень часто имя класса-служителя образуется из имени добавлением окончания

Класс-служитель должен удовлетворять следующим требованиям:

?         класс должен быть открытым (public), не абстрактным (abstract) и не окончательным (final);

?           у него должен быть открытый конструктор по умолчанию;

методы, реализующие интерфейс, не должны быть окончательны- MH(final) или статическими (static);

?           класс не должен реализовать метод finalize {).

В листинге 6.9 показано, как можно реализовать Web-службу Bookinfo средствами JAX-RPC.

ЬЛистинг6.9. Реализация Weh-службы: средствами JAX-RPC

package bookinfo;

import javax.xml.rpc.server.*; import j avax.servlet.ServletContext;

import import

public class Booklnfolmpl

implements BooklnfoPort, ServiceLifecycle{

private ArrayList books; private Connection con;

public void

ServletContext sc =

((ServletEndpointContext)context).getServletContext();

String

try{

con =

}catch(Exception e){}

}

public void destroy () { setToDB() ;

con = null;

}

public Booklnfolmpl () { try{

Statement st = con.createStatement ();

ResultSet rs = st.executeQuery("SELECT * FROM book");

insertFromDB(rs);

rs.close () ; st.close();

}catch(Exception e) { }

}

public Book getlnfо (String isbn) { Book t = null;

for (int к = 0; к < books.size О; к++){

t = (Book)books.get(k);

if (isbn.equals(t.getlsbn())) break;

}

return t;

}

public void setlnfo(Book book){ books.add(book);

}

private void insertFromDB(ResultSet rs){

II Заполнение списка книг books выборкой из базы

}

private void setToDB(){

// Сохранение списка книг books в базе

}

}

Обратите внимание на то, что класс-служитель — это не сервлет. Когда Web-служба будет устанавливаться в Web-контейнер, утилита установки автоматически создаст сервлет, который будет обращаться к методам класса- служителя. Обычно этот сервлет называется JAXRPCServiet, он входит в каждую реализацию пакета JAX-RPC.

Жизненный цикл Web-службы

В листинге 6.9 реализуется еще один интерфейс ServiceLifecycle, описывающий жизненный цикл Web-службы. Описание производится двумя методами:

public void init(Object context); public void destroy();

выполняющимися автоматически                                       под управлением ис

полняющей системы JAX-RPC по одному разу при создании экземпляра Web-службы и при завершении его работы соответственно.

Исполняющая система создает и передает в метод init () объект context, содержащий сведения о Web-контейнере, в котором работает Web-служба. Тип этого объекта указан просто как object, но им чаще всего бывает контекст сервлета типа           или специально разработанный тип, описанный интерфейсом ServletEndpointContext.

Контекст Web-службы

Интерфейс ServletEndpointContext описывает четыре метода, предоставляющие сведения об окружении, в котором работает Web-служба.

? public HttpSession getHttpSession() — передает сведения о НТТР- сеансе сервлета, под управлением которого работает Web-служба или null, если сервлет не участвует в сеансе;

public                                                                 () — передает сведения О

полученном сообщении;

• public ServletContext getServletContext () — Передает KOHTeKCT серв- летов Web-приложения, в котором работает Web-служба;

public Principal                                    () — передает имя пользователя,

обратившегося к Web-службе.

Среди этих сведений особенно интересен интерфейс                                даже

Не ОН, а его расширение SOAPMessageContext.

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

public void setProperty (String name, Object value); public Iterator getPropertyNames (); public Object getProperty (String name); public void rernoveProperty (String name); public boolean containsProperty (String name);

Интерфейс SOAPMessageContext добавляет к этим методам еще три метода, которые позволяют получить и изменить присланное SOAP-послание:

public SCAPMbssage getMessage ();

public void setMessage(SOAPMessage message);

public String [] getRoles();

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

Предварительная обработка послания

Средствами JAX-RPC можно сделать предварительную обработку SOAP- послания: раскодировать или закодировать сообщение, проверить подлинность сообщения, подтвердить права клиента. Очень часто предварительно обрабатываются блоки заголовка SOAP-послания, предназначенные для обработки промежуточными серверами, но не прошедшие ее.

Основу обработки задает интерфейс Handler. Он описывает методы, позволяющие обработать полученное SOAP-послание, отправляемое SOAP- послание и сообщение об ошибке:

public boolean handleRequest(MessageContext context); public boolean handleResponse(MessageContext context); public boolean handleFault(MessageContext context);

Хотя типом аргумента этих методов назван интерфейс MessageContext, на самом деле это тип SOAPMessageContext. Это дает возможность получить для обработки само SOAP-послание.

Кроме того, в интерфейсе Handler есть метод public QName[] getHeaders();

позволяющий получить блоки заголовка SOAP-послания. Еще два метода

public void init(Handlerlnfo config); public void destroy();

выполняются при создании объекта типа Handle и его удалении соответственно. В метод () передается ссылка на объект класса содержащий сведения, необходимые контейнеру для инициализации класса- обработчика, а также сведения о цепочке классов-обработчиков SOAP- послания.

Дело в том, что для обработки SOAP-послания сразу создается целая цепочка классов-обработчиков, даже если в нее входит только один обработчик. Цепочка обработчиков — это объект типа HandlerChain. Интерфейс расширяет интерфейс List, значит, описывает упорядоченный список объектов типа Handler. К многочисленным методам интерфейса List и его предка — интерфейса Collection, позволяющим работать со списком — добавлять, удалять и перебирать элементы списка, — интерфейс добавляет следующие методы:

public boolean handleRequest (MessageContext context); public boolean handleResponse (MessageContext context); public boolean        context);

public void

public void destroy(); public

public void setRoles(string[] actorNames);

Как видите, первые пять методов похожи на методы интерфейса Handler. Они включены в интерфейс HandierChain для того чтобы его объекты можно было включить в другую цепочку, получив тем самым вложенную цепочку классов-обработчиков. Последние два метода позволяют цепочке играть роль "next" одного или нескольких промежуточных серверов (actors), обрабатывающих блоки заголовка SOAP-послания (см. главу 3).

Методы handieXxx () сделаны логическими именно потому, что они участвуют в цепочке — если метод handleRequest () вернул логическое значение true, то обработка пришедшего SOAP-послания передается следующему в цепочке методу handleRequest о, или адресату Web-службы, если обработчик был последним в цепочке. Если метод handleRequest () вернул значение false, то следующие обработчики не вызываются, а послание передается методу       () того же экземпляра класса-обработчика и

дальше идет по цепочке методов handieResponse (). Если метод handleRequest () выбросил исключение класса SOAPFaultException, то управление передается методу                                        () того же экземпляра класса-

обработчика. Впрочем, разработчик может изменить эту стандартную последовательность действий по своему усмотрению.

Для удобства реализации интерфейса Handler, в пакете JAX-RPC имеется его частичная реализация — абстрактный класс GenericHandler. Реализация тривиальна — все логические методы просто возвращают значение true, прочие методы пусты, и только метод getHeaders о оставлен абстрактным.

Итак, класс-обработчик может выглядеть следующим образом:

public class MyHandler extends GenericHandler{

public MyHandler () {}

public boolean handleRequest(MessageContext context){

try{

SOAPHeader sh = ((SOAPMessageContext)context)

.getMessage().getSOAPPart().getEnvelope().getHeader();

// Обрабатываем блоки заголовка sh

return true;

}catch(Exception ex) { }

)

}

Цепочку классов-обработчиков SOAP-послания следует зарегистрировать у адресата или у клиента Web-службы. Для этого предназначен интерфейс описывающий два метода доступа к цепочке:

public List getHandlerChain(QName portName);

public void setHandlerChain(QName portName, List chain);

Объект типа HandierRegistry можно получить методом public HandierRegistry getHandlerRegistry() ; интерфейса          о котором речь пойдет ниже.

Продолжим наш пример. Получаем цепочку и включаем в нее класс MyHandler:

QName serviceName = new QNarne ("myservice") ;

ServiceFactory sf = ServiceFactory.newlnstance () ; Service serv = sf .createService (serviceName); HandlerRegistry reg = serv.getHandlerRegistry();

List chain = reg.getHandlerChain(serv.getServiceName()); chain.add(new Handlerlnfo(MyHandler.class, null, null));

Кроме регистрации классов-обработчиков в цепочке, их следует указать в конфигурационном файле. Как это сделать — рассказано ниже в этой главе.

Компиляция файлов Web-службы

Интерфейс BooklnfoPort и классы Booklnfolmpl, Book, а также другие нужные классы компилируются обычным образом

j avac *. java

Кроме интерфейса-порта, реализующего его класса и сопутствующих классов, для работы Web-службы надо получить еще несколько классов, обеспечивающих реализацию механизма RMI. Это, в частности, клиентская заглушка и серверная связка, классы, сериализующие и десериализующие сложные типы. Кроме того, надо сделать описание Web-службы на языке WSDL.

Создать вручную эти классы очень сложно, они используют специфику механизма RMI, а это весьма запутанная система. Пакет Sun WSDP предлагает три утилиты для создания классов JAX-RPC. Это уже успевший устареть компилятор      создающий и заглушки и связки, а также новые утили

ты: компилятор wsdeploy, создающий серверные связки и описания WSDL, и компилятор         создающий клиентские заглушки.

Компилятор хгрсс работает из командной строки. Для создания серверных файлов он запускается примерно так:

$ хгрсс -server config.xml

Для создания клиентских файлов указывается флаг -client. Если надо сразу создать все файлы, и серверные, и клиентские, то указывается флаг – both.

Для работы компилятора хгрсс нужны уже откомпилированные SEI- интерфейс и его реализация классом-служителем, а также специальный конфигурационный XML-файл, описывающий Web-службу. Этот файл обычно называется config.xml. Его структура описана в документации, приложенной к компилятору хгрсс, и может в дальнейшем измениться вместе с ним.

Компилятор ws deploy тоже запускается из командной строки следующим образом:

$ wsdeploy -tmpdir temp -о deployable.war portable.war

Он получает информацию из архива deployable.war (имя файла может быть другим), в котором лежат откомпилированные классы, специальный конфигурационный файл с именем jaxrpc-ri.xml и конфигурационный DD-файл (Deployment Descriptor) Web-приложения web.xml [10]. Компилятор сразу же упаковывает все файлы созданной Web-службы в архив, в нашем примере это файл portable.war. Имя архива произвольно, обычно оно отражает название Web-службы.

Флаг -tmpdir указывает каталог для временных файлов, в примере это подкаталог temp текущего каталога. Если дополнительно указать флаг -keep, то в этом каталоге останутся исходные файлы всех классов.

После этого остается установить Web-службу, то есть архивный файл (в примере — это файл portable.war), в Web-контейнер как обычное Web- приложение. Утилита установки, в частности, создает сервлет, который будет представлять класс-служитель (tie) в Web-контейнере.

Конфигурационный файл компилятора jaxrpc-ri.xml

Конфигурационный файл jaxrpc-ri.xml компилятора wsdeploy — это XML- файл с корневым элементом <webServices>, в который вкладывается всего два элемента — <endpoint> и                                           Каждый элемент может

появиться сколько угодно раз.

У корневого элемента <webServices>, кроме атрибута xlmns есть один обязательный атрибут version и три необязательных атрибута. Атрибуты

И                                                                                идентификаторы

СГрвНСГВ имен ШЖШИЯ WSDL И ТИПОВ данных, а атрибут urlPatternBase показывает начальный контекст Web-службы.

Элемент <endpoint> описывает адресата Web-службы следующими атрибутами:

?         обязательный атрибут name определяет название Web-службы, оно будет служить именем файла WSDL с описанием Web-службы;

? необязательный атрибут displayName определяет название Web-службы, используемое утилитами JAX-RPC и Web-контейнером в заголовках и пунктах меню;

?         необязательный атрибут description содержит произвольное описание Web-службы;

?         необязательный атрибут interface содержит имя SEI-интерфейса;

? необязательный атрибут implementation содержит имя класса- служителя;

•          необязательный атрибут model дает ссылку в виде строки URI на дополнительное описание Web-службы.

В элемент <endpoint> может быть вложен элемент <handlerChains>, описывающий цепочку классов-обработчиков. Каждый класс-обработчик описывается Элементом <chain>.

Элемент <endpointMapping> содержит путь к файлу с описанием WSDL. Он не содержит вложенных элементов, у него есть только два обязательных атрибута:

•            атрибут endpointName содержит имя файла с описанием WSDL;

•атрибутиг1Ра^егп определяет путь к нему. Вот схема файла jaxrpc-ri.xml:

<?xml version="1.0" encoding="UTF-8"?> •

<webServices

xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd" version=" xsd:string"

targetNamespaceBase=" xsd:string" typeNamespaceBase=" xsd:string" urlPatternBase=" xsd:string">

<endpoint

name="xsd:string" displayName="xsd:string" description="xsd:string" interface=" xsd:string" implementation^*" xsd: string" model="xsd:anyURI"> [*]

<handlerChains> [?]

< ! — Цепочка классов-обработчиков —> </handlerChains>

</endpoint>

<endpointMapping

endpointName=" xsd: string" urlPattern=" xsd:string" /> [*]

</webServices>

В листинге 6.10 приведен конфигурационный файл jaxrpc-ri.xml компилятора wsdeploy нашей примерной Web-службы Booklnf о.

Листинг 6.10. Конфигурационный файл компилятора jaxrpc-ri.xml

<?xml version="1.0" encoding="UTF-8M?>

<webServices

xmlns="http://java.sun.com/xml/ns/jax-rpc/ri/dd"

version="l.0"

targetNamespaceBase="http://bookinfo.com/wsdl"

typeNamespaceBase="http://bookinfo.com/types"

urlPatternBase="/ws">

<endpoint

name="BookInfo" displayName="BookInfo Service" description="CBefleHHH о книгах" interface="bookinfo.BooklnfoPort" implementation="bookinfo.Booklnfolmpl" />

< endpo i ntMappi ng

endpointName="BookInfo"

urlPattern="/baokinfo" />

</webServices>

После того как компилятор wsdeploy закончит работу, устанавливаем созданный им архив portable.war в Web-контейнер. Для Web-контейнера Tomcat в пакете WSDP есть графическая утилита установки deploytool. Она запускается из командной строки при работающем Tomcat просто набором ее имени

$ depioytool

После ее запуска на экране последовательно появляется ряд окон с подробными объяснениями процесса установки.

После установки Web-службы в Web-контейнер, можно проверить ее наличие, открыв на сервере браузер и набрав в нем адрес http://localhost:8080/services/bookinfo. В окне браузера появится список установленных Web-служб.

Слово "services" в этом адресе — это контекст сервлета, созданный в процессе установки. Он может получить другое название. Слово "/bookinfo" вместе с начальной наклонной чертой — это значение атрибута элемента <endpointName>, записанного в файле jaxrpc-ri.xml.

Итак, Web-служба создана, установлена в Web-контейнер, контейнер запущен и ждет, когда пойдут запросы. Теперь можно перейти к созданию клиента.

Литература:

Хабибуллин И. Ш. Разработка Web-служб средствами Java. — СПб.: БХВ-Петербург, 2003. — 400 с: ил.

По теме:

  • Комментарии