Colaboración más inteligente para la Industria de la Educación utilizando Lotus Connections, Parte 1: Integrar Lotus Connections con una aplicación web RESTful

Realizar publicaciones por persona utilizando Lotus Connections Profiles

Extender las posibilidades de IBM® Lotus® Connections con una aplicación web RESTful que soporte APIs de XML y JSON. La interfaz de usuario de Connections Profiles es un widget personalizado basado en el Dojo Toolkit. La aplicación web permite a un profesor universitario compartir sus publicaciones en una página de perfil de red social. Después personalizar la aplicación para compartir otra información del perfil del profesor tal como premios otorgados por investigación o cursos enseñados.

Vladislav Ponomarev, Software Engineer, IBM

Photo of Vladislav PonomarevVladislav Ponomarev es un líder de equipo de soluciones de tecnología Web en IBM Russian Software and Technology Lab. Sus áreas principales de experiencia incluyen aplicaciones de Java EE, virtualización y computación en nube.



Carl Osipov, Software Engineer, IBM SWG Strategy, IBM

Photo of Carl OsipovCarl Osipov es un arquitecto de software experimentado con la organización de Estrategia y Tecnología en IBM Software Group. Sus habilidades están en el área de la informática distribuida, desarrollo de aplicaciones de voz y entendimiento del lenguaje natural computacional. Ha publicado y presentado temas que incluyen la arquitectura orientada a servicios y la gestión del diálogo de conversación para iguales en la industria y la academia. Actualmente está enfocado en las analíticas y la optimización empresariales.



08-04-2011

Los artículos en la serie describirán casos de uso de colaboración para instituciones de educación superior (como las universidades) y proporcionarán una guía técnica para extender y personalizar IBM Lotus Connections así como otros productos de IBM para mejorar los escenarios colaborativos específicos de la industria de la educación. La serie se enfocará en utilizar analíticas empresariales y posibilidades de optimización de productos de IBM para permitir a los usuarios de Connections tomar decisiones más inteligentes sobre identificar y trabajar con colaboradores potenciales. Esperamos que el material en la serie sea valioso para los líderes y estrategas de la tecnología de la información (TI) de la industria de la educación que están considerando soluciones de colaboración para sus organizaciones.

El primer artículo en la serie describirá cómo integrar IBM Lotus Connections con servicios web basados en REST para permitir extensiones específicas de la industria de la educación para el producto. Los artículos siguientes utilizarán posibilidades extendidas de Connections para soportar procesos optimizados para ayudar a investigadores principales a utilizar la identificación de grupo colaborativo basada en habilidades y analíticas de contenido de publicaciones del investigador para mejorar los perfiles de colaboración y otros temas.

Introducción

La aplicación de IBM Lotus Connections® 2.5 (Connections) Profiles puede ser extendida con widgets personalizados. Este es un proceso relativamente simple cuando la interfaz de usuario (UI) del widget y el código están empaquetados junto con Profiles. Sin embargo, tal vez necesite integrar Connections con una aplicación separada teniendo una API externa, de forma que un widget personalizado de Profiles pueda mostrar y manipular los datos gestionados fuera de Connections.

Siglas más comunes

  • API: Interfaz de Programación de Aplicaciones
  • HTTP: Protocolo de Transferencia de Hipertexto
  • JAXB: Arquitectura Java para Enlace XML
  • JEE: Java Platform, Enterprise Edition
  • JSON: Notación de Objetos de JavaScript
  • REST: Transferencia de Estado Representacional
  • UI: Interfaz de Usuario
  • XML: Lenguaje de Marcas Extensible
  • XSS: Cross-site scripting

En la primera parte del artículo, obtendrá una visión general de las posibilidades y requisitos de la aplicación web. Después, profundizaremos en las tecnologías utilizadas y describiremos la arquitectura de la aplicación. La primera parte concluye con una descripción de implementación que también cubre el mecanismo de seguridad utilizado en la aplicación.

La segunda parte del artículo está dedicada a la implementación del iWidget del lado del cliente. Explicamos a grandes rasgos las posibilidades del widget proporcionadas a un usuario así como la forma en que está organizada la UI del widget y construida con el Dojo Toolkit.

