Colaboração Mais Inteligente para o Segmento de Mercado de Educação Usando o Lotus Connections, Parte 1: Integre o Lotus Connections a um aplicativo da web com REST

Controle as publicações por pessoa usando os perfis do Lotus Connections

Amplie os recursos do IBM® O Lotus® Connections com um aplicativo da web com REST que suporta APIs de XML e JSON. A interface com o usuário Connections Profiles é um widget customizado baseado no kit de ferramentas Dojo. O aplicativo da web permite que um professor universitário compartilhe suas publicações em uma página de perfil em uma rede social. Customize ainda mais o aplicativo para compartilhar outras informações do perfil do professor, como verbas para pesquisa que recebeu ou os cursos que ele lecionou.

Vladislav Ponomarev, Software Engineer, IBM

Vladislav Ponomarev é líder da equipe de soluções de tecnologia da web no laboratório russo de Software e Tecnologia IBM. Suas principais áreas de conhecimento incluem aplicativos Java EE, virtualização e computação em nuvem.



Carl Osipov, Software Architect, IBM

Carl Osipov é um experiente arquiteto de software da organização de estratégia e tecnologia do IBM Software Group. Suas qualificações estão relacionadas às áreas de computação distribuída, desenvolvimento de aplicativos de fala e entendimento do idioma natural por computadores. Tem publicações e apresentações sobre tópicos como Arquitetura Orientada a Serviços e gerenciamento de diálogo conversacional para colegas do segmento de mercado e da academia. Seu foco atual é a análise de negócios e a otimização.



31/Ago/2012

Os artigos da série descreverão casos de uso de colaboração para instituições de ensino superior (como faculdades e universidades) e fornecerão orientação técnica sobre a extensão e customização do IBM Lotus Connections e de outros produtos IBM para melhorar os cenários colaborativos específicos para o segmento de mercado de ensino superior. A série focará o uso dos recursos de análise de negócios e otimização de produtos IBM para possibilitar que os usuários do Connections tomem decisões melhores sobre a identificação de possíveis colaboradores e o trabalho com eles. Esperamos que o material da série seja importante para os líderes e estrategistas de tecnologia da informação (TI) para o segmento de mercado de educação que estão considerando a possibilidade de usar soluções de colaboração em suas organizações.

O primeiro artigo da série mostrará como integrar o IBM Lotus Connections a serviços da web baseados em REST para possibilitar extensões do produto específicas para o segmento de mercado de educação. Os artigos de acompanhamento usarão recursos estendidos do Connections para suportar processos otimizados a fim de ajudar os diretores pesquisadores a usar a identificação de grupos baseada nas qualificações e a analítica do conteúdo das publicações dos pesquisadores para melhorar os perfis de colaboração e outros tópicos.

Introdução

O aplicativo IBM Lotus Connections® 2.5 (Connections) Profiles pode ser estendido com widgets customizados. Esse processo é relativamente simples quando a interface com o usuário (UI) e o código do widget são empacotados junto com o Profiles. No entanto, talvez seja necessário integrar o Connections a um aplicativo separado que tem uma API externa para que o widget do Profiles customizado possa exibir e manipular os dados gerenciados fora do Connections.

Acrônimos usados frequentemente

  • API: Interface de programação de aplicativos
  • HTTP: Protocolo de Transporte de Hipertexto
  • JAXB: Arquitetura Java para Ligação XML
  • JEE: Plataforma Java, Enterprise Edition
  • JSON: JavaScript Object Notation
  • REST: Representational State Transfer
  • UI: Interface com o usuário
  • XML: linguagem de marcação extensível
  • XSS: Cross-site scripting

Na primeira parte deste artigo, apresentamos uma visão geral dos recursos e requisitos dos aplicativos da web. Em seguida, detalhamos as tecnologias utilizadas e descrevemos a arquitetura do aplicativo. A primeira parte termina com uma descrição de implementação que também aborda o mecanismo de segurança usado no aplicativo.

A segunda parte do artigo trata da implementação do widget no lado do cliente. Explicamos o contexto geral dos recursos do widget fornecidos para o usuário e como a UI do widget é organizada e criada com o kit de ferramentas Dojo.

