4. Aplicativo de exemplo – 02: rdvmedecins-jsf2-spring
Propomos agora portar a aplicação anterior para um ambiente Spring/Tomcat:
![]() |
Trata-se, de fato, de uma portabilidade. Partiremos da aplicação anterior e a adaptaremos ao novo ambiente. Comentaremos apenas as modificações. Elas se dividem em três categorias:
- o servidor não é mais o Glassfish, mas sim o Tomcat, um servidor leve que não possui um contêiner EJB,
- para substituir o EJB, utilizaremos o Spring, o principal concorrente do EJB e do [http://www.springsource.com/],
- a implementação JPA utilizada será o Hibernate, em vez do EclipseLink.
Como faremos muitas operações de copiar/colar entre o projeto antigo e o novo, mantemos os projetos anteriores abertos no NetBeans:
![]() |
O uso do framework Spring requer certos conhecimentos que podem ser encontrados em [ref7] (ver página 166).
4.1. As camadas [DAO] e [JPA]
![]() |
4.1.1. O projeto NetBeans
Estamos criando um projeto Maven do tipo [Java Application]:
![]() | ![]() | ![]() |
![]() |
- em [1], o projeto criado,
- em [2], o mesmo projeto sem os pacotes de [Source Packages] e [Test Packages] e sem a dependência [junit-3.8.1].
O mais difícil nos projetos Maven é encontrar as dependências corretas. Para este projeto Spring / JPA / Hibernate, elas são as seguintes:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-rdvmedecins-spring-dao-jpa</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-rdvmedecins-spring-dao-jpa</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>4.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.hibernate.java-persistence</groupId>
<artifactId>jpa-api</artifactId>
<version>2.0.Beta-20090815</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
<type>jar</type>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>3.1.1.RELEASE</version>
<type>jar</type>
</dependency>
</dependencies>
</project>
- linhas 18-29: para o Hibernate,
- linhas 30-34: para o driver JDBC do MySQL,
- linhas 35-41: para o teste JUnit,
- linhas 42-51: para o pool de conexões Apache Commons DBCP. Um pool de conexões é um conjunto de conexões abertas. Quando o aplicativo precisa de uma conexão, ele a solicita ao pool. Quando não precisa mais dela, ele a devolve. As conexões são abertas no início da aplicação e permanecem abertas durante toda a vida útil da aplicação. Isso evita o custo de aberturas e fechamentos repetidos das conexões. Esse tipo de pool já existia no Glassfish, mas seu uso era transparente para nós. Será o mesmo aqui, mas precisamos instalá-lo e configurá-lo,
- linhas 52-75: para o Spring.
Vamos adicionar essas dependências e compilar o projeto:
![]() |
- em [1], compilamos o projeto, o que forçará o Maven a baixar as dependências,
- em [2], elas aparecem então no ramo [Dependencies]. São muitas, já que os frameworks Hibernate e Spring, por si só, possuem inúmeras dependências. Mais uma vez, graças ao Maven, não precisamos nos preocupar com elas. Elas são baixadas automaticamente.
Agora que temos as dependências, copiamos o código do projeto EJB da camada [dao] para o projeto Spring da camada [dao]:
![]() |
- em [1], copiamos para o projeto de origem,
- em [2], cola-se no projeto de destino,
- em [3], o resultado.
Após a cópia, é preciso corrigir os erros.
4.1.2. O pacote [exceptions]
![]() |
A classe [RdvMedecinsExceptions] [1] apresenta erros devido ao pacote [javax], linha 4, que não existe mais. Trata-se de um pacote específico do EJB. O erro da linha 6 decorre do erro da linha 4. Eliminamos essas duas linhas. Isso elimina os erros [2].
4.1.3. O pacote [jpa]
![]() |
- no [1], a classe [Creneau] apresenta erro devido à ausência do pacote de validação da linha [5]. Seria possível adicionar esse pacote às dependências do projeto. No entanto, durante os testes, o Hibernate lança uma exceção por causa dele. Como não é essencial para nossa aplicação, nós o removemos. Para corrigir a classe, basta excluir todas as linhas com erro [2]. Fazemos isso para todas as classes com erro.
4.1.4. O pacote [dao]
Chegamos ao ponto seguinte:
![]() |
- em [1], os dois pacotes corrigidos,
- em [2], o pacote [dao]. Como não há mais o EJB, também não há mais o conceito de interface remota e local do EJB. Nós os removemos, [3].
![]() |
- no [1], os erros da classe [DaoJpa] têm duas origens:
- a importação de um pacote vinculado a EJB (linhas 6-8);
- o uso das interfaces local e remota que acabamos de excluir.
Excluímos as linhas com erros e utilizamos a interface [IDao] no lugar das interfaces local e remota [2].
![]() |
No projeto EJB, a classe [DaoJpa] era um singleton e seus métodos eram executados dentro de uma transação. Veremos que a classe [DaoJpa] será um bean gerenciado pelo Spring. Por padrão, todo bean do Spring é um singleton. Isso quanto à primeira propriedade. A segunda é obtida com a anotação @Transactional do Spring [3]:
![]() |
Feito isso, o projeto não apresenta mais erros [4].
4.1.5. Configuração da camada [JPA]
No projeto EJB, havíamos configurado a camada [JPA] com o arquivo [persistence.xml]. Aqui, temos uma camada [JPA] e, portanto, precisamos criar esse arquivo. No projeto EJB, nós o geramos com o Glassfish. Aqui, vamos criá-lo manualmente. O principal motivo é que parte da configuração do arquivo [persistence.xml] é migrada para o próprio arquivo de configuração do Spring.
Criamos o arquivo [persistence.xml]:
![]() |
com o seguinte conteúdo:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="spring-dao-jpa-hibernate-mysqlPU" transaction-type="RESOURCE_LOCAL">
<class>rdvmedecins.jpa.Client</class>
<class>rdvmedecins.jpa.Creneau</class>
<class>rdvmedecins.jpa.Medecin</class>
<class>rdvmedecins.jpa.Rv</class>
</persistence-unit>
</persistence>
- linha 3: atribuímos um nome à unidade de persistência,
- linha 3: o tipo das transações é RESOURCE_LOCAL. No projeto EJB, era JTA para indicar que as transações eram gerenciadas pelo contêiner EJB. O valor RESOURCE_LOCAL indica que a própria aplicação gerencia suas transações. Esse será o caso aqui por meio do Spring,
- linhas 4-7: os nomes completos das quatro entidades JPA. Isso é opcional, pois o Hibernate as procura automaticamente no ClassPath do projeto.
É isso. O nome do provedor JPA, suas propriedades e as características JDBC da fonte de dados agora estão no arquivo de configuração do Spring.
4.1.6. O arquivo de configuração do Spring
Mencionamos que a classe [DaoJpa] é um bean gerenciado pelo Spring. Isso é feito por meio de um arquivo de configuração. Esse arquivo também incluirá a configuração do acesso ao banco de dados, bem como o gerenciamento de transações. Ele deve estar no diretório ClassPath do projeto. Nós o colocamos no ramo [Other sources]:
![]() |
O arquivo [spring-config-dao.xml] é o seguinte:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!-- camadas de aplicação -->
<bean id="dao" class=" " />
<!-- EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect" />
<!--
<property name="showSql" value="true" />
<property name="generateDdl" value="true" />
-->
</bean>
</property>
</bean>
<!-- a fonte de dados DBCP -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/dbrdvmedecins2" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<!-- gerenciador de transações -->
<tx:annotation-driven transaction-manager="txManager" />
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!-- tradução de exceções -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- persistência -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
</beans>
Este é um arquivo compatível com o Spring 2.x. Não procuramos utilizar os novos recursos das versões 3.x.
- linhas 2-4: a tag raiz <beans> do arquivo de configuração. Não comentaremos os diversos atributos dessa tag. É importante ter cuidado ao copiar e colar, pois um erro em qualquer um desses atributos pode causar erros que, às vezes, são difíceis de entender,
- linha 7: o bean “dao” é uma referência a uma instância da classe [rdvmedecins.dao.DaoJpa]. Será criada uma única instância (singleton) que implementará a camada [dao] da aplicação,
- linhas 24-29: é definida uma fonte de dados. Ela fornece o serviço de “pool de conexões” de que falamos. Aqui é utilizado o [DBCP] do projeto Apache Commons DBCP [http://jakarta.apache.org/commons/dbcp/],
- linhas 25-28: para criar conexões com o banco de dados de destino, a fonte de dados precisa saber o driver JDBC utilizado (linha 25), o URL do banco de dados (linha 26), o usuário da conexão e sua senha (linhas 27-28),
- linhas 10-21: configuram a camada JPA,
- linha 10: define um bean do tipo [EntityManagerFactory] capaz de criar objetos do tipo [EntityManager] para gerenciar os contextos de persistência. A classe instanciada [LocalContainerEntityManagerFactoryBean] é fornecida pelo Spring. Ela precisa de alguns parâmetros para ser instanciada, definidos nas linhas 11-20,
- linha 11: a fonte de dados a ser utilizada para obter conexões com o SGBD. Trata-se da fonte [DBCP] definida nas linhas 24 a 29,
- linhas 12 a 20: a implementação JPA a ser utilizada,
- linha 13: define o Hibernate como a implementação JPA a ser utilizada,
- linha 14: o dialeto SQL que o Hibernate deve usar com o SGBD de destino, neste caso MySQL5,
- linha 16 (comentada): solicita que os comandos SQL executados pelo Hibernate sejam registrados no console,
- linha 17 (comentada): solicita que, ao iniciar o aplicativo, o banco de dados seja gerado (drop e create),
- linha 32: indica que as transações são gerenciadas com anotações Java (elas também poderiam ter sido declaradas em spring-config.xml). Trata-se, especificamente, da anotação @Transactional encontrada na classe [DaoJpa],
- linhas 33-35: definem o gerenciador de transações a ser utilizado,
- linha 33: o gerenciador de transações é uma classe fornecida pelo Spring,
- linha 34: o gerenciador de transações do Spring precisa conhecer a classe EntityManagerFactory, que gerencia a camada JPA. É a classe definida nas linhas 10 a 21,
- linha 41: define a classe que gerencia as anotações de persistência do Spring,
- linha 38: define a classe Spring que gerencia, entre outras coisas, a anotação @Repository, a qual torna uma classe assim anotada elegível para a conversão das exceções nativas do driver JDBC do SGBD em exceções genéricas do Spring do tipo [DataAccessException]. Essa conversão encapsula a exceção nativa JDBC em um tipo [DataAccessException] que possui várias subclasses:

Essa tradução permite que o programa cliente gerencie as exceções de forma genérica, independentemente do SGBD de destino. Não utilizamos a anotação @Repository em nosso código Java. Portanto, a linha 38 é desnecessária. Nós a mantivemos apenas a título informativo.
Concluímos o arquivo de configuração do Spring. Ele foi extraído da documentação do Spring. Sua adaptação a diversas situações geralmente se resume a duas modificações:
- a do banco de dados de destino: linhas 24-29,
- a da implementação JPA: linhas 12 a 20.
Ao executar o código, todos os beans do arquivo de configuração serão instanciados. Veremos como.
4.1.7. A classe de teste JUnit
![]() |
Tínhamos testado a camada [DAO] do projeto EJB com um teste JUnit. Fazemos o mesmo para a camada [DAO] do projeto Spring:
- nos projetos [1] e [2], ao copiar e colar o teste JUnit entre os dois projetos,
- em [3], o teste importado apresenta erros em seu novo ambiente.
![]() |
O erro relatado [1] é o da interface remota do EJB, que não existe mais. Além disso, o código de inicialização do campo [dao] da linha 19 era uma chamada JNDI específica para o EJB (linhas 25-28). Para instanciar o campo [dao] da linha 19, precisamos utilizar o arquivo de configuração do Spring. Isso é feito da seguinte maneira:
![]() |
- linha 21: o tipo da interface passou a ser [IDao],
- linha 28: instancia todos os beans declarados no arquivo [spring-config-dao.xml], incluindo este:
<bean id="dao" class="rdvmedecins.dao.DaoJpa" />
- a linha 29 solicita ao contexto Spring da linha 28 uma referência ao bean com id="dao". Obtém-se, então, uma referência ao singleton [DaoJpa] (class acima) que o Spring instanciou.
As linhas 28-29 constroem os seguintes blocos (linhas pontilhadas em rosa):
![]() |
Quando os testes do cliente JUnit são executados, a camada [DAO] já foi instanciada. Portanto, é possível testar seus métodos. Observe que não há necessidade de servidor para realizar este teste, ao contrário do teste do EJB [DAO], que exigiu o servidor Glassfish. Aqui, tudo é executado no mesmo JVM.
Agora é possível executar o teste JUnit. É necessário que o servidor MySQL esteja em execução. Os resultados são os seguintes:
![]() |
O teste JUnit foi bem-sucedido. Vamos examinar os logs do teste, assim como foi feito no teste do EJB:
- linhas 1-4: logs do Spring,
- linhas 5-10: logs do Hibernate,
- linha 11: o Spring lista todos os beans que instanciou. Em primeiro lugar, encontramos o bean [dao],
- linhas 12 e seguintes: os logs do teste JUnit,
- linhas 60-65: vemos claramente a exceção causada pela adição de um compromisso já existente no banco de dados. Vale lembrar que, com o EJB, não tivemos essa exceção devido a um problema de serialização.
A camada [dao] está operacional. Agora, estamos construindo a camada [métier].
4.2. A camada [métier]
![]() |
Procedemos da mesma forma que para a camada [DAO], copiando e colando do projeto EJB para o projeto Spring.
4.2.1. O projeto NetBeans
Criamos um novo projeto Maven do tipo [Java Application], removendo tudo o que não queremos manter do [1]:
![]() |
4.2.2. As dependências do projeto
Na arquitetura:
![]() |
a camada [métier] depende da camada [dao]. Portanto, adicionamos uma dependência do projeto anterior:
![]() |
- em [1] e [2], adicionamos uma dependência do projeto da camada [dao],
- no [3], essa dependência gerou outras dependências, as do projeto da camada [dao].
![]() |
- em [1] e [2], copiamos os códigos-fonte Java do projeto EJB para o projeto Spring,
- para [3]; os códigos-fonte importados apresentam erros no novo ambiente.
Começamos removendo as interfaces remota e local da camada [métier], que não existem mais no [4]:
![]() |
- no [5], os erros da classe [Metier] têm várias causas:
- o uso do pacote [javax.ejb], que não existe mais;
- o uso da interface [IDaoLocal], que não existe mais;
- o uso das interfaces [IMetierRemote] e [IMetierLocal], que não existem mais.
Nós
- excluímos todas as linhas incorretas relacionadas ao pacote [javax.ejb],
- substituímos a interface [IDaoLocal] pela interface [IDao],
- substituímos as interfaces [IMetierRemote] e [IMetierLocal] pela interface [IMetier].
![]() |
- em [6], a classe assim corrigida,
- No [7], não há mais erros.
Removemos as referências ao EJB, mas agora precisamos recuperar suas propriedades:
![]() |
- linha 22: tínhamos um singleton. Essa característica será obtida transformando a classe em um bean gerenciado pelo Spring,
- linha 23: cada método era executado em uma transação. Isso será obtido com a anotação @Transactional do Spring,
- linhas 27-28: a referência na camada [DAO] era obtida por injeção do contêiner EJB. Usaremos uma injeção do Spring.
O código da classe [Metier] do projeto Spring passa a ser o seguinte:
![]() |
Isso é tudo quanto ao código Java. O restante ocorre no arquivo de configuração do Spring.
4.2.3. O arquivo de configuração do Spring
Copiamos o arquivo de configuração do Spring do projeto da camada [DAO] para o projeto da camada [métier]. Começamos criando o ramo [Other Resources] no projeto da camada [métier], caso ele ainda não exista:
![]() |
- no [1], na aba [Files], criamos uma subpasta na pasta [main],
- em [2], ele deve se chamar [resources],
- em [3], na aba [Projects], o ramo [Other Sources] foi criado.
Podemos prosseguir com o copiar/colar do arquivo de configuração do Spring:
![]() |
- em [1], copiamos o arquivo do projeto [DAO] para o projeto [métier] [2],
- em [3], o arquivo copiado.
O arquivo de configuração que foi copiado configura a camada [DAO]. Adicionamos a ele um bean para configurar a camada [métier]:
- linha 2: o bean da camada [DAO],
- linhas 3-5: o bean da camada [métier],
- linha 3: o bean se chama métier (atributo id) e é uma instância da classe [rdvmedecins.metier.service.Metier] (atributo class). Esse bean será instanciado como os demais ao iniciar o aplicativo.
Vamos relembrar o código do bean [rdvmedecins.metier.service.Metier]:
package rdvmedecins.metier.service;
...
public class Metier implements IMetier, Serializable {
// camada DAO
private IDao dao;
public Metier() {
}
- linha 8: o campo [dao] será instanciado pelo Spring ao mesmo tempo que o bean métier. Voltemos à definição desse bean no arquivo de configuração do Spring:
- linha 4: a tag <property> serve para inicializar campos do bean instanciado. O nome do campo é definido pelo atributo name. Portanto, será instanciado o campo `dao` da classe [rdvmedecins.metier.service.Metier]. Isso ocorrerá por meio de um método setDao, que deve existir. O valor que lhe será atribuído é o do atributo `ref`. Esse valor, neste caso, é a referência do bean `dao` da linha 2.
Em termos mais simples, no código:
package rdvmedecins.metier.service;
...
public class Metier implements IMetier, Serializable {
// camada DAO
private IDao dao;
public Metier() {
}
O campo dao da linha 19 será inicializado pelo Spring com uma referência à camada [dao]. Era isso que queríamos. O campo **dao* será inicializado pelo Spring por meio de um *setter, que precisamos adicionar:
// setter
public void setDao(IDao dao) {
this.dao = dao;
}
Renomeamos o arquivo de configuração do Spring para refletir as alterações:
![]() |
Agora estamos prontos para um teste. Retomamos o teste de console usado para testar o EJB [Metier].
4.2.4. Teste da camada [métier]
O teste será realizado com a seguinte arquitetura:
![]() |
Copiamos o teste de console do projeto EJB para o projeto Spring:
![]() |
- em [1] e [2]; ao copiar e colar entre os dois projetos,
- no [3], o código importado apresenta erros.
![]() |
O código importado apresenta dois tipos de erro:
- linha 13: a interface [IMetierRemote] foi substituída pela interface [IMetier],
- linhas 24-27: a instanciação da camada [métier] não é mais feita por meio de uma chamada JNDI, mas sim pela instanciação dos beans do arquivo de configuração do Spring.
Corrigimos esses dois pontos:
![]() |
- linha 22: o arquivo [spring-config-metier-dao.xml] é utilizado. Todos os beans desse arquivo são, então, instanciados. Entre eles, estão os seguintes:
<!-- camadas de aplicação -->
<bean id="dao" class="rdvmedecins.dao.DaoJpa" />
<bean id="metier" class="rdvmedecins.metier.service.Metier">
<property name="dao" ref="dao"/>
</bean>
Esses dois beans representam as camadas [DAO] e [métier] da arquitetura do teste:
![]() |
Feito isso, o teste pode ser executado:
![]() |
Os logs do teste são, então, os seguintes:
- linhas 1-4: os registros do Spring e do Hibernate,
- linha 5: os beans instanciados pelo Spring. Destacam-se os beans DAO e de lógica de negócio,
- linhas 6-53: os logs do teste. Eles estão em conformidade com o que foi obtido no teste do projeto EJB. Remetemos o leitor aos comentários desse teste (parágrafo 3.5.3).
Construímos a camada [métier]. Passamos agora para a última camada, a camada [web].
4.3. A camada [web]
![]() |
Para construir a camada [web], seguiremos o mesmo procedimento utilizado nas outras duas camadas, ou seja, copiando e colando a partir da camada [web] do projeto EJB.
4.3.1. O projeto NetBeans
Primeiro, criamos um projeto web:
![]() |
- no [1], criamos um novo projeto,
- em [2], um projeto Maven do tipo [Web Application],
- em [3], atribuímos um nome a ele,
![]() |
- em [4], escolhe-se, desta vez, o servidor Tomcat e não o Glassfish, que foi utilizado no projeto EJB,
- no [5], o projeto obtido,
- em [6], o projeto após a exclusão de [index.jsp] e do pacote de [Source Packages].
4.3.2. As dependências do projeto
Vamos examinar a arquitetura do projeto:
![]() |
A camada [web] depende das camadas [métier], [DAO] e [JPA]. Essas fazem parte dos dois projetos que acabamos de compilar. Daí a dependência em relação a cada um desses projetos:
![]() |
- no [1], adicionamos a dependência do projeto Spring/negócio,
- no [2], o projeto Spring / domínio foi adicionado. Como ele próprio tinha uma dependência do projeto Spring / DAO / JPA, este foi automaticamente adicionado às dependências do [3].
Voltemos à estrutura da nossa aplicação:
![]() |
A camada web é uma camada JSF. Portanto, precisamos das bibliotecas do Java Server Faces. O servidor Tomcat não as possui. A dependência, portanto, não terá o escopo (scope) [provided], como acontecia com o servidor Glassfish, mas sim o escopo [compile], que é o escopo padrão quando não se especifica nenhum escopo.
Adicionamos essas dependências diretamente no código de [pom.xml]:
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-rdvmedecins-spring-metier</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
- as linhas 7 a 16 foram adicionadas ao arquivo [pom.xml]. Essas são as dependências em relação ao JSF. São as mesmas utilizadas no projeto EJB / Glassfish. Observe que elas não possuem a tag <scope>. Portanto, por padrão, têm o escopo [compile]. A biblioteca JSF será, assim, incorporada ao arquivo [war] do projeto web.
Após adicionar essas dependências ao arquivo [pom.xml], compilamos o projeto para que elas sejam baixadas.
4.3.3. Portação do projeto JSF / Glassfish para o projeto JSF / Tomcat
Copiamos todo o código do projeto JSF / Glassfish para o projeto JSF / Tomcat:
![]() |
- [1, 2, 3]: cópia das páginas da web do projeto antigo para o novo,
![]() |
- [1, 2, 3]: cópia dos códigos Java do projeto antigo para o novo. Há erros. Isso é normal. Nós os corrigiremos,
![]() |
- em [1], na aba [Files] do NetBeans, criamos uma subpasta [resources] na pasta [main],
- isso cria, na aba [Projects], o ramo [Other Sources] [3],
![]() |
- [1, 2, 3]: os arquivos de mensagens do projeto antigo são copiados para o novo projeto.
4.3.4. Alterações no projeto importado
Observamos que o código Java importado continha erros. Vamos analisá-los:
![]() |
- em [1], apenas o bean [Application] está com erro;
- em [2], o erro se deve exclusivamente à interface [IMetierLocal], que não existe mais. Aqui, pode ser surpreendente que a linha 20 não tenha sido sinalizada como erro. A anotação @EJB faz referência explícita a EJB e é reconhecida aqui. Isso se deve à presença da dependência [javaee-web-api-6.0] [3]. O Java EE 6 trouxe consigo uma arquitetura que permite implantar uma aplicação web baseada em EJB sem interface remota, em servidores que não possuem um contêiner EJB. Basta que o servidor forneça a dependência [javaee-web-api-6.0]. De fato, vemos que ela tem o escopo [provided] [3].
Aqui, não vamos utilizar a dependência [javaee-web-api-6.0]. Vamos removê-la, [1]:
![]() |
![]() |
Isso gera novos erros: [2]. Vamos começar pelos erros do bean [Form]:
![]() |
- no [1], as linhas com erros estão relacionadas à perda do pacote [javax]. Excluímos todas as linhas do [2]. As linhas com erros transformavam a classe [Form] em um bean de escopo de sessão (linhas 18-20 do [1]). Além disso, o bean [Application] era injetado na linha 25. Essas informações serão migradas para o arquivo de configuração de JSF e [faces-config.xml].
Passemos agora ao bean [Application]:
![]() |
Excluímos todas as linhas com erros do [1] e alteramos a interface das linhas 13 e 21 do [IMetierLocal] para [IMetier]. No [2], não há mais erros. No [1], excluímos as linhas 15 e 16 que transformavam a classe [Application] em um bean de escopo application.. Essa informação será migrada para o arquivo de configuração de JSF [faces-config.xml]. Também removemos a linha 20, que injetava uma referência da camada [métier] no bean. Agora, essa referência será inicializada pelo Spring. Já temos o arquivo de configuração necessário, que é o do projeto Spring / Métier. Vamos copiá-lo:
![]() |
- para [1, 2]; copiamos o arquivo de configuração do Spring do projeto Spring / Métier para o projeto Spring / JSF,
![]() |
Em [3], o resultado.
No bean [Application], é necessário utilizar esse arquivo de configuração para obter uma referência na camada [métier]. Isso é feito no método [init]:
package beans;
...
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application {
// camada de negócios
private IMetier metier;
...
public Application() {
}
@PostConstruct
public void init() {
try {
// instanciação da camada [métier]
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-config-metier-dao.xml");
metier = (IMetier) ctx.getBean("metier");
// armazenamos médicos e clientes no cache
...
} catch (Throwable th) {
...
}
...
}
- linha 20: os beans do arquivo de configuração do Spring são instanciados,
- linha 21: solicita-se uma referência ao bean de negócio, ou seja, à camada [métier].
De modo geral, a instanciação dos beans do Spring deve ser feita no método init do bean de escopo de aplicação. Existe outro método em que a instanciação dos beans é feita por um servlet do Spring. Isso implica alterar o arquivo [web.xml] e adicionar uma dependência do artefato [spring-web]. Não fizemos isso aqui para manter a consistência com o que havia sido utilizado nos códigos anteriores.
Removemos as anotações nas classes [Application] e [Form] que as transformavam em beans JSF. Essas classes devem permanecer como beans JSF. Em vez das anotações, utilizamos o arquivo de configuração de JSF [WEB-INF / faces.config.xml] para declarar os beans.
![]() |
Este arquivo agora tem o seguinte conteúdo:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<!-- o arquivo de mensagens -->
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
<!-- o bean applicationBean -->
<managed-bean>
<managed-bean-name>applicationBean</managed-bean-name>
<managed-bean-class>beans.Application</managed-bean-class>
<managed-bean-scope>application</managed-bean-scope>
</managed-bean>
<!-- o bean de formulário -->
<managed-bean>
<managed-bean-name>form</managed-bean-name>
<managed-bean-class>beans.Form</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
<managed-property>
<property-name>application</property-name>
<value>#{applicationBean}</value>
</managed-property>
</managed-bean>
</faces-config>
- as linhas 10 a 19 configuram o arquivo de mensagens. Essa era a única configuração que tínhamos no projeto JSF / EJB,
- as linhas 21 a 35 declaram os beans do aplicativo JSF. Esse era o método padrão com o JSF 1.x. O JSF 2 introduziu as anotações, mas o método do JSF e do 1.x ainda é compatível,
- linhas 21-25: declaram o bean applicationBean,
- linha 22: o nome do bean. Pode ser tentador usar o nome “application”. Isso deve ser evitado, pois é o nome de um bean predefinido do JSF,
- linha 23: o nome completo da classe do bean,
- linha 24: seu escopo,
- linhas 27-35: definem o bean form,
- linha 28: o nome do bean,
- linha 29: o nome completo da classe do bean,
- linha 30: seu escopo,
- linhas 31-34: definem uma propriedade da classe [beans.Form],
- linha 32: nome da propriedade. A classe [beans.Form] deve ter um campo com esse nome e o setter correspondente,
- linha 33: o valor do campo. Aqui, trata-se da referência ao bean applicationBean definido na linha 21. Portanto, estamos realizando aqui a injeção do bean de escopo application no bean de escopo session para que este tenha acesso aos dados do escopo application.
Mencionamos anteriormente que o campo [application] do bean [beans.Form] seria inicializado por meio de um setter. Portanto, é necessário adicioná-lo à classe [beans.Form], caso ainda não exista:
public void setApplication(Application application) {
this.application = application;
}
4.3.5. Teste do aplicativo
Nosso aplicativo agora está sem erros e pronto para os testes:
![]() |
- em [1], o projeto corrigido,
- em [2], compilamos,
- em [3], estamos executando-o. É necessário que o SGBD e o MySQL estejam em execução. O servidor Tomcat será então iniciado ([4]), caso ainda não esteja em execução, e, em seguida, a página inicial do aplicativo será exibida ([5]):
![]() |
A partir daí, encontramos o aplicativo em questão. Deixamos que o leitor verifique se ele está funcionando. Agora, vamos encerrar o aplicativo:
![]() |
- em [1], descarregamos o aplicativo,
- em [2], ela não está mais lá.
Vamos agora examinar os logs de do Tomcat:
As linhas 2 e 4 indicam uma falha ao encerrar o aplicativo. A linha 4 indica que há um risco provável de vazamento de memória. De fato, isso ocorre e, após algum tempo, o NetBeans fica inutilizável. Esse problema é particularmente irritante, pois é necessário reiniciar o NetBeans a cada nova execução do projeto. Esse problema já foi abordado no documento “Introdução ao Struts 2 por meio de exemplos” [http://tahe.developpez.com/java/struts2].
Há muitas informações na internet sobre esse erro. Ele ocorre quando se carrega e descarrega repetidamente uma aplicação do Tomcat. Após algum tempo, surge o erro java.lang.OutOfMemoryError: PermGen space. Parece que não há solução para evitar esse erro quando ele é causado por arquivos de terceiros (jar), como é o caso aqui. Nesse caso, é necessário reiniciar o Tomcat para que o erro desapareça.
No entanto, é possível adiar a ocorrência desse erro. Em primeiro lugar, aumenta-se o espaço da memória que sofreu o estouro.
![]() |
- em [1], acessa-se as propriedades do servidor Tomcat,
- em [2], na aba [Platform], definimos o valor da memória que está transbordando. Aqui, definimos 1 GB porque tínhamos uma memória total de 8 GB. É possível definir 512M (512 megabytes) com uma memória menor.
Em seguida, coloca-se o driver JDBC de MySQL em <tomcat>/lib, onde <tomcat> é o diretório de instalação do Tomcat.
![]() |
- em [1], nas propriedades do Tomcat, anota-se seu diretório de instalação <tomcat>,
- em <tomcat>/lib [2], coloca-se um driver JDBC mais recente do que MySQL e [3].
Em seguida, remova a dependência que o projeto tinha do driver JDBC em relação a MySQL e [4].
![]() |
Feito isso, testamos o aplicativo. Verificamos que é possível realizar carregamentos e descarregamentos repetidos do aplicativo. No entanto, os problemas de vazamento de memória não foram resolvidos. Eles simplesmente ocorrem mais tarde.
4.4. Conclusion
Transferimos a aplicação JSF / EJB / Glassfish para um ambiente JSF / Spring / Tomcat. Isso foi feito basicamente por meio de copiar e colar entre os dois projetos. Isso foi possível porque as tecnologias Spring e EJB3 apresentam grandes semelhanças. O EJB3 foi, de fato, criado depois que o Spring se mostrou mais eficiente do que o EJB2. O EJB3, então, incorporou as boas ideias do Spring.
4.5. Testes com o Eclipse
![]() |
- no [1], importam-se os três projetos Spring,
- no [2], seleciona-se o teste JUnit da camada [DAO] e o executa-se no [3],
![]() |
![]() |
- em [4], o teste é bem-sucedido,
- em [5], os logs do console.
![]() |
- em [6A] [6B], executa-se o cliente de console da camada [métier],
- em [7], a exibição da console obtida,
![]() |
- em [8] [9], executa-se o projeto web em um servidor Tomcat 7 [10],
![]() |
- e [11], a página inicial do aplicativo é exibida no navegador interno do Eclipse.





































