Finalmente, mostraremos cómo los componentes (Profiles, aplicación web y widget) son integrados unos con otros. Proporcionaremos ejemplos de archivos de configuración de Connections para integración.


Aplicación web RESTful

Describiremos una aplicación web que ayuda a un usuario de Profiles (como un profesor en una institución de educación o investigación) a mantener una lista de sus publicaciones creadas. La aplicación expondrá su funcionalidad mediante una interfaz REST basada en HTTP, haciendo más fácil la comunicación con la aplicación desde el navegador utilizando JavaScript o potencialmente desde cualquier otro software de terceros. La aplicación cumplirá con los siguientes requisitos:

  1. La interfaz REST debe:
  • Aceptar mensajes de solicitud con formato de XML y soportar la representación de respuesta en formatos JSON y XML.
  • Proporcionar operaciones de crear, recuperar, actualizar y suprimir (CRUD) en las publicaciones.
  1. Los siguientes datos deben persistir para cada publicación:
  • ID autogenerado
  • Propietario de publicación
  • Título
  • Año de publicación
  • Autor(es)
  • Diario
  • Publicador
  1. No se debe permitir a los usuarios no autenticados la realización de operaciones que modifiquen las publicaciones.
  2. A los usuarios autenticados sólo se les debe permitir modificar publicaciones de su propiedad.
  3. La aplicación debe ser fácilmente extensible para soportar nuevas entidades de REST (además de las publicaciones) sin recompilar su código. Por ejemplo, si la aplicación mantuvo una lista de premios otorgados para cada usuario de Profiles, el código debe mantenerse intacto y las únicas tareas de desarrollo deben ser el ensamble de aplicaciones, el despliegue y los cambios de esquema de la base de datos.

Tecnologías utilizadas

Ya que una instalación de IBM Lotus Connections 2.5 incluye una instancia de IBM WebSphere® Application Server (WebSphere) 6.1, utilizaremos la última para ejecutar nuestra aplicación. WebSphere 6.1 implementa un conjunto de especificaciones J2EE, incluyendo Java™ Servlet 2.4 (JSR 154) y Enterprise JavaBeans™ (EJB) 2.1 (JSR 153).

La aplicación utiliza un servlet compatible con JSR 154 como un punto final de servicio. Como se hizo notar en los requisitos, debemos implementar los métodos doGet(), doPost(), doPut(), y doDelete() para aceptar mensajes XML en el servlet.

Ya que la JAXB habilitada para anotación (estándar para serialización y deserialización de XML) no está disponible en WebSphere 6.1 JRE, utilizamos las bibliotecas Java 6 JAXB para habilitar el soporte de XML en el servlet. Para el soporte JSON, nuestra implementación se basa en la biblioteca JSON4J, enviada como parte de WebSphere Feature Pack for Web 2.0. Para soportar la persistencia, utilizamos beans de entidad JSR 153 que habilitan un modelo de programación de correlación de base de datos relacionales de objeto (ver el Apéndice B: pros y contras de EJB para más detalles).

Visión general de la arquitectura de la aplicación

El diagrama en la Figura 1 ilustra la arquitectura de la aplicación.

Figura 1. Arquitectura de la aplicación
Arquitectura de la aplicación

La UI mostrada en el lado izquierdo de la figura proporciona al usuario controles gráficos de HTML para realizar operaciones de CRUD en publicaciones. Siempre que una operación es desencadenada por una acción de usuario en el navegador web, el JavaScript de la aplicación pasa una solicitud HTTP a la parte de la aplicación del lado del servidor (en el centro de la figura) mediante una interfaz REST. Para asegurar que la UI pueda ser representada en una página de Profiles, se empaca el marcado HTML y el JavaScript en un formato de iWidget compatible con Connections.

La parte central de la figura representa el contenedor JEE que hospeda al servlet, al bean de entidad (EJB) y a los componentes auxiliares. Cuando una solicitud de HTTP es recibida desde el navegador, pasa a través del Filtro de Seguridad, para el método apropiado en el servlet de REST (como el doPost()method). Después, el servlet invoca la operación de CRUD apropiada mediante un objeto façade en el componente de bean de entidad. El façade oculta el código de cliente de la interfaz de granulado fino proporcionada por los beans de entidad, permitiendo al cliente trabajar con Plain Old Java Objects (POJOs) ligeros. Finalmente, el servlet representa la respuesta en el formato apropiado, lo que concluye el proceso de solicitud.

