Mostrando postagens com marcador EAR. Mostrar todas as postagens
Mostrando postagens com marcador EAR. Mostrar todas as postagens

terça-feira, 17 de novembro de 2015

CONFIGURANDO A EXCLUSÃO DO USO DE MÓDULOS PADRÃO NO JBOSS 7



INTRODUÇÃO
 
Nos artigos anteriores (http://www.josepojr.com/2013/10/ordem-de-carregamento-de-classes-jars.html e http://www.josepojr.com/2013/01/isolamento-de-classloaders-no-jboss-as-7.html) foi mostrada a configuração do jboss 7 no arquivo jboss-deployment-structure.xml e algumas regras de preferência no carregamento de dependências. Nesse artigo, vamos mostrar como evitar que um módulo padrão que já vem instalado dentro do JBoss 7 (pasta $JBOSS_HOME/modules) seja carregado. E consequentemente, podemos usar os nossos arquivos .jar sem problemas de dependências e lançamentos de exceções do tipo NoSuchMethodException (método não existente). Esse tipo de exceção pode ocorrer se em tempo de projeto (usando uma IDE por exemplo), usamos os jars de uma biblioteca como por exemplo Apache Http Client na versão 4.5, mas no JBoss 7, já temos por exemplo, uma versão dessa mesma biblioteca na versão 4.2, por exemplo. E ao invocar métodos que só existam na versão 4.5, mas na versão 4.2, os mesmos não existe, o erro NoSuchMethodException pode aparecer. Isso porque em tempo de compilação foi usada a versão 4.5, mas em tempo de execução (projeto war ou ear implantado dentro do JBoss 7), a versão 4.2 (dentro da pasta $JBOSS_HOME/modules) pode ser carregada primeiro em detrimento a versão 4.5 (que pode ou não estar na pasta EAR/lib).
Por isso, precisamos configurar o arquivo .ear gerado para que não ocorra esses erros de dependências que será mostrado nesse artigo.


DESENVOLVIMENTO

Como por exemplo, vamos usar a biblioteca Apache Http Client que pode ser baixada do site:
Esses componentes permite qualquer programa acessar servidores web no protocolo HTTP ou HTTPS. Para isso, fornece modelos para a construção de clientes HTTP no seu programa Java. Sem esses componentes, teríamos que escrever do “zero” (projeto desde o início, novo) um cliente HTTP dentro do nosso programa Java. Isso é possível usando as classes java.net.Socket, java.io.InputStream, java.io.OutputStream e outras semelhantes dentro do JRE (Java Runtime, ambiente de execução do Java mais a máquina virtual) fornecido em toda instalação Java em qualquer dispositivo.
Mas, teríamos que perder um tempo enorme para fazer todas essas chamadas, sendo que com o uso desses componentes e com poucas linhas de códigos já temos um cliente HTTP para usarmos no nosso código.
Portanto, como esse projeto é tem uma licença bem semelhante a um software Open Source e é usado em diversos projetos, o JBoss 7 já vem com essa biblioteca da Apache já configurada como módulo dentro do servidor JBoss 7.
Ao mesmo tempo em que isso é bom, pois já temos um módulo dessa biblioteca para usarmos em nosso projeto, ao mesmo tempo também é ruim, pois queremos usar uma versão mais recente da biblioteca em nossos projetos, por exemplo.
Para isso, vamos configurar o arquivo jboss-deployment-structure.xml para não usar o Apache Http Client existente como módulo dentro do JBoss:

<?xml version="1.0" encoding="UTF-8"?>

<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.1">
<ear-subdeployments-isolated>true</ear-subdeployments-isolated>
<deployment>
<exclusions>
<module name="org.apache.httpcomponents" slot="main" />
</exclusions>
<dependencies>
<module name="javax.api"/>
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
<module name="org.infinispan"/>
<module name="deployment.MeuProjeto.ear.MeuEJB.jar"/>
<module name="deployment.MeuProjeto.ear.MeuWAR.war"/>
</dependencies>
</deployment>
<!-- durante o deploy. -->
<sub-deployment name="MeuEJB.jar">
<exclusions>
<module name="org.apache.httpcomponents" slot="main" />
</exclusions>
<local-last value="true"/>
</sub-deployment>
<sub-deployment name="MeuWAR.war">
<dependencies>
<module name="deployment.MeuProjeto.ear.MeuEJB.jar"/>
</dependencies>
<local-last value="true"/>
</sub-deployment>
</jboss-deployment-structure>


Só colocar os trechos marcados em VERMELHO no arquivo jboss-deployment-structure.xml do seu projeto. Nesse exemplo, estamos dizendo que o projeto EJB chamado “MeuEJB.jar” precisa usar a biblioteca Apache Http Client própria e não do JBoss 7. E também, estamos excluindo o uso no ear como um todo como mostra o trecho em VERMELHO dentro da tag <deployment>.
Na pasta /lib dentro do projeto EAR (ou conhecida como EAR/lib) devem estar os jars do Apache Http Client obtido no site:


CONCLUSÃO

Mostramos que o uso de configurações dentro do arquivo jboss-deployment-structure.xml permite refinar algumas configurações que terão impacto tanto no tempo de implantação (deploy) ou em tempo de execução, quando a aplicação for chamada pela primeira vez.
Com isso, podemos usar as versões mais recentes das bibliotecas de terceiros sem conflitar com as mesmas bibliotecas de outras versões já instaladas dentro do JBoss 7.


REFERÊNCIAS











sexta-feira, 11 de outubro de 2013

Ordem de carregamento de classes / jars no JBoss AS 7


INTRODUÇÃO

Nesse blog escrevi sobre isolamentos de classloaders no JBoss AS 7.
Vejam nesse link:

http://www.josepojr.com/2013/01/isolamento-de-classloaders-no-jboss-as-7.html

Mas gostaria de complementar com mais explicações sobre a ordem usada no carregamento de classes / jars no JBoss AS 7.


CONCEITOS

1 - Só criar módulos no JBoss 7 se for alguma API que será usada em vários projetos (WAR ou EAR) que serão implantados (deploy) no JBoss 7.

2 - Dentro de um EAR, pode-se colocar os jars dentro dos seus respectivos subprojetos. Ou seja, se no seu EAR tem dois wars e um jar (Projeto EJB), um jar que será usado em somente um war, pode ser colocado dentro da pasta WEB-INF/lib desse respectivo war. Agora, se o seu jar é usado em todos os subprojetos do ear, usar dentro do diretório EAR/lib.

3 - O JBoss 7 tem uma regra de procedência para decidir qual classe de qual jar carregar primeiro. Veja em:

https://docs.jboss.org/author/display/AS71/Class+Loading+in+AS7


Resumindo a regra usada, de acordo com a prioridade:

1º Carregar classes de sistemas do JRE e JavaEE (especificação).
2º Dependências declaradas explicitamente no arquivo jboss-deployment-structure.xml
3º Classes / jars internos dos subprojetos como WEB-INF/classes ou WEB-INF/lib para um projeto WAR.
4º Jars contidos na pasta EAR/lib do seu projeto EAR.


Essas regras são muito importantes para o desenvolvimento de aplicações no JBoss AS 7. Se sua classe estiver no mesmo pacote e mesmo nome (exemplo meupacote.MinhaClasse) e estiver em dois lugares como WEB-INF/lib e EAR/lib, o JBoss AS 7 carregará a classe que achará primeiro. E pela regra da prioridade, pegará a classe MinhaClasse que está no jar do subprojeto WAR pasta WEB-INF/lib. Se a classe MinhaClasse estiver também em um outro jar da pasta EAR/lib, a classe nesse pacote não será carregada.


CONCLUSÃO

A equipe de projeto e desenvolvimento do JBoss AS 7, tiveram que elaborar algum mecanismo para definir em casos de "dúvidas" qual classe será carregada, no caso de existirem classes como mesmo nome e pacote dentro do servidor JBoss AS 7. Essas regras visam tomar decisões para em tempo de execução, o JBoss AS 7 resolver o impasse e escolher uma classe dentro de um conjunto de N classes de mesmo pacote e nome. Claro que nesse cenário estamos lidando com o pior caso, de temos a mesma classe e no mesmo pacote e M versões distintas e rodando no mesmo servidor de aplicações JBoss AS 7.



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