Статьи

Богатый API веб-сервисов для вашей любимой платформы, часть 2: JBoss Seam

Целью этой серии практических рекомендаций является демонстрация разработки API-интерфейса с широкими возможностями веб-служб на различных популярных средах разработки. Часть 2 (это руководство) предназначена для JBoss Seam.

  • Конечные точки ОТДЫХА . В примере сообщений будут предоставлены ресурсы REST в форматах данных XML и JSON.
  • SOAP конечные точки . Пример API сообщений будет представлен через SOAP и определен четко определенным и консолидированным WSDL.
  • Полная документация по API . API веб-службы сообщений будет полностью документирован.
  • Код на стороне клиента . Приложение предоставит клиентский код Java, который сможет удаленно обращаться к API веб-службы.

Излагают делает это потрясающе легко развивать этот вид услуг API Web: несколько усовершенствований в файл сборки, конфигурационный файл, а параметр настройки web.xml дескриптор развертывания, а также определение конечной точки службы.

Шаг 1: настройка среды

Предполагается, что вы можете заставить работать пример сообщений, следуя инструкциям по запуску примеров на JBoss AS . Таким образом, у вас должен быть установлен JBoss AS, а также Ant 1.7 или более поздняя версия . Предполагая, что JBoss работает, вы должны быть в состоянии перейти к каталогу $ SEAM_HOME / examples / messages (где $ SEAM_HOME относится к каталогу, в который вы распаковали дистрибутив Seam) и выполните:


развертывание муравья

Вы должны быть в состоянии открыть браузер для http: // localhost: 8080 / seam-messages, чтобы увидеть приложение сообщений во всей его красе.

Примечание: это руководство было написано на основе Seam 2.1.0.GA и JBoss AS 4.2.3.GA, обе из которых были самыми последними версиями, доступными на момент написания.

Далее нам нужно интегрировать Enunciate с нашим проектом. Поскольку мы используем Ant, нам нужно скачать последний дистрибутив Enunciate и распаковать его куда-нибудь (назовем это место $ ENUNCIATE_HOME ). Нам нужно будет передать ссылку на это местоположение в нашу среду сборки, отредактировав $ SEAM_HOME / build.properties . Вы уже должны были отредактировать этот файл, чтобы предоставить свойство jboss.home, как указано в руководстве по Seam. Теперь добавим еще одно свойство: enunciate.home = / path / to / enunciate / home .

Последним шагом для интеграции Enunciate в нашу среду сборки является редактирование файла $ SEAM_HOME / examples / message / build.xml . Мы делаем некоторую собственную магию сборки, чтобы вызывать Enunciate при создании нашего каталога war и добавлении некоторых дополнительных зависимостей в наш каталог ear. Мы делаем это, переопределяя стандартные муравьиные цели «война» и «ухо». Мы не будем вдаваться в подробности здесь. Все это задокументировано здесь . Но вот как должен выглядеть файл сборки, когда мы закончим (вы также можете скачать его здесь ):

build.xml

<?xml version="1.0"?>

<project name="Messages" default="deploy" basedir=".">

<!-- Example name -->
<property name="Name" value="Seam Message List Example"/>
<property name="example.name" value="jboss-seam-messages"/>

<!-- Libraries -->
<property name="seam.ui.lib" value="yes"/>
<property name="tomcat.standard.tag" value="yes"/>

<import file="../build.xml"/>

<target name="copyextradependencies">
<copyInlineDependencies id="jstl" scope="runtime" todir="${lib.dir}">
<dependency groupId="apache-taglibs" artifactId="jstl" version="1.1.2"/>
</copyInlineDependencies>
</target>

<target name="war" depends="SeamExample.war">
<!--make sure enunciate.home is set.-->
<fail unless="enunciate.home">
enunciate.home not set, update build.properties
</fail>

<!--define the enunciate classpath-->
<path id="enunciate.classpath">
<fileset dir="${enunciate.home}/lib">
<include name="*.jar"/>
</fileset>
<fileset dir="${enunciate.home}">
<include name="enunciate-full-*.jar"/>
</fileset>
<fileset dir="${java.home}">
<include name="lib/tools.jar"/>
</fileset>
<path refid="build.classpath"/>
</path>

<!--define the enunciate task-->
<taskdef name="enunciate" classname="org.codehaus.enunciate.main.EnunciateTask">
<classpath refid="enunciate.classpath"/>
</taskdef>

<!--enunciate the api-->
<enunciate basedir="src" configFile="enunciate.xml">
<include name="**/*.java"/>
<classpath refid="enunciate.classpath"/>
<export artifactId="spring.app.dir" destination="${war.dir}"/>
</enunciate>

</target>

<target name="ear" depends="SeamExample.ear">
<!--copy the enunciate dependencies to the ear-->
<copy todir="${ear.dir}/lib">
<fileset dir="${enunciate.home}/lib">
<include name="*.jar"/>