La parte que está en el extremo derecho de la Figura 1 ilustra la tabla de base de datos desplegada para el servidor de bases de datos. El contenedor de JEE y la base de datos son mostrados como separados lógicamente; sin embargo, en la práctica, los dos pueden estar coubicados. Ahora observe la estructura de la base de datos en la Figura 2.

Figura 2. Estructura de la base de datos
Estructura de la base de datos

La tabla PUBLICATION de la base de datos, detallada en la Figura 2, es creada como parte de la base de datos PEOPLEDB de Connections. Una ventaja de tener una base de datos común para Profiles y la aplicación es la habilidad de imponer una restricción clave foránea (ver la Figura 2) en la columna OWNERID de la tabla PUBLICATION, de forma que hace referencia a la columna clave primaria de la tabla EMPLOYEE.

Ahora observe la relación de POJO y EJB, comenzando con la jerarquía de objetos de POJO (ver la Figura 3).

Figura 3. Jerarquía de objetos de dominio
Jerarquía de objetos de dominio

La clase raíz, DomainObject, describe un objeto de dominio teniendo un ID y los métodos de objeto usuario respectivos. La clase descendiente, OwnedDomainObject (parte inferior izquierda en la figura), agrega un campo de ID de propietario. El objeto Publication hereda OwnedDomainObject y agrega campos específicos de publicación. Las clases son anotadas en JAXB, permitiendo la serialización y deserialización de objetos hacia y desde XML.

Observe los beans de entidad en la Figura 4.

Figura 4. Jerarquía de bean de entidad
Jerarquía de bean de entidad

Cada POJO tiene un bean de entidad correspondiente (ver el lado izquierdo de la Figura 4). Más aún, el árbol de herencia de interfaces de beans es similar al de los POJOs (esto es, DomainObject tiene las interfaces DomainLocal y DomainLocalHome correspondientes, y lo mismo para OwnedDomainObject (ver el lado derecho de la Figura 4). Las interfaces de beans están construidas utilizando la capacidad de Java 5 Generics para:

  • Proporcionar código de cliente con un método de fábrica toPojo() utilizado para convertir el bean en un POJO.
  • Definir métodos update() y create() que acepten el POJO como un argumento.

Un objeto façade dedicado esconde los beans de entidad del código de cliente y realiza operaciones CRUD en ellos. El façade (ver la Figura 1) puede soportar entidades arbitrarias, si están construidas de forma similar a la entidad Publication (esto es, si se adhiere a las mismas normas hereditarias), al registrarlas mediante el método registerDomainObject().

En el inicio de la aplicación, el servlet de REST, utilizando parámetros de configuración tomados del archivo web.xml, crea y configura los objetos de adaptador. Esto permite a un Ensamblador de Aplicaciones (un rol de JEE definido por la especificación EJB 2.1, capítulo 3.1.2) habilitar la aplicación para que trabaje con nuevas entidades sin recompilar el código. El objeto de servicio REST es responsable de la correlación de solicitudes HTTP con tipos de entidades particulares.

Capa de acceso de datos

El POJO Publication es implementado de acuerdo a la Figura 3 de la sección previa. Tome en cuenta que los campos de clase son anotados con @XmlElement para hacer el objeto serializable hacia y deserializable desde XML utilizando JAXB.

Los beans de entidad exponen sólo la interfaz local para prevenir el acceso a los beans en la red. En el Listado 1, observe la definición de una interfaz local de entidad:

Listado 1. Interfaces locales
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();
}

Tome en cuenta que la raíz de la jerarquía, la interfaz DomainLocal, prescribe que la entidad debe tener un método de fábrica para convertirse a sí misma en un POJO. También, el método update(), que acepta una instancia de POJO, actúa como un façade para los mutadores de granulado fino (los métodos setXXX()). Se le recomienda cambiar el estado de entidad utilizando este método, de forma que todos los campos sean establecidos en una operación.

Las interfaces iniciales locales imitan la misma jerarquía. Definen métodos para crear y buscar entidades.

Listado 2. Interfaces iniciales locales
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;
}

