Организация интеллектуальной совместной деятельности в сфере образования с использованием Lotus Connections: Часть 1. Интеграция Lotus Connections с RESTful Web-приложением

Отслеживание публикаций по автору при помощи Lotus Connections Profiles

Расширьте возможности IBM® Lotus® Connections при помощи RESTful Web-приложения, поддерживающего программные интерфейсы XML и JSON. Пользовательский интерфейс Connections Profiles представляет собой клиентский виджет, основанный на Dojo Toolkit. Web-приложение позволяет преподавателю университета публиковать документы для широкого доступа на странице своего профиля социальной сети. Также можно настроить приложение на совместный доступ к другой информации в профиле преподавателя, такой как полученные гранты на научно-исследовательские работы или проводимые учебные курсы.

Владислав Пономарев, разработчик программного обеспечения, IBM

Photo of Vladislav PonomarevВладислав Пономарев (Vladislav Ponomarev) – руководитель команды разработчиков Web-решений IBM Russian Software and Technology Lab. Его основными направлениями деятельности являются приложения Java EE, виртуализация и облачные вычисления.



Карл Осипов, архитектор ПО, IBM

Карл Осипов (Carl Osipov) - архитектор ПО, подразделениe стратегий и технологий IBM Software Group. Обладает большим опытом в области распределенных вычислений, разработке приложений по распознаванию живой речи и компьютерной обработке естественного языка. Имеет ряд публикаций и выступлений по вопросам Сервис-ориентированной архитектуры и управлением разпознования речи перед образовательской и бизнес аудиторией. На данный момент он в основном занимается проектированием технологий повторного использования для составных бизнес-сервисов.



31.10.2011

В статьях данной серии будут описаны возможные сценарии совместной деятельности в учреждениях высшей школы (таких как колледжи или университеты), а также будет представлено техническое руководство по расширению и настройке IBM Lotus Connections и других продуктов IBM для совершенствования сценариев совместной работы, характерных для сферы образования. Основное внимание в этой серии будет уделяться использованию бизнес-аналитики и оптимизации возможностей продуктов IBM с целью дать пользователям Connections возможность принимать более интеллектуальные решения по выявлению потенциальных партнеров и работы с ними. Мы надеемся, что материалы серии будут полезны для руководителей и специалистов стратегического планирования отделов информационных технологий (ИТ) в сфере образования, которые исследуют возможности организации совместной деятельности в своих организациях.

В первой статье серии описывается интеграция IBM Lotus Connections с основанными на REST Web-сервисами, позволяющая использовать расширения продукта, специфичные для сферы образования. В последующих статьях расширенные возможности Connections будут использованы для поддержки оптимизированных процессов, помогающих ведущим исследованиям формировать группы на основании опыта участников и анализировать содержимое статей исследовательских статей для улучшения профилей совместной деятельности и т.д.

Введение

Приложение IBM Lotus Connections® 2.5 (Connections) Profiles допускает расширение при помощи клиентских виджетов. Это относительно простой процесс, в ходе которого пользовательский интерфейс и код виджета объединяются с Profiles. Однако может понадобиться интеграция Connections с отдельным приложением, имеющим внешний программный интерфейс, чтобы клиентский виджет Profiles мог отображать и работать с данными, управляемыми вне Connections.

Часто используемые сокращения

  • API: Application Programming Interface (программный интерфейс приложения)
  • HTTP: Hypertext Transfer Protocol (протокол передачи гипертекста)
  • JAXB: Java Architecture for XML Binding
  • JEE: Java Platform, Enterprise Edition
  • JSON: JavaScript Object Notation
  • REST: Representational State Transfer (передача репрезентативного состояния)
  • UI: User Interface (пользовательский интерфейс)
  • XML: Extensible Markup Language (расширяемый язык разметки)
  • XSS: Cross-site scripting (межсайтовый вызов скриптов)

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

Вторая часть статьи посвящена клиентской стороне - реализации iWidget. Объясняется вся картина возможностей виджета, предоставляемых пользователю, а также то, как при помощи Dojo Toolkit организован и построен пользовательский интерфейс виджета.

Наконец, показывается, как компоненты (Profiles, Web-приложение и виджет) интегрируются друг с другом. Приводятся примеры конфигурационных файлов Connections для интеграции.


RESTful Web-приложение

