sábado, 7 de abril de 2012

Uso do JBoss Cache para aplicações JEE



1. INTRODUÇÃO

Nesse artigo vamos mostrar o uso da API JBoss Cache versão 3.10 apelido "Cascabel" numa aplicação JEE sobre o servidor de aplicações JBoss AS 5.1. A maior vantagem de se usar JBoss Cache reside no fato do JBoss Cache estar embutido no JBoss AS 5.1. Se for usar outra API de cache, terá que configurar todo o servidor de aplicações e com isso, correndo o risco de dar conflitos com outros jars da API do servidor JBoss. Além disso, possui uma API de fácil entendimento e uso, pois com poucos objetos e operações já estaremos tirando proveito de suas funcionalidades.


2. CONFIGURAÇÃO

Para ilustrar melhor o uso da API JBoss Cache, vamos criar um projeto EAR com o nome "CacheEAR" e outro projeto web com o nome "CacheWAR".


2.1. CRIAÇÃO DO PROJETO EAR

Para isso, vamos criar um projeto Ear (Enterprise Application) no IDE Eclipse 3.7.1 (ou pode ser outra IDE, desde que possua essa funcionalidade). Chamaremos o nosso projeto EAR de "CacheEAR". Veja nas figuras a seguir a criação do projeto EAR.




Observação

Precisa ter o JBOSS TOOLS instalado no Eclipse para criar o Runtime (Ambiente de Execução) do servidor de aplicações JBoss  5.1 (veja nas figuras a seguir).







2.2. CRIAÇÃO DO PROJETO WEB

Para isso, vamos criar um projeto web dinâmico (Que cria o arquivo WAR no JEE) no IDE Eclipse 3.7.1 (ou pode ser outra IDE, desde que possua essa funcionalidade). Chamaremos o nosso projeto web de "CacheWAR". Veja nas figuras a seguir a criação do projeto WAR.







2.3. CRIAÇÃO DO LISTENER DO CACHE


Para usar o JBoss Cache numa aplicação web da melhor maneira possível, precisamos criar um listener (ouvinte) para "escutar" o início e fim de execução de uma aplicação web. No início da aplicação web, o listener limpará o cache que ficou carregado num deploy da mesma aplicação anteriormente. Essa "limpeza" é necessária, pois se a aplicação web sofrer um redeploy, consequentemente, o novo deploy estará usando um classloader (fornecido pelo containter JEE) diferente do classloader dos objetos em cache (carregados pela mesma aplicação web no deploy antigo). Consequentemente, se a aplicação web "nova" (novo redeploy) estará com o classloader diferente dos objetos cacheados no deploy anterior, o que pode resultar na ocorrência da exceção ClassCastException. Para entender melhor a hierarquia de classloader de uma aplicação web, veja nas figuras a seguir.






Por isso temos que usar o listener (classe que implementará a interface javax.servlet.ServletContextListener) que possa limpar o cache do deploy antigo antes do início da aplicação web (que criará e usará o cache). Veja nas figuras a seguir o processo de criação do listener.





Cole o código abaixo dentro do corpo do método "void contextInitialized(ServletContextEvent arg0)" do listener criado (CacheListener).




// Toda vez que a aplicação web é iniciada, o cache apartir do caminho X
// precisa ser limpo, senão o cache pode buscar objetos carregados de
// outros

// deployers cujo classloader também será outro. Consequentemente,
// ocorrerão erros de ClassCastException ao tentar recuperar objetos do
// cache.

try {

javax.naming.Context ctx = new javax.naming.InitialContext();
org.jboss.cache.CacheManager cacheManager = (org.jboss.cache.CacheManager) ctx
.lookup("java:CacheManager");

org.jboss.cache.Cache<Object, Object> cache = cacheManager.getCache(
"standard-session-cache", true);


if (CacheStatus.INSTANTIATED.equals(cache.getCacheStatus())
|| CacheStatus.STOPPED.equals(cache.getCacheStatus())) {
cache.start();
}

org.jboss.cache.Fqn<String> f = org.jboss.cache.Fqn.fromElements("nivel1");

// Limpa cache.
cache.removeNode(f);

}  catch (Exception e) {
e.printStackTrace();
}
2.4. CRIAÇÃO DO POJO PARA SER USADO NO CACHE

Para ilustrar melhor o uso do cache, vamos criar uma classe simples java (POJO) para ser usada como objeto de cache para podermos entender melhor o uso da API JBoss Cache. Lembrando que é bom que essa classe implemente a interface java.io.Serializable para poder trafegar pela rede na aplicação web.
Veja o código a seguir:



import java.io.Serializable;

public class Pojo implements Serializable {

/**
*
*/
private static final long serialVersionUID = 5549023217127630221L;

private Long id;

public Pojo(Long id) {
this.id = id;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}
}



2.5. CRIAÇÃO DO SERVLET DO CACHE