Utilizado de forma extensiva en todo el código, el dispositivo Generics del lenguaje Java minimiza la necesidad de moldes de clase en el cliente. Sin embargo, utilizar Generics genera algunos problemas:

La especificación EJB limita el tipo de retorno de los métodos create() y findByPrimaryKey() a la interfaz de entidad (ver la línea comentada en el fragmento de código en el Listado 2). Ya que los objetos de implementación son generados por el contenedor de JEE al momento de ejecución, cuando la información no genérica está disponible, usted no puede mover el método create() hacia arriba en la jerarquía y hacerlo genérico como lo hizo con findByOwner().

El dispositivo de borrado de Java Generics esconde el tipo de información de las clases de POJO concretas. Por lo tanto la implementación de los métodos update() y toPojo() de la interfaz local debe depender del ancestro común (esto es, DomainObject).

El Listado 3 muestra la implementación de los métodos.

Listado 3. Implementación de métodos genéricos en el bean 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;
 }
...
}

Ahora que ya ha implementado el EJB, necesita un objeto façade que primero esconda el código de búsqueda de Java Naming and Directory Interface (JNDI) y después permita a los clientes trabajar con entidades de REST (tales como Publication) en términos de las clases de POJO respectivas. El façade debe tener métodos CRUD en su interfaz, como en el Listado 4.

Listado 4. Métodos de interfaz façade
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)

El propósito del último método en la lista es hacer al objeto façade consciente de las nuevas entidades de REST.

Ejemplo de registro de entidad

Así es como la entidad Publication REST es registrada dentro del façade:

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

El objeto façade permite que el código de cliente trabaje con clases concretas de POJO (como Publication) y con clases base (DomainObject o OwnedDomainObject). Sin embargo, existe un obstáculo más: El façade necesita llamar los métodos create() y findByPrimaryKey() que pertenecen a la interfaz inicial concreta (tal como PublicationLocalHome), no a las de base. Para trabajar alrededor de esto, cuando usted registra una nueva entidad de RESST mediante el método registerDomainObject(), el código de cliente debe pasar un objeto adicional que encapsule las llamadas antes mencionadas para las interfaces iniciales, como en el Listado 5.

Listado 5. Implementación de interlocutor muestra
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); 
    }
}

Los parámetros de tipo Generic de LocalInterfaceCaller son sustituidos con interfaces locales concretas e iniciales locales que proporcionan los métodos create() y findByPrimaryKey().

Además de esconder el código relacionado con EJB, el objeto façade (en conjunción con la jerarquía de objetos de dominio) tiene otra ventaja: Puede simplificar la migración a EJB 3. POJO se convertirá en anotado de JPA (Java Persistence API), convirtiéndose en beans de entidad, y la implementación de façade será cambiada para reflejar la siguiente versión de la especificación de EJB. Tome en cuenta que el código de cliente no es afectado.

Figura 5. Diagrama de clase del objeto façade
Diagrama de clase del objeto faade

Para la implementación del objeto façade, refiérase al código de origen de muestra. Puede encontrar el enlace en Downloads.

Mecanismo de seguridad

Para entender la implementación de seguridad, recapitulemos de cuáles amenazas desea proteger la aplicación.

  1. Las operaciones de modificación (expresadas por verbos HTTP POST, PUT y DELETE) sólo deben ser permitidas para usuarios autenticados.
  2. A los usuarios autenticados sólo se les debe permitir modificar publicaciones de su propiedad.

El mecanismo de autenticación utilizado en Lotus Connections descansa en un token de Lightweight Third-Party Authentication (LTPA) realizado junto con cookies con cada solicitud de HTTP. Ya que la UI de la aplicación está integrada en Profiles, puede esperar que el mismo token esté presente en una solicitud que llegue a su servlet de REST. WebSphere Application Server detecta la identidad del usuario con base en el token, así que la aplicación verifica la autenticación al llamar al método getRemoteUser() definido en la clase HttpServletRequest. Si el usuario es autenticado, este método retorna el nombre de inicio de sesión del usuario. De lo contrario, esta llamada retorna nula.