Мы опишем Web-приложение, которое помогает пользователю Profiles (например, преподавателю в образовательном или научно-исследовательском учреждении) вести список своих публикаций. Доступ к функциональности приложения будет осуществляться посредством основанного на HTTP REST-интерфейса, что упростит взаимодействие с приложением из браузера при помощи JavaScript или любого другого стороннего программного обеспечения. Приложение будет отвечать следующим требованиям:

  1. REST-интерфейс должен:
  • Принимать сообщения запроса в формате XML и поддерживать представление ответа в форматах JSON и XML.
  • Обеспечивать применение операций создания, получения, обновления и удаления (create, retrieve, update, delete - CRUD) к публикациям.
  1. Для каждой публикации необходимо постоянно хранить следующие данные:
  • Auto-generated ID (автоматически генерируемый идентификатор)
  • Publication owner (владелец публикации)
  • Title (название)
  • Year published (год опубликования)
  • Author(s) (автор(ы))
  • Journal (журнал)
  • Publisher (издательство)
  1. Неаутентифицированным пользователям не должно быть разрешено выполнять операции по изменению публикаций.
  2. Аутентифицированные пользователи должны иметь возможность изменять только принадлежащие им публикации.
  3. Приложение должно легко расширяться для поддержки новых REST-объектов (в дополнение к публикациям) без повторной компиляции кода. Например, если приложение ранее поддерживало список грантов для каждого пользователя Profiles, код должен остаться неизменным, а вся разработка должна быть ограничена сборкой приложения, его развертыванием и изменением схемы базы данных.

Используемые технологии

Так как IBM Lotus Connections 2.5 включает в себя экземпляр IBM WebSphere® Application Server (WebSphere) 6.1, мы будем использовать последний для выполнения нашего приложения. WebSphere 6.1 реализует набор J2EE спецификаций, в том числе Java™ Servlet 2.4 (JSR 154) и Enterprise JavaBeans™ (EJB) 2.1 (JSR 153).

Приложение использует совместимый с JSR 154 сервлет в качестве оконечной точки сервиса. Как отмечалось в требованиях, чтобы принимать XML-сообщения в сервлете, необходимо реализовать методы doGet(), doPost(), doPut() и doDelete().

Поскольку JAXB с разрешенным аннотированием (стандарт для XML сериализации и десериализации) в WebSphere 6.1 JRE не поддерживается, мы использовали для поддержки XML в сервлете библиотеки Java 6 JAXB. Что касается поддержки JSON, то наша реализация использует библиотеку JSON4J, поставляемую в составе WebSphere Feature Pack for Web 2.0. Для поддержки персистентности мы используем bean-компоненты JSR 153, которые позволяет применить модель программирования с объектно-реляционным отображением базы данных (см. Приложение Б. Плюсы и минусы EJB).

Обзор архитектуры приложения

На рисунке 1 схематически показана архитектура приложения.

Рисунок 1. Архитектура приложения
Рисунок 1. Архитектура приложения

Пользовательский интерфейс, показанный в левой части рисунка, предоставляет пользователю графические HTML-элементы управления для выполнения CRUD-операций над публикациями. Всякий раз, когда действие пользователя в браузере вызывает операцию, JavaScript приложения передает HTTP-запрос на сторону сервера (центральная часть рисунка) через REST-интерфейс. Для Чтобы отобразить пользовательский интерфейс на странице Profiles, HTML-разметка (HTML markup) и JavaScript упаковываются в совместимом с Connections iWidget-формате.

В центральной части рисунка представлен JEE-контейнер, содержащий сервлет, bean-компонент (EJB) и вспомогательные компоненты. Полученный из браузера HTTP-запрос передается через фильтр безопасности в соответствующий метод (такой как doPost()) в REST-сервлет (REST servlet). Затем сервлет вызывает соответствующую операцию CRUD через фасадный объект bean-компонента. Фасадный объект изолирует клиентский код от детализированного интерфейса, предоставляемого bean-компонентом, что позволяет клиенту работать с компактными объектами Plain Old Java Object (POJO). Наконец, сервлет предоставляет ответ в соответствующем формате, чем завершается обработка запроса.

В правой части рисунка 1 представлена таблица базы данных, развернутой на сервере базы данных. JEE-контейнер и база данных показаны логически разделенными, однако на практике они могут находиться в одном месте. Теперь посмотрим на структуру базы данных на рисунке 2.

Рисунок 2. Структура базы данных
Рисунок 2. Структура базы данных

Таблица PUBLICATION базы данных, представленная на рисунке 2, создается как часть базы данных Connections PEOPLEDB. Одним из преимуществ использования общей базы данных для Profiles и приложения является возможность наложить ограничение внешнего ключа (см. рисунок 2) на столбец OWNERID таблицы PUBLICATION, чтобы он ссылался на столбец первичного ключа таблицы EMPLOYEE.

Теперь посмотрим на взаимосвязь POJO и EJB, начиная с иерархии объектов POJO (см. рисунок 3).

Рисунок 3. Иерархия объектов домена
Рисунок 3. Иерархия объектов домена

Корневой класс DomainObject описывает объект домена, имеющий идентификатор (ID) и соответствующие методы доступа. Потомок этого класса, OwnedDomainObject (внизу слева на рисунке), добавляет поле идентификатора (ID) владельца. Объект Publication наследует OwnedDomainObject и добавляет поля, специфичные для публикации. Классы являются JAXB-аннотированными, что делает возможной сериализацию и десериализацию объекта в XML и обратно.