Para concluir, mostramos como os componentes (Profiles, aplicativo da web e widget) são integrados entre si. Damos exemplos de arquivos de configuração do Connections para integração.


Aplicativo da web com REST

Descreveremos um aplicativo da web que ajuda o usuário do Profiles (como um professor de uma instituição de ensino ou pesquisa) a manter uma lista das publicações de sua autoria. O aplicativo exporá a funcionalidade por meio de uma interface de REST baseada em HTTP, facilitando a comunicação com o aplicativo a partir do navegador usando o JavaScript ou, possivelmente, a partir de qualquer outro software de terceiros. O aplicativo cumprirá os seguintes requisitos:

  1. A interface de REST deve:
  • Aceitar mensagens de pedido formatadas como XML e suportar a renderização de respostas nos formatos JSON e XML.
  • Fornecer operações de criação, recuperação, atualização e exclusão (CRUD) nas publicações.
  1. Os seguintes dados devem persistir em cada publicação:
  • ID gerado automaticamente
  • Proprietário da publicação
  • Título
  • Ano de publicação
  • Autor(es)
  • Journal
  • Publicador
  1. Os usuários não autenticados não podem ter permissão para realizar operações que modificam as publicações.
  2. Os usuários autenticados só podem ter permissão para modificar as publicações das quais são os proprietários.
  3. O aplicativo deve ser extensível facilmente para suportar novas entidades de REST (além das publicações) sem recompilar o código. Por exemplo: se o aplicativo mantinha uma lista de verbas oferecidas a cada usuário do Profiles, o código deve permanecer do jeito que está, e as únicas tarefas de desenvolvimento devem ser a montagem de aplicativo, implementação e mudanças no esquema do banco de dados.

Tecnologias utilizadas

Já que a instalação do IBM Lotus Connections 2.5 incluir uma instância do IBM WebSphere® Application Server (WebSphere) 6.1, usaremos esse programa para executar o nosso aplicativo. O WebSphere 6.1 implementa um conjunto de especificações J2EE, incluindo Java™ Servlet 2.4 (JSR 154) e Enterprise JavaBeans™ (EJB) 2.1 (JSR 153).

O aplicativo usa um servlet compatível com JSR 154 como terminal de serviço. Conforme o que foi mencionado nos requisitos, é necessário implementar os métodos doGet(), doPost(), doPut() e doDelete() para aceitar mensagens no servlet.

Já que o JAXB habilitado para anotação (padrão para serialização e desserialização XML) não está disponível no WebSphere 6.1 JRE, usamos as bibliotecas de JAXB do Java 6 para habilitar o suporte a XML no servlet. No caso do suporte a JSON, a nossa implementação usa a biblioteca JSON4J, que vem com o WebSphere Feature Pack para Web 2.0. Para suportar a persistência, usamos os beans de entidade JSR 153, que possibilitam um modelo de programação de mapeamento de banco de dados relacionais de objetos (consulte o Apêndice B: Prós e contras do EJB para ver os detalhes).

Visão geral da arquitetura do aplicativo

O diagrama da Figura 1 ilustra a arquitetura do aplicativo.

Figura 1. Arquitetura do aplicativo
Arquitetura do aplicativo

A UI mostrada à esquerda da figura fornece ao usuário controles gráficos de HTML para realizar operações de CRUD nas publicações. Sempre que uma operação é acionada por uma ação do usuário no navegador da web, o JavaScript do aplicativo passa um pedido de HTTP para a parte do aplicativo que é o lado do servidor (meio da figura) por meio de uma interface de REST. Para assegurar que a UI possa ser renderizada em uma página do Profiles, a marcação HTML e o JavaScript são empacotados em um formato de iWidget compatível com o Connections.

A parte central da figura representa o contêiner de JEE que hospeda o servlet, o bean de entidade (EJB) e os componentes auxiliares. Quando um pedido de HTTP proveniente do navegador é recebido, ele passa pelo filtro de segurança e vai para o método adequado no servlet de REST (como o métododoPost()). Em seguida, o servlet chama a operação de CRUD por meio de um objeto deçfachada no componente de bean de entidade. A ç fachada oculta o código do cliente da interface de granularidade fina fornecida pelos beans de entidade, permitindo que o cliente trabalhe com Plain Old Java Objects (POJOs) leves. Finalmente, o servlet renderiza a resposta no formato adequado, o que conclui o processamento de pedidos.