<!--exclude annotations.jar since jboss ships with its
own jar containing the jsr250 annoations-->
<exclude name="annotations*.jar"/>

<!--exclude servlet.jar; provided by jboss-->
<exclude name="servlet*.jar"/>

<!--exclude stax; it ships with the JDK-->
<exclude name="stax*.jar"/>
</fileset>
<fileset dir="${enunciate.home}">
<include name="enunciate-full-*.jar"/>
</fileset>
</copy>

</target>
</project>

Теперь давайте добавим API веб-сервиса.

Шаг 2: Создайте конфигурацию

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

Элемент <api-classes> просто сообщает Enunciate, какие классы должны использоваться для определения API веб-службы. По умолчанию Enunciate предполагает, что все классы в проекте являются частью API веб-службы, но в приложении сообщений есть другие классы, которые используются для управления пользовательским интерфейсом, который никогда не был частью API веб-службы. Поэтому мы должны сказать Enunciate, что только два класса используются для определения нашего API веб-службы (обратите внимание, что мы еще не определили класс «MessageService»):

enunciate.xml

...
<api-classes>
<include pattern="org.jboss.seam.example.messages.Message"/>
<include pattern="org.jboss.seam.example.messages.MessageService"/>
</api-classes>
...

Баланс файла конфигурации используется для определения поведения в конкретном модуле Enunciate. Enunciate генерирует документацию нашего API веб-сервиса из JavaDocs наших классов API. Настраивая модуль docs , мы говорим Enunciate поместить документацию в каталог / api приложения и назначить заголовок документации.

Enunciate собирает API веб-службы в форме приложения на основе сервлета, и его необходимо объединить с приложением сообщений. Мы делаем это путем слияния сгенерированного Enunciate файла web.xml с файлом messages web.xml .

Мы также просим Enunciate забыть о компиляции, упаковке и копировании зависимостей, поскольку сборка приложений для сообщений уже делает это.

enunciate.xml

...
<modules>
<docs docsDir="api" title="Seam Messages API"/>
<spring-app doCompile="false" doPackage="false" doLibCopy="false">
<war mergeWebXML="resources/WEB-INF/web.xml"/>
</spring-app>
</modules>
...

 

Шаг 3: Определите классы API

Создать MessageService.java

Seam (и JSF в целом) использует ориентированный на состояние стиль для определения бизнес-логики и управления процессами. Мы должны предоставить сервис-ориентированный интерфейс, который легче представить как веб-сервис. Мы просто напишем наш собственный сервис для использования уже определенной функциональности MessageManagerBean . Наш сервис должен иметь методы: getMessages для получения списка всех сообщений и getMessage go для получения конкретного сообщения по идентификатору. Чтобы реализовать этот сервис, мы должны внести небольшое изменение в наш сессионный компонент, чтобы отобразить список сообщений. Мы просто изменим метод findMessages, чтобы он возвращал список сообщений.

Вот как должен выглядеть интерфейс org.jboss.seam.example.messages.MessageManager (изменилось только возвращаемое значение метода findMessages ):

MessageManager.java

@Local
public interface MessageManager
{
public List<Message> findMessages();
public void select();
public void delete();
public void destroy();
}

И вот компонент реализации org.jboss.seam.example.messages.MessageManagerBean :

MessageManagerBean.java

@Stateful
@Scope(SESSION)
@Name("messageManager")
public class MessageManagerBean implements Serializable, MessageManager
{

...

@Factory("messageList")
public List<Message> findMessages()
{
messageList = em.createQuery("select msg from Message msg order by msg.datetime desc").getResultList();
return messageList;
}

...

}

Теперь мы можем определить простой класс org.jboss.seam.example.messages.MessageService, который определяет наши операции (вы также можете скачать его здесь ):

MessageService.java

public class MessageService {

public List<Message> getMessages() {
MessageManager bean = getMessageManager();
return bean.findMessages();
}

public Message getMessage(long id) {
for (Message message : getMessages()) {
if (message.getId() != null && message.getId().equals(id)) {
return message;
}
}

return null;
}

private MessageManager getMessageManager() {
return (MessageManager) Component.getInstance(MessageManagerBean.class, ScopeType.SESSION, true);
}
}

SOAP Метаданные

Чтобы предоставить интерфейс SOAP, нам просто нужно применить некоторые метаданные JAX-WS к MessageService . В частности, мы применяем аннотацию @ javax.jws.WebService к классу.

ОТДЫХ Метаданные

Конечно, мы также хотим применить интерфейс REST к нашему API. Это можно сделать, применив аннотации JAX-RS к MessageService , но это немного сложнее из-за дополнительных ограничений API REST.

Прежде всего необходимо сопоставить службу с путем URI, применив аннотацию @ javax.ws.rs.Path . Мы будем монтировать сервис по пути «/ messages».