Para implementar el mecanismo de seguridad, utilizamos un filtro en frente del servlet para interceptar cada solicitud (ver la Figura 6). El filtro determina si un usuario es autenticado o no, como se describió anteriormente. Si no, y el método de HTTP es distinto a GET, el proceso de solicitud es terminado y se retorna el estado 401 (no autorizado) de HTTP. Si el método de HTTP es GET, el flujo de control es transferido al servlet. Finalmente, si un usuario autenticado está a punto de realizar una operación de modificación, el filtro recupera el nombre de inicio de sesión del usuario, consulta la base de datos para obtener un identificador de Connections del usuario y lo guarda en la memoria caché en el objeto de sesión. Una vez que un usuario es autenticado, el filtro no hace nada con cada llamada subsiguiente, por lo que el identificador es recuperado sólo una vez.

Figura 6. Filtrado de autenticación
Filtrado de autenticación

Cuando una solicitud de HTTP para una operación de modificación alcanza el servlet, el identificador de usuario es guardado en la memoria caché en el objeto de sesión, de forma que usted pueda comparar ese identificador con el ID de propietario de la entidad de REST objetivo (en este caso, Publication). Para encapsular esta verificación de autorización, desarrollamos un adaptador sobre el objeto façade descrito anteriormente. El adaptador posee una referencia para el façade. Este último es declarado con métodos públicos imitando métodos de CRUD definidos en el adaptador pero con un identificador de usuario como un argumento adicional. El identificador del propietario de la entidad de REST es comparado con el identificador de usuario, y si no coinciden, una excepción de autorización es arrojada.

Soporte de XML y JSON

El soporte de XML es habilitado en la aplicación lista para usar, siempre que las bibliotecas de JAXB estén en la ruta de acceso de clases y los objetos de POJO estén anotados. El proceso es tan fácil como registrar clases de dominio dentro de un objeto JAXBContext y utilizar sus métodos de fábrica, createMarshaller() y createUnmarshaller(), para obtener objetos de dominio con capacidad para serialización y deserialización de XML.

Para habilitar el soporte JSON, hemos utilizado la biblioteca JSON4J enviada como parte de WebSphere Feature Pack for Web 2.0. La salida en formato JSON es ajustada para cumplir con los requisitos del componente dojo.data.ItemFileReadStore: El objeto retornado especifica los atributos identifier, label, yitems.

El servlet acepta mensajes de XML y representa una respuesta en formato XML o JSON, dependiendo de la cabecera Accept de la solicitud de HTTP o del parámetro de solicitud as. Este último tiene prioridad sobre la cabecera. Esta lógica es encapsulada en un método de fábrica dedicado del servlet que construye un representador de XML o JSON responsable de la serialización del objeto. Ver la Figura 7 a continuación.

Figura 7. Diagrama de clase de representadores
Diagrama de clase de representadores

Implementación del servlet de REST

El ciclo de vida del servlet de REST comienza con el método init() que maneja la inicialización del servlet con base en parámetros definidos en web.xml.

El parámetro de inicialización CrudConfigurators contiene una lista de clases separadas por comas que implementan la interfaz CrudServiceConfigurator. Se requiere que estos objetos tengan un constructor sin argumentos. Después de la instalación, el método configure() para cada objeto, que acepta una instancia façade de CRUD, es llamado. Cada configurador registra un conjunto de entidades de REST dentro del façade.

De forma similar, el parámetro restConfigurators almacena una lista de implementaciones RestServiceConfiguration separadas por comas. El proceso es el mismo que para crudConfigurators. El objeto RestService es esencialmente responsable por establecer una correspondencia entre una vía de acceso de solicitud y una clase de dominio particular. Por ejemplo, la solicitud DELETE /rest/publication/123 debe ser ejecutada contra la entidad Publication. Cada configurador registra su propio conjunto de entidades de REST dentro del objeto RestService.

Este mecanismo de configuración y el objeto façade que cubrimos antes hacen que sea fácil agregar soporte para entidades arbitrarias que siguen el enfoque de diseño de Publication sin recompilar el código. Para cada nueva entidad, un Ensamblador de Aplicaciones sólo tiene que agregar nuevos objetos de configurador para parámetros de inicialización de servlet en el descriptor de despliegue y empaquetarlos junto con clases de dominio en la aplicación web.