A parte mais à direita da Figura 1 descreve a tabela de banco de dados implementada no servidor de banco de dados. O contêiner de JEE e o banco de dados são exibidos como se estivessem separados em termos lógicos, entretanto, na prática, os dois podem estar no mesmo local. Agora, veja a estrutura de banco de dados na Figura 2.

Figura 2. Estrutura do banco de dados
Estrutura do banco de dados

A tabela de banco de dadosPUBLICATION detalhada na Figura 2, é criada como parte do banco de dados PEOPLEDB . Uma das vantagens de ter um banco de dados comum para o Profiles e o aplicativo é a capacidade de impor uma restrição a chaves estrangeiras (veja a Figura 2) na coluna OWNERID da tabela PUBLICATION para que faça referência à coluna-chave principal da tabela EMPLOYEE .

Agora observe a relação entre POJO e EJB, começando pela hierarquia dos objetos POJO (veja a Figura 3).

Figura 3. Hierarquia dos objetos de domínio
Hierarquia dos objetos de domínio

A classe-raiz, DomainObject, descreve um objeto de domínio que tem um ID e os respectivos métodos de acessador. A classe descendente, OwnedDomainObject (parte inferior esquerda da figura), adiciona um campo de ID do proprietário. O objetoPublication herda o OwnedDomainObject). e adiciona campos específicos da publicação. As classes são anotadas com JAXB, possibilitando a serialização e desserialização de objetos de/para XML.

Veja os beans de entidade na Figura 4.

Figura 4. Hierarquia dos beans de entidade
Hierarquia dos beans de entidade

Cada POJO tem um bean de entidade correspondente (veja o lado esquerdo da Figura 4). Além disso, a árvore de herança de interface é semelhante à dos POJOs (ou seja, o DomainObject tem interfaces correspondentes de DomainLocal e DomainLocalHome , e acontece o mesmo com o OwnedDomainObject). (veja o lado direito da Figura 4). As interfaces de bean são criadas usando o recurso Generics do Java 5 para:

  • Fornecer ao código do cliente um factory methodtoPojo() usado para converter o bean para POJO.
  • Defina interface de dados e create() que aceitam o POJO como argumento.

Um objeto de fachada çdedicado oculta os beans de entidade do código do cliente e realiza operações de CRUD neles. A çfachada (veja a Figura 1) pode suportar entidades arbitrárias se elas são criadas de forma semelhante à entidade Publication (ou seja, cumprem as mesmas regras de herança), registrando-as por meio do método registerDomainObject() .

Na inicialização do aplicativo, o servlet de REST, usando os parâmetros de configuração tomados do arquivo web.xml , cria e configura os objetos de adaptador. Isso permite que um assembler de aplicativos (uma atribuição do JEE definida pela especificação EJB 2.1, capítulo 3.1.2) habilite o aplicativo para funcionar com novas entidades sem recompilar o código. O objeto de serviço REST é responsável pelo mapeamento dos pedidos de HTTP para tipos de entidades específicos.

Camada de acesso a dados

O POJO Publication é implementado de acordo com a Figura 3 da seção anterior. Observe que os campos de classe são anotados com @XmlElement para tornar o objeto serializável para/desserializável do XML usando JAXB.

Os beans de entidade expõem somente a interface local para impedir o acesso aos beans pela rede. Na Listagem 1, veja a definição de uma interface local de entidade:

Lista 1. Interfaces locais
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();
}

Observe que a raiz da hierarquia, a interface DomainLocal , prescreve que a entidade deve ter um factory method para se converter para um POJO. Além disso, o pacote interface de dados , que aceita uma instância de POJO, age como uma fachadaçpara os mutadores de granularidade fina (os métodos setXXX() ). É recomendável alterar o estado da entidade usando esse método para que todos os campos sejam configurados em uma operação.

As interfaces iniciais locais espelham a mesma hierarquia. Elas definem os métodos para criar e procurar entidades.