Посмотрим на bean-компоненты на рисунке 4.

Рисунок 4. Иерархия bean-компонентов
Рисунок 4. Иерархия bean-компонентов

Каждому POJO соответствует bean-компонент (см. левую часть рисунка 4). Кроме того, дерево наследования bean-интерфейсов похоже на дерево объектов POJO, т.е. DomainObject имеет соответствующие интерфейсы DomainLocal и DomainLocalHome и аналогично для OwnedDomainObject (см. правую часть рисунка 4). Bean-интерфейсы созданы с использованием Java 5 Generics, что позволяет:

  • Снабдить клиентский код фабричным методом toPojo(), используемым для преобразования bean-компонента в POJO-объект.
  • Определить методы update() и create(), принимающие POJO в качестве аргумента.

Специальный фасадный объект скрывает bean-компоненты клиентского кода и выполняет CRUD-операции над ними. Фасад (см. рисунок 1) может поддерживать произвольные объекты, если они построены по аналогии с объектом Publication (т.е. придерживаются тех же правил наследования), регистрируя их посредством метода registerDomainObject().

При запуске приложения REST-сервлет, используя конфигурационные параметры, взятые из файла web.xml, создает и настраивает адаптерные объекты. Это позволяет Application Assembler (роль JEE определяется спецификацией EJB 2.1, глава 3.1.2) обеспечить возможность работы приложения с новыми объектами без перекомпиляции кода. Объект REST-сервиса отвечает за отображение HTTP-запросов на конкретные типы объектов.

Уровень доступа к данным

POJO-объект Publication реализован в соответствии с рисунком 3 из предыдущего раздела. Обратите внимание, что поля классов аннотированы при помощи @XmlElement, чтобы объект можно было сериализовать в XML и десериализовать обратно с использованием JAXB.

Bean-компоненты отображают только локальный интерфейс, чтобы исключить доступ к ним по сети. Взгляните на локальный интерфейс объекта в листинге 1.

Листинг 1. Локальные интерфейсы
public interface DomainLocal<D extends DomainObject>
{
 String getId();
 void setId(String newId);
 
 void update(D domainObject);
 D toPojo();
}

public interface OwnedDomainLocal<D extends OwnedDomainObject> extends DomainLocal<D>
{
 String getOwnerId();
 void setOwnerId(String ownerId);
}

public interface PublicationLocal extends OwnedDomainLocal<Publication>, EJBLocalObject
{
 public java.lang.String getTitle();
 public void setTitle(java.lang.String newTitle);

 public java.lang.String getJournal();
 public void setJournal(java.lang.String newJournal);

 public java.lang.String getPublisher();
 public void setPublisher(java.lang.String newPublisher);

 public java.lang.Integer getYearPublished();
 public void setYearPublished(java.lang.Integer newYear);

 public java.lang.String getInternetLink();
 public void setInternetLink(java.lang.String newLink);

 public void setAuthors(String authors);
 public String getAuthors();
}

Отметим, что корень иерархии, интерфейс DomainLocal, предусматривает, что объекты должны иметь фабричный метод для преобразования себя в POJO. Кроме того, метод update(), который принимает экземпляр POJO, действует как фасад для мелкозернистых мутаторов (методы setXXX()). Рекомендуется изменять состояние объектов с помощью этого метода так, чтобы все поля устанавливались в одной операции.

Локальные исходные интерфейсы следуют той же иерархии. Они определяют методы для создания и поиска объектов.

Листинг 2. Локальные исходные интерфейсы
public interface DomainLocalHome<D extends DomainObject, L extends DomainLocal<D>>
{
 Collection<L> findAll()
 throws FinderException;
}

public interface OwnedDomainLocalHome
 <D extends OwnedDomainObject, L extends OwnedDomainLocal<D>>
 extends DomainLocalHome<D, L>
{
 Collection<L> findByOwner(String ownerId)
 throws FinderException;
}

public interface PublicationLocalHome
 extends OwnedDomainLocalHome<Publication, PublicationLocal>, EJBLocalHome
{
 public PublicationLocal create(Publication pub) // Return type is local interface!
 throws CreateException; 

 public PublicationLocal findByPrimaryKey(String primaryKey)
 throws FinderException;
}

Функция Generics языка Java, широко используемая в коде, минимизирует необходимость приведения типов классов в клиенте. Однако использование Generics порождает ряд проблем:

Спецификация EJB ограничивает возвращаемый тип методов create() и findByPrimaryKey() для интерфейса объекта (см. комментарий во фрагменте кода, приведенном в листинге 2). Поскольку реализация объектов генерируется JEE-контейнером во время выполнения, когда нет доступной порожденной информации, нельзя переместить метод create() вверх в иерархии и сделать его общим, как этосделано с findByOwner().

Функция очистки Java Generics скрывает тип информации конкретных POJO-классов. Поэтому реализация методов update() и toPojo() локального интерфейса должна зависеть от общего предка, т.е. от DomainObject.