Una vez que el web.xml ha sido configurado, el servlet está listo para manejar solicitudes de HTTP. La Tabla 1 a continuación resume la API de REST proporcionada por el servlet. Para ser breves, la parte <host>:<port> del URL ha sido omitida.

Tabla 1. API de REST del servlet
Método HTTPURIParámetros
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}]

Después de realizar el proceso con éxito, el cuerpo de respuesta para una solicitud GET contendrá una lista de publicaciones, la respuesta para una POST contiene una única publicación (la que está almacenada en la base de datos) y la respuesta para una PUT y DELETE contiene sólo un mensaje de confirmación. Si una excepción ocurre, el cuerpo de respuesta contendrá un código de estado HTTP y un mensaje de error opcional en un formato de representación apropiado.

El Listado 6 muestra un ejemplo de una solicitud HTTP.

Listado 6. Solicitud POST de HTTP de muestra
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>

El diagrama de secuencia en la Figura 8 ilustra el proceso de solicitud, donde los componentes están conectados juntos. (Ver una versión más grande de la Figura 8.)

Figura 8. Proceso de POST de HTTP
Proceso de POST de HTTP

iWidget

La interfaz de usuario de la aplicación es implementada por un componente iWidget 1.0 incluido en el UI de Profiles. La interfaz de usuario depende de la biblioteca Dojo Toolkit JavaScript versión 1.2.3 que es enviada junto con Lotus Connections 2.5. El widget de publicaciones proporciona las siguientes posibilidades:

  • Ver la lista de publicación de una página de perfil abierta para un usuario de Connections.
  • Crear o actualizar una publicación, mientras se hacen cumplir las normas de validación del lado del cliente.
  • Remover una publicación.

Los botones de edición de iWidget están disponibles sólo cuando un usuario está viendo su propio perfil.

Estructura de la UI

La UI del iWidget está compuesta de un contenedor de pila Dojo con 3 paneles. El primer panel presenta al usuario con una lista de publicaciones formateada de sólo lectura (ver la Figura 9a o una versión más grande de la Figura 9a).

Figura 9a. Lista de publicaciones
Figura 9a. Lista de publicaciones

Si un usuario está viendo su perfil, entonces es posible cambiarse al siguiente panel donde una publicación individual puede ser seleccionada para edición (ver la Figura 9b o una versión más grande de la Figura 9b).

Figura 9b. Detalles de publicación
Figura 9b. Detalles de publicación

El panel incluye los botones Add, Edit y Remove. Haga clic en los botones Add o Edit para mostrar la edición del panel con los campos de publicación Título, Enlace, Autor(es), Diario, Publicador, Año y Seleccionado (ver la Figura 9c o una versión más grande de la Figura 9c.).

Figura 9c. Editar la publicación
Figura 9c. Editar la publicación

Los controles de entrada del formulario aplican normas de validación básicas (ver la sección Validación de entrada anterior para más detalles).

Implementación

De acuerdo con la especificación, un iWidget consiste en una clase de JavaScript (en este caso, la clase DwPublication) con una definición de manejadores de devolución de llamada y un archivo XML que contiene el marcado de HTML.

La Figura 10 muestra cómo los componentes de la UI interactúan entre ellos y con el servidor. (Ver una versión más grande de la Figura 10.)

Figura 10. Interacción de los componentes de iWidget
Interacción de los componentes de iWidget

Tras la primera carga del iWidget de publicaciones, sus manejadores de devolución de llamada onLoad() son ejecutados (Paso 1, Figura 10) y después su marcado de HTML es analizado por Dojo, de forma que el widget es representado en el navegador. Después, la clase DwPublication capta una lista de publicaciones al instanciar un objeto de almacenamiento de datos Dojo (dojo.data.ItemFileWriteStore) y lo instruye para consultar una lista de publicaciones del servidor (2). Se pasa una función de devolución de llamada al almacén de datos que construye nodos de DOM respectivos para mostrar una lista de solo lectura (2.1). Cuando un usuario hace clic en el enlace Edit publications, la clase DwPublication construye un objeto DataGrid nuevo y muestra el panel de edición donde mostrar la cuadrícula (3). La cuadrícula es llenada por contenido del almacén de datos creado en el paso (2).