Lista 2. Interfaces iniciais locais
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;
}

Usado amplamente em todo o código, o recurso Generics da linguagem Java minimiza a necessidade de casts de classe no cliente. Entretanto, o uso de Generics causa alguns problemas:

A especificação EJB restringe o tipo de retorno dos métodos create() e findByPrimaryKey() para a interface de entidade (consulte a linha de comentário no fragmento de código da Listagem 2). Já que os objetos de implementação são gerados pelo contêiner de JEE no tempo de execução, quando não há informações genéricas disponíveis, não é possível mover o método create() para cima na hierarquia e torná-lo genérico como você fez com o findByOwner().

O recurso de apagamento do Generics de Java oculta o tipo de informações das classes concretas de POJO. Portanto, a implementação dos métodos interface de dados e toPojo() da interface local deve depender do ancestral comum (ou seja, DomainObject).

A Listagem 3 mostra a implementação dos métodos.

Lista 3. Implementação de métodos genéricos no 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;
 }
...
}

Agora que você implementou o EJB, é necessário um objeto de fachadaç que primeiro oculte o código de consulta da Java Naming and Directory Interface (JNDI) e, em segundo lugar, permita que os clientes trabalhem com entidades de REST (como Publication) em termos das respectivas classes de POJO. A fachadaç deve ter métodos de CRUD na interface, como na Listagem 4.

Lista 4. Métodos da çinterface de fachada
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)

O propósito do último método da lista é tornar o objeto de çfachada consciente das novas entidades de REST.

Exemplo de registro de entidade

Veja como a entidade de REST Publication é registrada dentro daçfachada:

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

O çobjeto de fachada permite que o código do cliente trabalhe com as classes concretas de POJO (como Publication) e com as classes base (DomainObject ouOwnedDomainObject).). No entanto, há mais um obstáculo: a çfachada precisa chamar os métodos create() e findByPrimaryKey() que fazem parte da interface inicial concreta (como PublicationLocalHome), não os de base. Para resolver isso, ao registrar uma nova entidade de REST por meio do método registerDomainObject() , o código do cliente deve passar um objeto adicional que encapsula as chamadas mencionadas anteriormente nas interfaces iniciais, como na Listagem 5.

Lista 5. Exemplo de implementação do responsável pela chamada
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); 
    }
}

Os parâmetros de tipo genérico de LocalInterfaceCaller são substituídos por interfaces iniciais locais e interfaces concretas locais que fornecem os métodos create() e findByPrimaryKey() .

Além de ocultar o código relacionado com EJB, o objeto de çfachada (em conjunto com a hierarquia dos objetos de domínio) tem outra vantagem: pode simplificar a migração para o EJB 3. O POJO se tornará anotado por JPA (Java Persistence API), transformando-se em beans de entidade, e a çimplementação da fachada será alterada para refletir a especificação de EJB. Observe que o código do cliente não é afetado.

Figura 5. çDiagrama de classe do objeto de fachada
Diagrama de classe do objeto de fachada

Para a implementação do objeto deçfachada, consulte o exemplo de código de origem. Você encontra o link em Downloads.

Mecanismo de segurança

Para entender a implementação de segurança, vamos recapitular as ameaças contra as quais é necessário proteger o aplicativo.

  1. Somente os usuários autenticados devem ter permissão para fazer operações de modificação (expressas pelos verbos de HTTP POST, PUT, e DELETE).
  2. Os usuários autenticados só devem ter permissão para modificar as publicações que são de sua propriedade.

O mecanismo de autenticação usado no Lotus Connections depende de um token de Lightweight Third-Party Authentication (LTPA) transportado junto com os cookies com cada pedido de HTTP. Já que a UI do aplicativo está integrada ao Profiles, pode-se esperar que o mesmo token esteja presente em um pedido que vem para o servlet de REST. O WebSphere Application Server detecta a identidade do usuário com base no token; portanto, o aplicativo verifica a autenticação chamando o método getRemoteUser() definido na classe HttpServletRequest . Se o usuário é autenticado, esse método retorna o nome de login dele. Do contrário, essa chamada retorna "null".