В листинге 3 показана реализация методов.

Листинг 3. Реализация общих методов в компоненте Publication
public abstract class PublicationBean implements javax.ejb.EntityBean
{
 public String ejbCreate(Publication pub)
 throws javax.ejb.CreateException
 {
 setId(pub.getId());
 setAuthors(pub.getAuthors());
 setInternetLink(pub.getInternetLink());
 setJournal(pub.getJournal());
 setOwnerId(pub.getOwnerId());
 setPublisher(pub.getPublisher());
 setTitle(pub.getTitle());
 setYearPublished(pub.getYearPublished());
 
 return null;
 }

 public void ejbPostCreate(Publication pub)
 throws javax.ejb.CreateException {
 }

 public void update(DomainObject domainObject)
 {
 Publication pub = (Publication) domainObject;
 
 setAuthors(pub.getAuthors());
 setInternetLink(pub.getInternetLink());
 setJournal(pub.getJournal());
 setOwnerId(pub.getOwnerId());
 setPublisher(pub.getPublisher());
 setTitle(pub.getTitle());
 setYearPublished(pub.getYearPublished());
 }
 
 public DomainObject toPojo()
 {
 Publication pub = new Publication();
 pub.setId(getId());
 pub.setAuthors(getAuthors());
 pub.setInternetLink(getInternetLink());
 pub.setJournal(getJournal());
 pub.setOwnerId(getOwnerId());
 pub.setPublisher(getPublisher());
 pub.setTitle(getTitle());
 pub.setYearPublished(getYearPublished());
 
 return pub;
 }
...
}

Теперь, когда EJB реализован, необходим фасадный объект, который, во-первых, скрывает код поиска Java Naming and Directory Interface (JNDI), и, во-вторых, позволяет клиентам работать с REST-объектами (такими как Publication) в терминах соответствующих POJO-классов. В интерфейсе фасада должны быть CRUD-методы, как показано в листинге 4.

Листинг 4. Методы интерфейса фасада
public <D extends DomainObject> Collection<D> find(Class<D> domainClass)
public <D extends DomainObject> D find(Class<D> domainClass, String id)
public <D extends DomainObject> void create(D domainObject)
public <D extends DomainObject> void remove(Class<D> domainClass, String id)
public <D extends DomainObject> void update(D domainObject)
public <LH extends DomainLocalHome<D, ?>, D extends DomainObject>
void registerDomainObject(
 Class<LH> localHomeClass,
 Class<D> domainClass,
 String jndiName,
    LocalInterfaceCaller<?, LH, D> caller)

Цель последнего метода в списке - проинформировать фасадный объект о новых REST-объектах.

Пример регистрации объекта

Вот как REST-объект Publication регистрируется в фасаде:

service.registerDomainObject(
PublicationLocalHome.class, Publication.class, "ejb/Publication", new PublicationCaller());

Фасадный объект позволяет клиентскому коду работать как с конкретными POJO-классами (например, Publication), так и с базовыми классами (DomainObject или OwnedDomainObject). Однако есть еще одно препятствие: фасаду нужно вызвать методы create() и findByPrimaryKey(), которые принадлежат конкретному исходному интерфейсу (например, PublicationLocalHome), а не базовому. Чтобы обойти это, при регистрации нового REST-объекта посредством метода registerDomainObject() клиентский код должен передать дополнительный объект, включающий вышеупомянутые вызовы исходного интерфейса, как показано в листинге 5.

Листинг 5. Реализация примера вызывающей программы
public class PublicationCaller implements 
    CrudService.LocalInterfaceCaller<PublicationLocal, PublicationLocalHome, Publication>
{ 
    public void callCreate(PublicationLocalHome localHome, 
        Publication domainObject) throws CreateException { 
        localHome.create(domainObject); 
    } 

public PublicationLocal callFindByPrimaryKey( PublicationLocalHome localHome, String id) 
    throws FinderException { 
        return localHome.findByPrimaryKey(id); 
    }
}

Generic-параметры LocalInterfaceCaller заменяются конкретным локальным и локальным исходным интерфейсами, которые предоставляют методы create() и findByPrimaryKey().

Кроме того, скрывая связанный с EJB код, фасадный объект (в сочетании с иерархией объектов домена) дает еще одно преимущество: он может упростить миграцию на EJB 3. POJO-объекты станут JPA-аннотированными (Java Persistence API), превратившись в bean-компоненты, а реализация фасада будет изменена с учетом следующей версии спецификации EJB. Обратите внимание, что клиентский код при этом не затрагивается.

Рисунок 5. Диаграмма класса фасадного объекта
Рисунок 5. Диаграмма класса фасадного объекта

Реализация фасадного объекта приведена в примере исходного кода. Ссылка на пример находится в разделе Загрузка.

Механизм обеспечения безопасности