Далее, поскольку вы ограничены ограниченным набором операций, вы должны аннотировать конкретные методы, которые должны быть включены в REST API. Вы должны указать метод HTTP, который используется для вызова метода, и подпуть, который используется для его обнаружения. Мы сделаем это просто, выставив только метод getMessage с помощью операции GET с помощью аннотации javax.ws.rs.GET и смонтировав метод по пути «/ messages / message / {id}» с помощью javax.ws.rs .Path аннотация. «{Id}» в пути будет указывать идентификатор сообщения, которое мы хотим получить. Это означает, что параметр метода должен быть аннотирован аннотацией @ javax.ws.rs.PathParam, которая также используется для указания имени параметра пути.

Конечно, вы можете предоставить другие методы, используя другие аннотации, но мы отошлем вас к документации JAX-RS, чтобы узнать, как это сделать. Вот как MessageService выглядит после применения всех аннотаций:

MessageService.java

package org.jboss.seam.example.messages;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;

import javax.jws.WebService;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import java.util.List;

/**
* Message service for retrieving messages.
*/
@WebService
@Path("/messages")
public class MessageService {

/**
* Get the list of messages.
*
* @return The list of messages.
*/
public List<Message> getMessages() {
MessageManager bean = getMessageManager();
return bean.findMessages();
}

/**
* Get the message of the specified id.
*
* @param id The message id.
* @return The message, if any.
*/
@GET
@Path("/message/{id}")
public Message getMessage(@PathParam("id") long id) {
for (Message message : getMessages()) {
if (message.getId() != null && message.getId().equals(id)) {
return message;
}
}

return null;
}

private MessageManager getMessageManager() {
return (MessageManager) Component.getInstance(MessageManagerBean.class, ScopeType.SESSION, true);
}
}

Еще одна вещь необходима для предоставления REST API. Поскольку по умолчанию конечные точки REST будут предоставлять данные XML, мы должны предоставить корневые элементы XML для ответов XML. Для этого мы просто аннотируем класс org.jboss.seam.example.messages.Message с помощью @ javax.xml.bind.annotation.XmlRootElement .

Message.java

@XmlRootElement
public class Message {
...
}

 

Шаг 4: Установите жизненный цикл компонента шва

Поскольку наш MessageService использует преимущества жизненного цикла компонента Seam (обратите внимание на метод getMessageManager ), мы должны убедиться, что жизненный цикл настроен при вызове нашего сервиса. Мы делаем это, используя ServletFilter , который уже был написан людьми из Seam. Мы включаем этот фильтр в нашем файле resources / WEB-INF / web.xml и применяем его ко всем запросам:

<filter>
<filter-name>Seam Context Filter</filter-name>
<filter-class>org.jboss.seam.web.ContextFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>Seam Context Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

 

Шаг 5: Построить и развернуть

Вернемся к командной строке:


развертывание муравья

 

Вот слава

Ваше приложение полнофункционально по адресу http: // localhost: 8080 / seam-messages / .

Ознакомьтесь с документацией по вашему новому API веб-службы по адресу http: // localhost: 8080 / seam-messages / api / :

Все задокументировано, снято с комментариев JavaDoc. Вот документация для SOAP API:

И документация для REST API:

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

А как насчет вашего WSDL? HTTP: // локальный: 8080 / Шов-сообщения / API / ns1.wsdl

А как насчет вашей XML-схемы? HTTP: // локальный: 8080 / Шов-сообщения / API / ns0.xsd

Хотите увидеть свой API в действии? Ваши конечные точки SOAP монтируются в подконтексте / soap , а ваши конечные точки REST монтируются в подконтексте / rest . Чтобы просмотреть сообщение, просто используйте путь, который мы определили с помощью аннотаций JAX-RS относительно подконтекста / rest . Поэтому для просмотра сообщения, идентифицируемого идентификатором «1», мы используем http: // localhost: 8080 / seam-messages / rest / messages / message / 1 :

Для удобства тот же ресурс XML также можно найти по адресу http: // localhost: 8080 / seam-messages / rest / messages / message / 1 . И если вы хотите получить тот же ресурс, что и JSON, вы можете использовать http: // localhost: 8080 / seam-messages / json / messages / message / 1 .

И дальше …

Ну, вот как легко добавить API веб-сервиса в ваше приложение JBoss Seam. Но мы только слегка поцарапали поверхность того, что может сделать Enunciate. Как насчет всего этого:

  • Безопасность (HTTP Auth, OAuth, вход в систему на основе форм, управление сеансами и т. Д.)
  • Конечные точки GWT RPC и клиентский JavaScript для доступа к ним.
  • Конечные точки AMF и ActionScript на стороне клиента для доступа к ним.
  • Потоковый API для больших запросов.
  • И т.п.

На данный момент, это только вопрос конфигурации ….