Para implementar o mecanismo de segurança, usamos um filtro na frente do servlet para interceptar cada pedido (veja a A Figura 6). O filtro determina se o usuário é autenticado ou não, conforme o descrito acima. Se não é, e o método de HTTP é diferente de GET, o processamento do pedido é terminado e o status de HTTP status 401 (desautorizado) é retornado. Se o método de HTTP é GET, o fluxo de controle é transferido para o servlet. Finalmente, se um usuário autenticado está prestes a realizar uma operação de modificação, o filtro recupera o nome de login do usuário, consulta o banco de dados para obter o identificador do usuário no Connections e o armazena em cache no objeto de sessão. Depois que o usuário é autenticado, o filtro não faz nada em cada chamada subsequente; portanto, o Identificador só é recuperado uma vez.

Figura 6. Filtragem de autenticação
Filtragem de autenticação

Quando um pedido de HTTP referente a uma operação de modificação chega ao servlet, o identificador de usuários é armazenado em cache no objeto de sessão, para que você possa comparar esse identificador com o ID do proprietário da entidade de REST de destino (nesse caso, Publication). Para encapsular essa verificação de autorização, desenvolvemos um adaptador por cima do objeto deçfachada descrito acima. O adaptador contém uma referência à çfachada. Essa referência é declarada com métodos públicos que espelham os métodos de CRUD definidos no adaptador, mas com um identificador de usuários como argumento adicional. O identificador do proprietário da entidade de REST é comparado ao identificador de usuários e, se não correspondem, ocorre uma exceção de autorização.

Suporte a XML e JSON

O suporte a XML é habilitado de fábrica no aplicativo, desde que as bibliotecas de JAXB estejam no caminho da classe e os objetos de POJO estejam anotados. O processo é tão fácil quanto registrar classes de domínio dentro de um objeto de JAXBContext e usar os seus factory methods, createMarshaller() e createUnmarshaller() para obter objetos de domínio com capacidade de serialização e desserialização de XML.

Para habilitar o suporte a JSON, usamos a biblioteca JSON4J que vem com o WebSphere Feature Pack para Web 2.0. A saída no formato JSON é ajustada para cumprir os requisitos do componente dojo.data.ItemFileReadStore : o objeto retornado especifica os atributos de identificador, etiqueta e itens .

O servlet aceita mensagens XML e renderiza uma resposta no formato XML ou JSON, dependendo do cabeçalho Accept do pedido de HTTP ou do parâmetro de pedido como . Esse parâmetro tem precedência em relação ao cabeçalho. Essa lógica é encapsulada em um factory method dedicado do servlet, que desenvolve um renderizador de XML ou JSON responsável pela serialização de objetos. Consulte a Figura 7 abaixo.

Figura 7. Diagrama de classe de renderizador
Diagrama de classe de renderizador

Implementação do servlet de REST

O ciclo de vida do servlet de REST começa com o método init() que trata da inicialização do servlet com base nos parâmetros definidos em web.xml.

O parâmetro de inicializaçãoCrudConfigurators contém uma lista separada por vírgula das classes que implementam a interface CrudServiceConfigurator parametrizada. Esses objetos são necessários para ter um construtor de não argumentos. Depois da instanciação, o método configure() de cada objeto, que aceita uma instância de fachada de CRUD,ç é chamado. Cada configurador registra um conjunto de entidades de REST dentro da fachadaç.

De forma semelhante, a função restConfigurators armazena uma lista separada por vírgula das implementações de RestServiceConfiguration . O processamento é o mesmo de crudConfigurators.. O objetoRestService é basicamente responsável pelo estabelecimento de uma correspondência entre um caminho de pedido e uma classe de domínio específica. Por exemplo, o comando DELETE /rest/publication/123 deve ser executado em relação a uma entidade Publication. Cada configurador registra seu próprio conjunto de entidades de REST no objeto RestService .

Esse mecanismo de configuração e o objeto deçfachada que abordamos anteriormente facilitam o acréscimo do suporte a entidades arbitrárias que seguem a abordagem de projeto de publicação sem recompilar o código. Para cada nova entidade, um assembler de aplicativos só tem que adicionar novos objetos de configurador aos parâmetros de inicialização do servlet no descritor de implementação e empacotá-los junto com as classes de domínio no aplicativo da web.