Чтобы понять реализацию безопасности, необходимо определить угрозы, от которых требуется защитить наши приложения.

  1. Операции модификации (выраженные HTTP-методами POST, PUT и DELETE) должны быть разрешены только для аутентифицированных пользователей.
  2. Аутентифицированным пользователям должно быть разрешено изменять только те публикации, владельцами которых они являются.

Механизм аутентификации, используемый в Lotus Connections, основывается на маркере Lightweight Third-Party Authentication (LTPA), передаваемом при помощи cookie в каждом HTTP-запросе. Поскольку пользовательский интерфейс приложения интегрирован в Profiles, можно ожидать, что тот же маркер будет присутствовать и в запросе к REST-сервлету. WebSphere Application Server идентифицирует пользователя на основании маркера; таким образом, приложение выполняет аутентификацию посредством вызова метода getRemoteUser(), определенного в классе HttpServletRequest. Если пользователь прошел аутентификацию, этот метод возвращает учетное имя пользователя для входа в систему. В противном случае этот вызов возвращает пустое значение.

Для реализации механизма обеспечения безопасности перед сервлетом располагается фильтр для перехвата каждого запроса (см. рисунок 6). Фильтр определяет, аутентифицирован ли пользователь, как описано выше. Если пользователь неаутентифицирован и HTTP-метод отличается от GET, обработка запроса завершается и возвращается код 401 HTTP-статуса (не авторизован). Если HTTP-методом является GET, поток управления передается в сервлет. Наконец, если аутентифицированный пользователь собирается выполнять операции модификации, фильтр извлекает учетное имя пользователя, запрашивает базу данных, чтобы получить идентификатор пользователя в Connections, и кэширует его в сессионном объекте. После аутентификации пользователя при каждом последующем вызове фильтр ничего не делает; таким образом, идентификатор извлекается только один раз.

Рисунок 6. Фильтрация аутентификации
Рисунок 6. Фильтрация аутентификации

Когда HTTP-запрос на выполнение операции модификации попадает в сервлет, идентификатор пользователя кэшируется в сессионном объекте; это дает возможность сравнить этот идентификатор с идентификатором владельца целевого REST-объекта (в данном случае это Publication). Для инкапсуляции этой авторизации мы разработали адаптер в дополнение к фасадному объекту, описанному выше. Адаптер содержит ссылку на фасад. Последний объявлен при помощи публичных методов, отображающих CRUD-методы, определенные в адаптере, но с идентификатором пользователя в качестве дополнительного аргумента. Идентификатор владельца REST-объекта сравнивается с идентификатором пользователя, и если они не совпадают, генерируется исключительная ситуация авторизации.

Поддержка XML и JSON

Поддержка XML работает в приложении без дополнительных настроек, если JAXB-библиотеки включены в переменную среды и POJO-объекты являются аннотированными. Достаточно зарегистрировать классы домена в объекте JAXBContext и использовать его фабричные методы createMarshaller() и createUnmarshaller() для получения объектов домена, пригодных для XML-сериализации и десериализации.

Чтобы включить поддержку JSON, мы использовали библиотеку JSON4J, поставляемую в составе WebSphere Feature Pack for Web 2.0. Вывод в JSON-формате регулируется требованиями компонента dojo.data.ItemFileReadStore: возвращенный объект определяет атрибуты identifier, label и items.

Сервлет принимает XML-сообщения и представляет ответ в XML или JSON-формате в зависимости от заголовка Accept HTTP-запроса или от параметра as запроса. Последний имеет приоритет над заголовком. Эта логика заключена в специальный фабричный метод сервлета, который создает либо XML, либо JSON-рендерер, отвечающий за сериализацию объектов (см. рисунок 7).

Рисунок 7. Диаграмма классов рендереров
Рисунок 7. Диаграмма классов рендереров

Реализация REST-сервлета

Жизненный цикл REST-сервлета начинается с метода init(), который выполняет инициализацию сервлета на основе параметров, определенных в web.xml.

Параметр инициализации CrudConfigurators содержит разделенный запятыми список классов, которые реализуют интерфейс CrudServiceConfigurator. Эти объекты должны иметь конструктор без аргументов. После создания экземпляра вызывается метод configure() для каждого объекта, который принимает CRUD-экземпляр фасада. Каждый конфигуратор регистрирует в фасаде набор REST-объектов.

Аналогичным образом параметр restConfigurators заключает в себе разделенный запятыми список реализаций RestServiceConfiguration. Обработка происходит так же, как и для crudConfigurators. Объект RestService отвечает главным образом за установление соответствия между путем запроса и конкретным классом домена. Например, запрос DELETE /rest/publication/123 должен быть выполнен в отношении объекта Publication. Каждый конфигуратор регистрирует свой собственный набор REST-объектов в объекте RestService.