El panel de edición tiene los botones Create, Edit y Remove. Si se hace clic en alguno de los primeros dos, se muestra al usuario un formulario de edición para llenar los datos. Cuando el formulario es enviado, el flujo de control es transferido de regreso a DwPublication lo que envía una solicitud HTTP de POST o PUT para crear o actualizar la publicación.

Alternativamente, si se hace clic en el botón Remove, DwPublication envía una solicitud HTTP de DELETE para suprimir una publicación de la base de datos.

Nuestra implementación de iWidget asegura el correcto comportamiento de los dispositivos de renovar y maximizar habilitados por Lotus Connections. Cuando toda la página web de Profiles está cargada o recargada y un iWidget está en un estado maximizado, el manejador de eventos onLoad() del cuerpo de HTML agregado por DOJO automáticamente analiza el marcado de HTML en la página para construir los widgets de Dojo. En contraste, cuando un iWidget es renovado mediante su menú de contexto (o maximizado después de que la página fue cargada con un iWidget minimizado), entonces el árbol de DOM del iWidget es destruido y reconstruido con base en el archivo XML dado como parte la definición de iWidget. El análisis no ocurre en este caso y debe ser llamado automáticamente. También, los widgets de Dojo creados antes de los eventos de renovación o maximización deben ser destruidos para prevenir fugas de memoria.

La Figura 11 muestra la diferencia en estos flujos. Los manejadores de devolución de llamada onLoad() y onUnload() en la clase DwPublication son invocados cuando los widgets antiguos deben ser limpiados y el marcado de HTML debe ser analizado nuevamente.

Figura 11. Comportamiento de (re)carga del iWidget
Comportamiento de (re)carga del iWidget

Soporte de localización

Para habilitar la localización completa de la UI, implementamos un número de widgets de Dojo personalizados diseñados para establecer la propiedad innerHTML de un nodo particular para una secuencia localizada tomada de un paquete de recursos. Primero, usted desarrolla una mezcla que soporte una propiedad label (ver el Listado 7).

Listado 7. Mezcla con una propiedad '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;
 }
 }
);

Con la mezcla en su lugar, usted puede agregar la propiedad label a un widget de Dojo recientemente declarado al especificar i18nInnerHtmlMixin en la lista de ancestros (ver el fragmento de código en el Listado 8 ). La implementación predeterminada del método buildRendering() puede ser alterada temporalmente para poner un valor de propiedad label en cualquier nodo de DOM que sea requerido.

Listado 8. Widget 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: ""
 }
);

Finalmente, en el marcado de HTML, el widget de Dojo localizado es utilizado como en el Listado 9:

Listado 9. Declaración de widget localizado en el marcado de 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"/>

En este caso, la serie de recursos labelYearPublished es tomada de un paquete de recursos y establecida como un valor de la propiedad label del widget personalizado.

Profiles y Dojo

Utilizamos algunos trucos de implementación para hacer que ciertos controles de Dojo funcionaran con Profiles. Primero, debemos poner los contenedores entro de un bloque <div> con un atributo id definido. Después, para hacer que dojox.grid.DataGrid se muestre correctamente, debemos cargar una colección de archivos de CSS antes de que el marcado sea analizado. Esto se consigue con código JavaScript que construye y añade nodos <link type=”text/css”> a la cabecera de página.

Validación de entrada

Casi todos los widgets de Dojo de las aplicaciones (excepto diji.form.SimpleTextarea) aceptan posibilidades de validación de soporte de entrada de usuario y es por ello que los elegimos para implementar el formulario de edición de Publication. Para llenar la brecha con el área de texto, desarrollamos un widget adicional, com.ibm.tonkawa.ValidationTextarea, Para mejorar el elemento textarea de HTML con soporte de validación. En pocas palabras, es una mezcla de los widgets dijit.form.ValidationTextBox y dijit.form.SimpleTextarea con los manejadores de devolución de llamada onBlur() y onFocus() alterados temporalmente para realizar la validación.

Al construir un mensaje de XML para enviar al servidor, algunos caracteres como los menores que (<) o los mayores que (>) son escapados para prevenir inyecciones de XSS. De forma similar, estos caracteres también son escapados al representarlos en la página de HTML.


Apéndice A: Integración con Profiles