Uma vez que o web.xml esteja configurado, o servlet estará pronto para tratar dos pedidos de HTTP. Tabela 1 abaixo resume a API REST fornecida pelo servlet. Por uma questão de concisão, a parte de <host>:<port> da URL foi omitida.

Tabela 1. API REST do servlet
Método de 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}]

Após o processamento com êxito, o corpo de resposta de um pedido de GET conterá uma lista de publicações, a resposta de um POST contém uma única publicação (a que está armazenada no banco de dados) e a resposta de um PUT ou DELETE contém apenas uma mensagem de confirmação. Se ocorrer uma exceção, o corpo de resposta conterá um código de status de HTTP e uma mensagem de erro opcional em um formato de renderização adequado.

A Listagem 6 mostra um exemplo de pedido de HTTP.

Lista 6. Exemplo de pedido de 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>

O diagrama de sequência da Figura 8 ilustra o processamento de pedidos, no qual os componentes estão conectados. (Veja uma versão maior da Figura 8)..)

Figura 8. Processamento do iWidget de
Processamento do iWidget de

iWidget

A interface com o usuário do aplicativo é implementada por um componente de iWidget 1.0 incluído na UI do Profiles. A interface com o usuário depende da biblioteca do kit de ferramentas Dojo JavaScript versão 1.2.3 que vem com o Lotus Connections 2.5. O widget de publicações fornece os seguintes recursos:

  • Ver a lista de publicações em uma página de perfil aberto de um usuário do Connections.
  • Criar ou atualizar uma publicação, cumprindo as regras de validação no lado do cliente.
  • Remover uma publicação.

Os botões de edição do iWidget só estão disponíveis quando o usuário visualiza o seu próprio perfil.

Estrutura da UI

A UI do iWidget é formada por um contêiner de pilha do Dojo com 3 painéis. O primeiro painel apresenta o usuário com uma lista de publicações formatada, somente para leitura (veja a Figura 9a ou uma versão maior da Figura 9a).

Figura 9a. Lista de publicações
Figura 9a. Lista de publicações

Se um usuário visualiza o seu perfil, é possível passar ao próximo painel, no qual se pode selecionar uma publicação individual para edição (veja a A figura 9b ou uma versão maior da Figura 9b).).

Figura 9b. Detalhes da publicação
Figura 9b. Detalhes da publicação

O painel inclui os botões Add, Editar e Remova . Clique em Add ouEditar para exibir o painel de edição de formulário com os campos Title, Link, Author(s), Journal, Publisher, Year e Selected publication (veja a Figura 9c ou uma versão maior da Figura 9c.).

Figura 9c. Editar a publicação
Figura 9c. Editar a publicação

Os controles de entrada do formulário aplicam regras básicas de validação (consulte a seção Validação de entrada acima para ver os detalhes).

Implementation

De acordo com a especificação, um iWidget é formado por uma classe de JavaScript (nesse caso, a classe DwPublication ) com uma definição de manipuladores de retorno de chamada e um arquivo XML que contêm a marcação de HTML.

A Figura 10 mostra como os componentes da UI interagem entre si e com o servidor. (Veja uma versão maior da Figura 10.)

Figura 10. Interação dos componentes do iWidget
Interação dos componentes do iWidget

No primeiro carregamento do iWidget de publicações, os seus manipuladores de retorno de chamada de Os manipuladores de retorno de chamada onLoad() são executados (Etapa 1, Figura 10) e, em seguida, a marcação de HTML é analisada pelo Dojo, para que o widget seja renderizado no navegador. Em seguida, a tela DwPublication obtém uma lista de publicações instanciando um objeto de armazenamento de dados do Dojo (dojo.data.ItemFileWriteStore) e o instrui a consultar uma lista de publicações a partir do servidor (2). É passada ao armazenamento de dados uma função de retorno de chamada que cria os respectivos nós de DOM para exibir uma lista somente para leitura (2.1). Quando um usuário clica no link Edit publications , a classe DwPublication desenvolve um novo objeto de DataGrid e exibe o painel de edição onde a grade deve ser exibida (3). A grade é preenchida pelo conteúdo do armazenamento de dados criado na etapa (2).