Механизм конфигурации и фасадный объект, рассмотренные ранее, позволяют легко добавлять поддержку для любых объектов, следующих проектному подходу Publication, без перекомпиляции кода. Для каждого нового объекта Application Assembler должен просто добавлять новые объекты конфигуратора для параметров инициализации сервлета в дескриптор развертывания и связывать их с классами домена в Web-приложении.

После настройки web.xml сервлет готов для обработки HTTP-запросов. В таблице 1 приводится резюме по программному интерфейсу REST, предоставляемому сервлетом. Для краткости часть URL <host>:<port> опускается.

Таблица 1. Программный интерфейс REST-сервлета
HTTP-методURIПараметры
GET/tonkawaWeb/rest/publication
[?ownerId=<ownerId>]
[&as={xml|json}]
POST/tonkawaWeb/rest/publication[?as={xml|json}]
DELETE, PUT/tonkawaWeb/rest/publication/<id>[?as={xml|json}]

После успешной обработки тело ответа на запрос GET будет содержать список публикаций, ответа на POST - одну публикацию (хранится в базе данных), а ответа на PUT или DELETE - только подтверждающее сообщение. Если возникает исключительная ситуация, тело ответа будет содержать код HTTP-статуса и необязательное сообщение об ошибке в соответствующем формате рендеринга.

В листинге 6 показан пример HTTP-запроса.

Листинг 6. Пример HTTP-запроса POST
POST /tonkawaWeb/rest/publication
Accept: application/json

<?xml version="1.0" encoding="UTF-8"?>
<publication>
 <id></id>
 <title>Publication title</title>
 <internetLink>http://mysite.com</internetLink>
 <authors>John Doe</authors>
 <journal>My Journal</journal>
 <publisher>Publisher and Co</publisher>
 <yearPublished>2010</yearPublished>
 <ownerId>6d14080a-e26e-40d3-8588-9b7b4011e65d</ownerId>
</publication>

На диаграмме на рисунке 8 проиллюстрировала последовательность обработки запроса, где компоненты связаны друг с другом. (Увеличенная версия рисунка 8.)

Рисунок 8. Обработка HTTP-запроса POST
Рисунок 8. Обработка HTTP-запроса POST

iWidget

Пользовательский интерфейс приложения реализуется при помощи компонента iWidget 1.0, включенного в интерфейс Profiles. Пользовательский интерфейс использует библиотеку Dojo Toolkit JavaScript версии 1.2.3, которая поставляется вместе с Lotus Connections 2.5. Виджет публикаций предоставляет следующие возможности:

  • Просмотр списка публикаций на открытой странице профиля пользователя Connections.
  • Создание или обновление публикации с принудительным исполнением правил проверки достоверности на стороне клиента.
  • Удаление публикации.

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

Структура пользовательского интерфейса

Пользовательский интерфейс iWidget состоит из контейнера стека Dojo с тремя панелями. Первая панель предоставляет пользователю отформатированный список публикаций, предназначенный только для чтения (см. рисунок 9а или увеличенную версию рисунка 9а).

Рисунок 9а. Список публикаций
Рисунок 9а. Список публикаций

Когда пользователь просматривает свой собственный профиль, он может перейти на следующую панель, где можно выбрать отдельные публикации для редактирования (см. рисунок 9б или увеличенную версию рисунка 9б).

Рисунок 9б. Сведения о публикации
Рисунок 9б. Сведения о публикации

Панель содержит кнопки Add, Edit и Remove. Нажмите кнопку Add или Edit, чтобы показать панель формы редактирования с полями Title, Link, Author(s), Journal, Publisher, Year и Selected (см. рисунок 9в или увеличенную версию рисунка 9в).

Рисунок 9в. Редактирование публикации
Рисунок 9в. Редактирование публикации

Элементы ввода данных формы реализуют базовые правила проверки достоверности (см. раздел Проверка достоверности ввода).

Реализация

Согласно спецификации, iWidget состоит из JavaScript-класса (в данном случае это класс DwPublication), содержащего определение обработчиков обратного вызова, и XML-файла, содержащего HTML-разметку.

На рисунке 10 показано, как компоненты пользовательского интерфейса взаимодействуют друг с другом и с сервером. (Увеличенная версия рисунка 10.)

Рисунок 10. Взаимодействие компонентов iWidget
Рисунок 10. Взаимодействие компонентов iWidget

После первой загрузки компонента iWidget публикаций выполняются его обработчики обратного вызова OnLoad() (шаг 1 на рисунке 10), а затем его HTML-разметка обрабатывается Dojo, и виджет отображается в браузере. После этого класс DwPublication извлекает список публикаций, создавая экземпляр объекта хранилища данных Dojo (dojo.data.ItemFileWriteStore) и указывая ему запросить список публикаций с сервера (шаг 2). Хранилище данных передается функции обратного вызова, которая создает соответствующие DOM-узлы для отображения списка публикаций, предназначенных только для чтения (шаг 2.1). Когда пользователь нажимает ссылку Edit publication, класс DwPublication создает новый объект DataGrid и показывает панель редактирования для отображения сетки (шаг 3). Сетка заполняется содержимым хранилища данных, созданным на шаге 2.