El proceso de agregar el iWidget personalizado en la aplicación Profiles está completamente cubierto en IBM Lotus Connections Infocenter. Involucra detener Profiles, revisar el archivo de configuración del widget, aumentarlo con la definición de iWidget y las instrucciones de colocación y verificarlo. El Listado 10 muestra un ejemplo del archivo widgets-config.xml.

Listado 10. Definición del widget de Profiles y configuración de colocación
<widgetDef
 defId="dwPublication"
 bundleRefId="tonkawaBundle"
 url="{contextRoot}/../tonkawaWeb/widget/dwPublication.xml?version={version}"/>
…
<widgetInstance uiLocation="col2" defIdRef="dwPublication"/>

Opcionalmente, puede registrar un paquete de recursos personalizado para proporcionar secuencias de localización para el título del iWidget y los elementos del menú. Un paquete es un directorio comprimido con archivos de propiedades cuya definición está agregada al archivo de configuración de Lotus Connections (LotusConnections-config.xml). Para un ejemplo, vea el Listado 11.

Listado 11. Definición del paquete de recursos
<resources>
 <!-- Bundle is located in the ext-i18n-resources/tonkawa.zip file -->
 <localZipFile file="tonkawa.zip">
 <bundle bid="tonkawaBundle" name="tonkawa.widgets.resources"/>
 </localZipFile>
…
</resources>

Finalmente, la aplicación de Profiles inicia.


Apéndice B: Pros y contras de EJB

En el caso de la aplicación en este artículo, el uso de EJBs ofrece las siguientes ventajas:

  • Independencia del entorno de despliegue. El modelo de programación de EJB aísla a los desarrolladores del middleware subyacente.
  • Reusabilidad de los componentes de EJB. La especificación del modelo de programación prescribe la API requerida para contenedores compatibles con JEE, permitiendo que se elija el middleware más adecuado dados los requisitos mientras se minimizan los cambios de código de componente.
  • Integración con la plataforma JEE. Las tecnologías maduras relacionadas, tales como Java Database Connectivity (JDBC), Java Message Service (JMS), J2EE Connector architecture (JCA), etc., proporcionan servicios que el código de EJB puede aprovechar de forma bien definida descrita por las especificaciones;
  • Soporte de transacción distribuida.
  • Escalabilidad transparente.

La complejidad de EJB 2.1 es bien conocida. Por ejemplo, carga a un desarrollador con diversas interfaces (local, remota e inicio), código de búsqueda de JNDI y descriptores de despliegue de XML que dependen de parámetros específicos de contenedor de JEE. Para la aplicación en este artículo, los méritos resaltados anteriormente superan los inconvenientes debidos a la complejidad. También, la siguiente generación de la especificación de EJB, EJB 3.0, atiende muchos de los problemas de complejidad. El soporte para la nueva especificación puede ser habilitado al instalar EJB 3.0 Feature Pack sobre WebSphere 6.1. Ya que puede no ser aceptable instalar el paquete de dispositivos en todos los entornos de despliegue de Connections, este artículo describe EJB 2.1 para la persistencia de la implementación de capa. Sin embargo, al utilizar ciertas técnicas cubiertas en la sección Capa de acceso de datos, la migración a EJB 3 puede ser simplificada.


Conclusión

En este artículo, mostramos un enfoque para crear una aplicación web de REST personalizada e integrarla con IBM Lotus Connections Profiles. La aplicación extiende a Profiles con la habilidad de mantener una lista de publicaciones que pertenecen a un usuario particular. Proporciona una UI fácil de usar alineada con el aspecto visual de Lotus Connections y está construida con base en los componentes de Dojo Toolkit. Las restricciones de seguridad son aplicadas para mantener los datos consistentes y prevenirlos de la corrupción. Más aún, la aplicación es extensible para soportar nuevas entidades sin recompilar el código.


Descargar

DescripciónNombretamaño
Sample implementation codesource.zip840KB

Recursos

Aprender

Obtener los productos y tecnologías

Comentar

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=tecnologia Java, Lotus
ArticleID=645394
ArticleTitle=Colaboración más inteligente para la Industria de la Educación utilizando Lotus Connections, Parte 1: Integrar Lotus Connections con una aplicación web RESTful
publish-date=04082011