O painel de edição tem os botões Create, Editar e Remova . Se qualquer um dos dois primeiros botões for clicado, um formulário de edição é exibido ao usuário para que ele preencha os dados. Quando o formulário é enviado, o fluxo de controle é transferido de volta para DwPublication , que envia um pedido de HTTP POST ou PUT para criar ou atualizar a publicação.

Por outro lado, se o botão Remova , for clicado DwPublication envia um pedido de HTTP DELETE para excluir a publicação do banco de dados.

Nossa implementação de iWidget garante o comportamento correto dos recursos de atualização e maximização possibilitados pelo Lotus Connections. Quando a página da web inteira do Profiles é carregada ou recarregada e um iWidget se encontra em um estado maximizado, o manipulador de eventos Os manipuladores de retorno de chamada onLoad() do corpo HTML que foi adicionado pelo Dojo analisa automaticamente a marcação HTML na página para desenvolver os widgets Dojo. Por outro lado, quando um iWidget é atualizado por meio do seu menu de contexto (ou maximizado depois que a página foi carregada com um iWidget minimizado), a árvore de DOM do iWidget é destruída e redesenvolvida com base no arquivo XML dado como parte da definição do iWidget. Nesse caso, a análise não ocorre e deve ser chamada manualmente. Além disso, os widgets Dojo criados antes dos eventos de atualização ou maximização devem ser destruídos para evitar fugas de memória.

A Figura 11 mostra a diferença desses fluxos. Os manipuladores de retorno de chamada onLoad() e onUnload() na classe DwPublication são chamados quando os widgets antigos devem ser limpos e quando a marcação HTML deve ser analisada novamente.

Figura 11. Comportamento de (re)carregamento do iWidget
Comportamento de (re)carregamento do iWidget

Suporte à localização

Para possibilitar a localização integral da UI, implementamos vários widgets Dojo customizados projetados para definir a propriedade innerHTML de um nó específico como uma cadeia de caractere localizada tomada de um pacote configurável de recursos. Primeiro, você desenvolve uma mescla que suporta uma propriedaderótulo (consulte a a Listagem 7).

Lista 7. Mescla com uma propriedade "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;
 }
 }
);

Com a mescla estabelecida, é possível adicionar a propriedade rótulo a um widget Dojo declarado recentemente especificando i18nInnerHtmlMixin na lista de ancestrais (consulte o fragmento de código na A Listagem 8 ). A implementação padrão do método buildRendering() pode ser substituída para colocar um valor da propriedade rótulo em qualquer nó de DOM que seja necessário.

Lista 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: ""
 }
);

dFinalmente, na marcação HTML, o widget Dojo localizado é usado da maneira mostrada naListagem 9:

Lista 9. Declaração de widget localizado na marcação 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"/>

Nesse caso, a operação labelYearPublished é tomada do pacote configurável de recursos e definida como um valor da propriedade rótulo do widget customizado.

Profiles e Dojo

Usamos alguns truques de implementação para fazer certos controles do Dojo funcionar com o Profiles. Primeiro, precisamos colocar os contêineres dentro de um bloco <div> com um atributo de definido. Em segundo lugar, para fazer com que o dojox.grid.DataGrid seja exibido corretamente, é necessário carregar uma coleção de arquivos CSS antes da análise da marcação. Isso é feito com o código JavaScript, que desenvolve e anexa nós <link type=”text/css”> ao cabeçalho da página.

Validação de entrada

Quase todos os widgets Dojo do aplicativo (com exceção de diji.form.SimpleTextarea) aceitam recursos de validação do suporte a entradas do usuário e, consequentemente, nós os escolhemos para implementar o formulário de edição Publication. Para preencher a lacuna com a área de texto, desenvolvemos outro widget, o com.ibm.tonkawa.ValidationTextarea, para melhorar o elemento HTML textarea com o suporte à validação. De forma resumida, é uma mistura dos widgets dijit.form.ValidationTextBox e dijit.form.SimpleTextarea com os manipuladores de retorno de chamada onBlur() e onFocus() substituídos para realizar a validação.