Панель редактирования содержит кнопки Create, Edit и Remove. При нажатии любой из первых двух кнопок отображается форма редактирования для ввода данных пользователем. После отправки формы управление процессом передается обратно в DwPublication, который посылает HTTP-запросы POST или PUT на создание или обновление публикации.

При нажатии кнопки Remove, DwPublication посылает HTTP-запрос DELETE, чтобы удалить публикацию из базы данных.

Наша реализация iWidget обеспечивает корректное поведение функций обновления и развертывания, предоставляемых Lotus Connections. Когда происходит загрузка или перезагрузка всей Web-страницы Profiles и iWidget находится в развернутом состоянии, обработчик события onLoad() тела HTML, добавленный Dojo, автоматически анализирует HTML-разметку страницы, строя Dojo-виджеты. Наоборот, когда iWidget обновляется из контекстного меню (или разворачивается после загрузки страницы с минимизированным iWidget), дерево DOM iWidget уничтожается и строится заново на основе XML-файла, заданного как часть определения iWidget. В этом случае анализ не выполняется, и его следует вызывать вручную. Кроме того, Dojo-виджеты, созданные до события обновления или развертывания, должны быть уничтожены, чтобы предотвратить утечку памяти.

На рисунке 11 показано различие между этими процессами. Обработчики обратного вызова OnLoad() и OnUnload() в классе DwPublication вызываются в том случае, если необходимо очистить старые виджеты и заново проанализировать HTML-разметку.

Рисунок 11. Поведение функции загрузки (перезагрузки) iWidget
Рисунок 11. Поведение функции загрузки (перезагрузки) iWidget

Поддержка локализации

Для обеспечения полной локализации пользовательского интерфейса мы реализовали ряд специализированных Dojo-виджетов, предназначенных для установки свойства InnerHtml конкретного узла в строку локализации, взятую из набора ресурсов (resource bundle). Прежде всего разрабатывается миксин (mix-in), поддерживающий свойство label (см. листинг 7).

Листинг 7. Миксин со свойством label
dojo.provide("com.ibm.tonkawa.i18nInnerHtmlMixin");

dojo.declare(
 "com.ibm.tonkawa.i18nInnerHtmlMixin",
 null,
 {
 label: {},
 _defaultLabel: "$unset$",

 buildRendering: function()
 {
 this.inherited(arguments);
 this.containerNode.innerHTML = this.label ? this.label : this._defaultLabel;
 }
 }
);

Имея этот миксин, можно добавить свойство label к только что объявленному Dojo-виджету, указав i18nInnerHtmlMixin в списке предков (см. фрагмент кода в листинге 8). Реализация по умолчанию метода buildRendering() может быть отменена для ввода значения свойства label в любой DOM-узел.

Листинг 8. Виджет LocalizedLabel
dojo.provide("com.ibm.tonkawa.LocalizedLabel");

dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.require("com.ibm.tonkawa.i18nInnerHtmlMixin");

dojo.declare(
 "com.ibm.tonkawa.LocalizedLabel",
 [dijit._Widget, dijit._Templated, com.ibm.tonkawa.i18nInnerHtmlMixin],
 {
 templateString: "<label for=\"${forLabel}\" dojoAttachPoint=\"containerNode\"></label>",
 forLabel: ""
 }
);

Наконец, в HTML-разметке используется Dojo-виджет локализации, как показано в листинге 9.

Листинг 9. Объявление виджета локализации в HTML-разметке
<script>
dojo.requireLocalization("com.ibm.tonkawa", "Publication", null, "en,ru,ROOT"); myBundle = dojo.i18n.getLocalization("com.ibm.tonkawa", "Publication"); </script> ... <div forLabel="publicationYearPublished" dojoType="com.ibm.tonkawa.LocalizedLabel" label="myBundle.labelYearPublished"/>

В данном случае строка ресурса labelYearPublished берется из набора ресурсов и устанавливается в качестве значения свойства label специализированного виджета.

Profiles и Dojo

Чтобы гарантировать работу элементов управления Dojo с Profiles, мы используем специальные приемы. Во-первых, необходимо поместить контейнеры в тег <div> с определенным атрибутом id. Во-вторых, чтобы dojox.grid.DataGrid отображался корректно, до анализа разметки мы должны загрузить набор CSS-файлов. Это делается при помощи JavaScript-кода, который создает и добавляет теги <link type="text/css"> в заголовок страницы.

Проверка достоверности введенных данных