Para testar o uso do cache, vamos criar um servlet que estende a classe javax.servlet.http.HttpServlet.
Esse servlet numa aplicação web é responsável por tratar as requisições web recebidas (seja no protocolo HTTP, FTP, etc) e enviar respostas as requisições (ou não). Nesse exemplo, ao enviar uma requisição para o servlet, o mesmo tentará buscar o objeto da classe Pojo (criada anteriormente) do cache. Na primeira requisição ao servlet, o objeto não existirá no cache, consequentemente, o servlet criará o objeto (da classe Pojo) e depois disso, adicionará no cache e imprimirá no console dados do objeto cacheado. Nas segundas requisições em diante, o objeto estará no cache, consequentemente, o servlet apenas imprimirá o objeto do cache, sem a necessidade da criação do objeto. Lembrando também que dependendo das configurações do JBoss Cache usado (veja no arquivo $JBOSS_HOME/server/all/deploy/cluster/jboss-cache-manager.sar/META-INF/jboss-cache-manager-jboss-beans.xml para ajuste fino do cache), o objeto cacheado tem um tempo de vida no cache, o que significa que o mesmo pode ficar no cache por um intervalo de tempo finito (definido nas configurações do JBoss Cache) e depois que esse tempo "estourou", o objeto cacheado é removido do cache. Para criar o servlet, veja nas figuras a seguir:







Abaixo temos o código para ser inserido no método "protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException" do CacheServlet criado.

try {

Context ctx = new InitialContext();
CacheManager cacheManager = (CacheManager) ctx
.lookup("java:CacheManager");

Cache<Object, Object> cache = cacheManager.getCache(
"standard-session-cache", true);


                        if (CacheStatus.INSTANTIATED.equals(cache.getCacheStatus())
|| CacheStatus.STOPPED.equals(cache.getCacheStatus())) {
cache.start();
}

Fqn<String> f = Fqn.fromElements("nivel1", "nivel2");

Pojo pojo = (Pojo) cache.get(f, new Long(10));

if (pojo == null) {
Fqn<String> afqn = Fqn.fromElements("nivel1", "nivel2");
Long id = 10l;
pojo = new Pojo(new Long(1000));

cache.put(afqn, id, pojo);

System.out.println("Adicionando objeto no cache");
} else {
System.out.println("Usando objeto no cache");
}

System.out.println(cache);
System.out.println(pojo);
} catch (Exception e) {
e.printStackTrace();
}



2.6. RESUMO DA CONFIGURAÇÃO


Abaixo estão listadas as configurações dos projetos criados. Todos os passos seguidos anteriormente já criam esses arquivos automaticamente, mas, por ventura a sua IDE usada deu algum conflito, falha que você não conseguiu criar os projetos adequadamente, então, veja as configurações abaixo:


Arquivo application.xml do projeto CacheEAR



<?xml version="1.0" encoding="UTF-8"?>
<application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:application="http://java.sun.com/xml/ns/javaee/application_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd" id="Application_ID" version="5">
  <display-name>CacheEAR </display-name>
  <module>
    <web>
      <web-uri>CacheWAR.war </web-uri>
      <context-root>CacheWAR </context-root>
   </web>
  </module>
</application>



Arquivo web.xml do projeto CacheWAR



<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>CacheWAR</display-name>
  <listener>
    <listener-class>CacheListener</listener-class>
  </listener>
  <servlet>
    <description></description>
    <display-name>CacheServlet</display-name>
    <servlet-name>CacheServlet</servlet-name>
    <servlet-class>CacheServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>CacheServlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>



Figura com a estrutura de diretórios dos dois projetos (CacheEAR e CacheWAR).




2.7. EXECUTANDO UM EXEMPLO

Abra qualquer navegador e digite a seguinte URL:

http://localhost:8080/CacheWAR/

Supondo que o servidor JBoss para aplicações web está usando a porta 8080 para receber requisições e também, estamos supondo o uso do servidor local (localhost) senão, terá que substituir o "localhost" pelo nome ou ip do servidor JBoss.

Nisso, o console do servidor JBoss mostrará uma mensagem sobre o objeto conforme figura a seguir:








Nessa primeira requisição do objeto ao cache, o mesmo não está no cache, então o servlet terá que instânciar o objeto e adicionar no cache. Agora, chamamos novamente o mesmo link (http://localhost:8080/CacheWAR/) e a mensagem mostrada sobre o objeto cacheado conforme mostra a figura a seguir:


Nota-se que o objeto instanciado anteriormente (primeira requisição do objeto ao cache) e o objeto cacheado são os mesmos objetos!! Isso mostra que o JBoss Cache guarda o objeto em cache conforme suas configurações (políticas de cache que o desenvolvedor pode alterá-las no arquivo $JBOSS_HOME/server/all/deploy/cluster/jboss-cache-manager.sar/META-INF/jboss-cache-manager-jboss-beans.xml).


3. CONCLUSÃO

O JBoss Cache permite o uso de cache de maneira rápida e fácil numa aplicação JEE usando como servidor de aplicações o JBoss AS 5.1. Embora no site do JBoss Cache cite que o projeto está em "Estado de manutenção" e que novas funcionalidades a princípio não serão incorporadas, existem muitas empresas que usam o JBoss AS 5.1 em produção e cujo custo de mudar o servidor de aplicações é proibitivo, fora o risco de desconfigurar a API padrão de caches (JBoss Cache 3.1) do servidor JBoss e configurar outra API, como a Infinispan ou Ehcache dentro do servidor JBoss.


4. REFERÊNCIAS

Download da última versão do IDE Eclipse

http://www.eclipse.org/downloads/

Download da última versão do JBOSS TOOLS

http://www.jboss.org/tools/download

JBoss Cache

http://www.jboss.org/jbosscache

http://www.ime.usp.br/~reverbel/SMA-06/Slides/seminarios/

Outras API's de cache (no Java)

http://ehcache.org/

http://www.jboss.org/infinispan