Ao desenvolver uma mensagem XML para enviar ao servidor, certos caracteres, como menor que (<) ou maior que (>) são escapados para evitar injeções de XSS. De forma semelhante, esses caracteres também são escapados ao renderizá-los na página HTML.


Apêndice A: Integração ao Profiles

O processo de adicionar um iWidget customizado a um aplicativo Profiles é explicado detalhadamente no centro de informações do IBM Lotus Connections.. Envolve a parada do Profiles, a verificação do arquivo de configuração do widget, a sua ampliação com a definição e as instruções de colocação do iWidget e o registro de entrada do mesmo. A Listagem 10 mostra um exemplo do arquivo widgets-config.xml .

Lista 10. Definição do widget do Profiles e configuração da colocação
<widgetDef
 defId="dwPublication"
 bundleRefId="tonkawaBundle"
 url="{contextRoot}/../tonkawaWeb/widget/dwPublication.xml?version={version}"/>
…
<widgetInstance uiLocation="col2" defIdRef="dwPublication"/>

Opcionalmente, é possível registrar um pacote configurável de recursos customizável para fornecer cadeias de caracteres de localização para o título e os itens de menu do iWidget. Um pacote configurável é um diretório compactado com arquivos de propriedades cuja definição é adicionada ao arquivo de configuração do Lotus Connections (LotusConnections-config.xml).). Para ver um exemplo. consulte a Listagem 11.

Listagem 11. Definição do pacote configurável 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, o aplicativo Profiles é iniciado.


Apêndice B: Prós e contras do EJB

No caso do aplicativo deste artigo, o uso de EJBs oferece as seguintes vantagens:

  • Independência do ambiente de implementação. O modelo de programação de EJB isola os desenvolvedores do middleware subjacente.
  • Reusabilidade dos componentes de EJB. A especificação do modelo de programação prescreve a API necessária para os contêineres compatíveis com JEE, permitindo a escolha do middleware mais adequado para os requisitos e minimizando as alterações no código dos componentes.
  • Integração à plataforma JEE. As tecnologias maduras relacionadas, como Java Database Connectivity (JDBC), Java Message Service (JMS), J2EE Connector architecture (JCA), etc. fornecem serviços que o código EJB pode aproveitar de uma forma bem definida, descrita pelas especificações;
  • Suporte a transações distribuídas.
  • Escalabilidade transparente.

A complexidade do EJB 2.1 é notória. Por exemplo: impõe ao desenvolvedor várias interfaces (local, remota e inicial), o código de consulta do JNDI e os descritores de implementação de XML, que dependem de parâmetros específicos do contêiner de JEE. No caso do aplicativo deste artigo, os méritos ressaltados acima superam as desvantagens devido à complexidade. Além disso, a próxima geração da especificação EJB, EJB 3.0, trata de vários problemas de complexidade. O suporte à nova especificação pode ser habilitado ao instalar o feature pack EJB 3.0 em cima do WebSphere 6.1. Já que talvez não seja aceitável instalar o feature pack em todos os ambientes de implementação do Connections, este artigo descreve o EJB 2.1 para a implementação da camada de persistência. Entretanto, ao usar certas técnicas descritas na seçãoCamada de acesso a dados , pode-se simplificar a migração ao EJB 3.


Conclusão

Neste artigo, mostramos uma abordagem para criar um aplicativo da web customizado com REST e integrá-lo ao IBM Lotus Connections Profiles. O aplicativo amplia o Profiles com a capacidade de manter uma lista de publicações pertencentes a um determinado usuário. Fornece uma UI fácil e simples alinhada à sensação do Lotus Connections e é baseado em componentes do kit de ferramentas Dojo. As restrições de segurança são impostas para manter os dados consistentes e evitar danos. Além disso, o aplicativo é extensível para suportar novas entidades sem recompilar o código.


Download

DescriçãoNomeTamanho
Sample implementation codesource.zip840KB

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

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


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

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

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Segmentos de mercado, Tecnologia Java, Lotus
ArticleID=833133
ArticleTitle=Colaboração Mais Inteligente para o Segmento de Mercado de Educação Usando o Lotus Connections, Parte 1: Integre o Lotus Connections a um aplicativo da web com REST
publish-date=08312012