Почти все Dojo-виджеты приложения (за исключением diji.form.SimpleTextarea) допускают ввод данных пользователем с поддержкой проверки достоверности, поэтому мы выбрали их для реализации формы редактирования Publication. Чтобы восполнить пробел, касающийся текстовой области, мы разработали дополнительный виджет com.ibm.tonkawa.ValidationTextarea для расширения HTML-элемента textarea поддержкой проверки достоверности. В двух словах, - это смесь виджетов dijit.form.ValidationTextBox и dijit.form.SimpleTextarea с обработчиками обратного вызова onBlur() и onFocus(), переопределенными для выполнения проверки достоверности.

При создании XML-сообщения, отправляемого на сервер, определенные символы, такие как меньше (<) или больше (>), снабжаются escape-кодами во избежание появления XSS-уязвимости. Кроме того, эти символы также снабжаются escape-кодами при визуализации их на HTML-странице.


Приложение А. Интеграция с Profiles

Процесс добавления специализированного iWidget в приложение Profiles подробно рассматривается в IBM Lotus Connections Infocenter. Он включает в себя остановку Profiles, проверку конфигурационного файла виджета, дополнение его iWidget-инструкциями определения и размещения, а также регистрацию. В листинге 10 приведен пример файла widgets-config.xml.

Листинг 10. Конфигурация определения и размещения Profiles-виджета
<widgetDef
 defId="dwPublication"
 bundleRefId="tonkawaBundle"
 url="{contextRoot}/../tonkawaWeb/widget/dwPublication.xml?version={version}"/>
…
<widgetInstance uiLocation="col2" defIdRef="dwPublication"/>

При желании можно зарегистрировать пользовательский пакет ресурсов, содержащий локализованные строки для названия iWidget и пунктов меню. Пакет представляет собой заархивированный каталог с файлами свойств, определение которых добавляется в конфигурационный файл Lotus Connections (LotusConnections-config.xml). Пример приведен в листинге 11.

Листинг 11. Определение пакета ресурсов
<resources>
 <!-- Пакет находится в файле ext-i18n-resources/tonkawa.zip -->
 <localZipFile file="tonkawa.zip">
 <bundle bid="tonkawaBundle" name="tonkawa.widgets.resources"/>
 </localZipFile>
…
</resources>

Теперь приложение Profiles можно запустить.


Приложение Б. Плюсы и минусы EJB

Применительно к приложению данной статьи использование EJB-компонентов дает следующие преимущества:

  • Независимость от среды развертывания. Модель программирования EJB изолирует разработчиков от нижележащего ПО промежуточного уровня.
  • Повторное использование EJB-компонентов. Спецификация модели программирования предусматривает необходимые программные интерфейсы для JEE-совместимых контейнеров, позволяя выбрать наиболее подходящее программное обеспечение промежуточного уровня с учетом предъявляемых требований и при минимизации изменений компонентов кода.
  • Интеграция с платформой JEE. Имеются зрелые родственные технологии, такие как Java Database Connectivity (JDBC), Java Message Service (JMS), J2EE Connector Architecture (JCA) и другие, предоставляющие сервисы, которые EJB-код может использовать хорошо определенными способами, описанными в спецификации.
  • Поддержка распределенных транзакций.
  • Прозрачная масштабируемость.

Сложность EJB 2.1 хорошо известна. Например, он требует от разработчиков использования нескольких интерфейсов (локального, удаленного и исходного), кодов поиска JNDI и дескрипторов развертывания XML, которые зависят от специфичных параметров JEE-контейнера. Применительно к приложению данной статьи достоинства, о которых говорилось выше, перевешивают недостатки, связанные со сложностью. Кроме того, многие проблемы сложности решены в EJB-спецификации следующего поколения - EJB 3.0. Поддержка новой спецификации может быть реализована путем установки EJB 3.0 Feature Pack поверх WebSphere 6.1. Поскольку установка Feature Pack не всегда может быть допустима в конкретной среде развертывания Connections, в настоящей статье описывается реализация уровня персистентности данных при помощи EJB 2.1. Однако при использовании определенных приемов, рассмотренных в разделе Уровень доступа к данным, миграцию на EJB 3 можно упростить.


Заключение

В данной статье мы продемонстрировали подход к созданию специализированного REST Web-приложения и его интеграции с IBM Lotus Connections Profiles. Приложение наделяет Profiles возможностью поддерживать список публикаций, относящихся к конкретному пользователю. Оно предоставляет дружественный пользовательский интерфейс, соответствующий сценарию диалога с пользователем в Lotus Connection, и построено на компонентах Dojo Toolkit. Для сохранения целостности данных и предотвращения их разрушения реализованы ограничения безопасности. Кроме того, приложение можно дополнять поддержкой новых объектов без перекомпиляции кода.


Загрузка

ОписаниеИмяРазмер
Пример кода реализацииsource.zip840 КБ

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java, Lotus, XML
ArticleID=768799
ArticleTitle=Организация интеллектуальной совместной деятельности в сфере образования с использованием Lotus Connections: Часть 1. Интеграция Lotus Connections с RESTful Web-приложением
publish-date=10312011