4. Aplicação de exemplo – 02: rdvmedecins-jsf2-spring
Propomos agora portar a aplicação anterior para um ambiente Spring / Tomcat:
![]() |
Trata-se, de facto, de uma migração. Partiremos da aplicação anterior e adaptá-la-emos ao novo ambiente. Iremos comentar apenas as alterações. Estas dividem-se em três categorias:
- o servidor já não é o Glassfish, mas sim o Tomcat, um servidor leve que não possui um contentor 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 iremos recorrer frequentemente à função copiar/colar entre o projeto antigo e o novo, mantemos os projetos anteriores abertos no NetBeans:
![]() |
A utilização do framework Spring requer alguns conhecimentos que se encontram em [ref7] (ver página 166).
4.1. As camadas [DAO] e [JPA]
![]() |
4.1.1. O projeto NetBeans
Estamos a criar um projeto Maven do tipo [Java Application]:
![]() | ![]() | ![]() |
![]() |
- em [1], o projeto criado,
- em [2], o mesmo 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, 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 conjunto de ligações Apache Commons DBCP. Um conjunto de ligações é um conjunto de ligações abertas. Quando a aplicação necessita de uma ligação, solicita-a ao conjunto. Quando já não precisa dela, devolve-a. As ligações são abertas no arranque da aplicação e permanecem abertas durante todo o seu ciclo de vida. Isto evita o custo de aberturas e encerramentos repetidos das ligações. Este tipo de pool já existia no Glassfish, mas a sua utilização foi transparente para nós. Será também o caso aqui, mas temos de o instalar e configurar,
- linhas 52-75: para o Spring.
Vamos adicionar estas dependências e compilar o projeto:
![]() |
- em [1], compilamos o projeto, o que obrigará o Maven a descarregar as dependências,
- em [2], estas aparecem então no ramo [Dependencies]. São em grande número, uma vez que os frameworks Hibernate e Spring têm, por sua vez, inúmeras dependências. Mais uma vez, graças ao Maven, não precisamos de nos preocupar com elas. São descarregadas 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.
Depois de concluída a cópia, é necessário corrigir os erros.
4.1.2. O pacote [exceptions]
![]() |
A classe [RdvMedecinsExceptions] [1] apresenta erros devido ao pacote [javax], linha 4, que já não existe. Trata-se de um pacote específico do EJB. O erro da linha 6 decorre do erro da linha 4. Eliminam-se estas duas linhas. Isto elimina os erros [2].
4.1.3. O pacote [jpa]
![]() |
- em [1], a classe [Creneau] apresenta um erro devido à ausência do pacote de validação da linha [5]. Poderíamos ter adicionado este pacote às dependências do projeto. No entanto, durante os testes, o Hibernate lança uma exceção devido a ele. Como não é indispensável para a nossa aplicação, removemo-lo. Para corrigir a classe, basta eliminar todas as linhas com erro [2]. Fazemos isto para todas as classes com erros.
4.1.4. O pacote [dao]
Chegámos ao ponto seguinte:
![]() |
- em [1], os dois pacotes corrigidos,
- em [2], o pacote [dao]. Como já não existe o EJB, também já não existe o conceito de interface remota e local do EJB. Vamos eliminá-los, [3].
![]() |
- no [1], os erros da classe [DaoJpa] têm duas origens:
- a importação de um pacote associado a EJB (linhas 6-8);
- a utilização das interfaces local e remota que acabámos de eliminar.
Eliminamos as linhas com erros e utilizamos a interface [IDao] em vez das interfaces local e remota [2].
![]() |
No projeto EJB, a classe [DaoJpa] era um singleton e os seus métodos eram executados no âmbito de uma transação. Veremos que a classe [DaoJpa] será um bean gerido pelo Spring. Por predefinição, qualquer bean do Spring é um singleton. Eis a primeira propriedade. A segunda é obtida com a anotação @Transactional do Spring [3]:
![]() |
Feito isto, o projeto já não apresenta erros [4].
4.1.5. Configuração da camada [JPA]
No projeto EJB, tínhamos configurado a camada [JPA] com o ficheiro [persistence.xml]. Aqui temos uma camada [JPA] e, por isso, temos de criar esse ficheiro. No projeto EJB, tínhamos-o gerado com o Glassfish. Aqui, criamo-lo manualmente. A principal razão para isso é que parte da configuração do ficheiro [persistence.xml] é migrada para o próprio ficheiro de configuração do Spring.
Criamos o ficheiro [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 geridas pelo contentor EJB. O valor RESOURCE_LOCAL indica que a própria aplicação gere as suas transações. Será esse o caso aqui, através do Spring,
- linhas 4-7: os nomes completos das quatro entidades JPA. Isto é opcional, pois o Hibernate procura-as automaticamente no ClassPath do projeto.
É tudo. O nome do provedor JPA, as suas propriedades e as características JDBC da fonte de dados encontram-se agora no ficheiro de configuração do Spring.
4.1.6. O ficheiro de configuração do Spring
Já referimos que a classe [DaoJpa] é um bean gerido pelo Spring. Isto é feito através de um ficheiro de configuração. Este ficheiro irá incluir também a configuração do acesso à base de dados, bem como a gestão das transações. Deve estar no diretório ClassPath do projeto. Colocamo-lo no ramo [Other sources]:
![]() |
O ficheiro [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>
<!-- o gestor 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>
Trata-se de um ficheiro compatível com o Spring 2.x. Não procurámos utilizar as novas funcionalidades das versões 3.x.
- linhas 2-4: a baliza raiz <beans> do ficheiro de configuração. Não comentamos os vários atributos desta baliza. É importante ter cuidado ao copiar e colar, pois um erro num destes atributos provoca erros por vezes difíceis de compreender,
- 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. Esta fornece o serviço de «pool de ligações» de que falámos. É o [DBCP] do projeto Apache Commons DBCP [http://jakarta.apache.org/commons/dbcp/] que é aqui utilizado,
- linhas 25-28: para estabelecer ligações com a base de dados de destino, a fonte de dados precisa de saber o controlador JDBC utilizado (linha 25), o URL da base de dados (linha 26), o utilizador da ligação e a sua palavra-passe (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 gerir os contextos de persistência. A classe instanciada [LocalContainerEntityManagerFactoryBean] é fornecida pelo Spring. Necessita de um determinado número de parâmetros para ser instanciada, definidos nas linhas 11-20,
- linha 11: a fonte de dados a utilizar para obter ligações ao SGBD. Trata-se da fonte [DBCP] definida nas linhas 24 a 29,
- linhas 12 a 20: a implementação JPA a utilizar,
- linha 13: define o Hibernate como a implementação JPA a utilizar,
- linha 14: o dialeto SQL que o Hibernate deve utilizar com o SGBD de destino, neste caso o MySQL5,
- linha 16 (comentada): solicita que os comandos SQL executados pelo Hibernate sejam registados na consola,
- linha 17 (comentada): solicita que, ao iniciar a aplicação, a base de dados seja gerada (drop e create),
- linha 32: indica que as transações são geridas com anotações Java (também poderiam ter sido declaradas em spring-config.xml). Trata-se, em particular, da anotação @Transactional presente na classe [DaoJpa],
- linhas 33-35: definem o gestor de transações a utilizar,
- linha 33: o gestor de transações é uma classe fornecida pelo Spring,
- linha 34: o gestor de transações do Spring precisa de conhecer a classe EntityManagerFactory, que gere a camada JPA. É a classe definida nas linhas 10-21,
- linha 41: define a classe que gere as anotações de persistência do Spring,
- linha 38: define a classe Spring que gere, nomeadamente, a anotação @Repository, a qual torna uma classe assim anotada elegível para a conversão das exceções nativas do controlador JDBC do SGBD em exceções genéricas Spring do tipo [DataAccessException]. Esta conversão encapsula a exceção nativa JDBC num tipo [DataAccessException] com várias subclasses:

Esta conversão permite que o programa cliente lide com as exceções de forma genérica, independentemente do SGBD de destino. Não utilizámos a anotação @Repository no nosso código Java. Por isso, a linha 38 é desnecessária. Deixámo-la apenas a título informativo.
Terminámos o ficheiro de configuração do Spring. Este foi extraído da documentação do Spring. A sua adaptação a diversas situações resume-se frequentemente a duas alterações:
- a do banco de dados de destino: linhas 24-29,
- a da implementação JPA: linhas 12-20.
Ao executar o código, todos os beans do ficheiro 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 [1] e [2], ao copiar e colar o teste JUnit entre os dois projetos,
- em [3], o teste importado apresenta erros no seu novo ambiente.
![]() |
O erro sinalizado [1] refere-se à interface remota do EJB, que já não existe. Além disso, o código de inicialização do campo [dao] da linha 19 era uma chamada JNDI específica do EJB (linhas 25-28). Para instanciar o campo [dao] da linha 19, temos de utilizar o ficheiro de configuração do Spring. Isso faz-se da seguinte forma:
![]() |
- linha 21: o tipo da interface passou a ser [IDao],
- linha 28: instancia todos os beans declarados no ficheiro [spring-config-dao.xml], nomeadamente 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 a rosa):
![]() |
Quando os testes do cliente JUnit são executados, a camada [DAO] foi instanciada. Assim, é possível testar os seus métodos. Note-se que não é necessário um servidor para realizar este teste, ao contrário do teste do EJB [DAO], que exigiu o servidor Glassfish. Aqui, tudo é executado no próprio 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 analisar os registos do teste, tal como foi feito no teste do EJB:
- linhas 1-4: registos do Spring,
- linhas 5-10: registos do Hibernate,
- linha 11: o Spring indica todos os beans que instanciou. Encontramos aqui, em primeiro lugar, o bean [dao],
- linhas 12 e seguintes: os registos do teste JUnit,
- linhas 60-65: vê-se claramente a exceção provocada pela adição de um compromisso já existente na base de dados. Recorde-se que, com o EJB, não se verificou esta exceção devido a um problema de serialização.
A camada [dao] está operacional. Estamos agora a construir 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], limpo de tudo o que não queremos manter do [1]:
![]() |
4.2.2. As dependências do projeto
Na arquitetura:
![]() |
a camada [métier] assenta na camada [dao]. Por isso, adicionamos uma dependência do projeto anterior:
![]() |
- em [1] e [2], adicionamos uma dependência do projeto da camada [dao],
- no [3], esta dependência gerou outras dependências, as do projeto da camada [dao].
![]() |
- em [1] e [2], copiam-se os códigos-fonte Java do projeto EJB para o projeto Spring,
- para o [3]; os códigos-fonte importados apresentam erros no novo ambiente.
Começa-se por eliminar as interfaces remota e local da camada [métier], que já não existem em [4]:
![]() |
- no [5], os erros da classe [Metier] têm várias causas:
- a utilização do pacote [javax.ejb], que já não existe;
- a utilização da interface [IDaoLocal], que já não existe;
- a utilização das interfaces [IMetierRemote] e [IMetierLocal], que já não existem.
Nós
- eliminamos todas as linhas incorretas relacionadas com o 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], já não há erros.
Eliminámos as referências ao EJB, mas agora precisamos de recuperar as suas propriedades:
![]() |
- linha 22: tínhamos um singleton. Esta característica será obtida transformando a classe num bean gerido pelo Spring,
- linha 23: cada método decorria numa transação. Isto será conseguido com a anotação @Transactional do Spring,
- linhas 27-28: a referência na camada [DAO] era obtida por injeção do contentor EJB. Iremos utilizar uma injeção do Spring.
O código da classe [Metier] do projeto Spring passa, assim, a ter a seguinte forma:
![]() |
É tudo no que diz respeito ao código Java. O resto ocorre no ficheiro de configuração do Spring.
4.2.3. O ficheiro de configuração do Spring
Copiamos o ficheiro de configuração do Spring do projeto da camada [DAO] para o projeto da camada [métier]. Começamos por criar o ramo [Other Resources] no projeto da camada [métier], caso ainda não exista:
![]() |
- em [1], no separador [Files], criamos uma subpasta na pasta [main],
- em [2], deve chamar-se [resources],
- em [3], no separador [Projects], foi criado o ramo [Other Sources].
Podemos passar à operação de copiar/colar do ficheiro de configuração do Spring:
![]() |
- em [1], copiamos o ficheiro do projeto [DAO] para o projeto [métier] [2],
- em [3], o ficheiro copiado.
O ficheiro de configuração que foi copiado configura a camada [DAO]. Adicionamos-lhe 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 chama-se métier (atributo id) e é uma instância da classe [rdvmedecins.metier.service.Metier] (atributo class). Este bean será instanciado tal como os outros no arranque da aplicação.
Recorde-se 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 deste bean no ficheiro de configuração do Spring:
- linha 4: a baliza <property> serve para inicializar campos do bean instanciado. O nome do campo é indicado pelo atributo name. Será, portanto, o campo `dao` da classe [rdvmedecins.metier.service.Metier] que será instanciado. A instanciação será feita através de um método setDao, que deve existir. O valor que lhe será atribuído é o do atributo `ref`. Este 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 pretendíamos. O campo dao será inicializado pelo Spring através de um setter que temos de adicionar:
// setter
public void setDao(IDao dao) {
this.dao = dao;
}
Renomeamos o ficheiro de configuração do Spring para refletir as alterações:
![]() |
Estamos agora prontos para um teste. Retomamos o teste de consola utilizado para testar o EJB [Metier].
4.2.4. Teste da camada [métier]
O teste decorrerá com a seguinte arquitetura:
![]() |
Copiamos o teste de consola do projeto EJB para o projeto Spring:
![]() |
- para [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] já não é feita através de uma chamada JNDI, mas sim através da instanciação dos beans do ficheiro de configuração do Spring.
Corrigimos estes dois pontos:
![]() |
- linha 22: o ficheiro [spring-config-metier-dao.xml] é utilizado. Todos os beans deste ficheiro são então instanciados. Entre eles, encontram-se 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>
Estes dois beans representam as camadas [DAO] e [métier] da arquitetura do teste:
![]() |
Feito isto, o teste pode decorrer:
![]() |
Os registos do teste são, então, os seguintes:
- linhas 1-4: os registos do Spring e do Hibernate,
- linha 5: os beans instanciados pelo Spring. Destacam-se os beans DAO e de negócio,
- linhas 6-53: os registos do teste. Estão em conformidade com o que se obteve com o teste do projeto EJB. Remetemos o leitor para os comentários desse teste (parágrafo 3.5.3).
Construímos a camada [métier]. Passamos agora à última camada, a camada [web].
4.3. A camada [web]
![]() |
Para criar a camada [web], vamos proceder da mesma forma que para as outras duas camadas, copiando e colando a partir da camada [web] do projeto EJB.
4.3.1. O projeto NetBeans
Começamos por criar um projeto web:
![]() |
- no [1], criamos um novo projeto,
- em [2], um projeto Maven do tipo [Web Application],
- em [3], atribuímos-lhe um nome,
![]() |
- 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 eliminação de [index.jsp] e do pacote de [Source Packages].
4.3.2. As dependências do projeto
Vejamos a arquitetura do projeto:
![]() |
A camada [web] necessita das camadas [métier], [DAO] e [JPA]. Estas fazem parte dos dois projetos que acabámos de compilar. Daí a dependência em relação a cada um desses projetos:
![]() |
- no [1], adicionamos a dependência do projeto Spring / domínio de negócio,
- no [2], o projeto Spring / negócio foi adicionado. Como este, por sua vez, 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. Por isso, precisamos das bibliotecas do Java Server Faces. O servidor Tomcat não as possui. A dependência não terá, portanto, o âmbito (scope) [provided], como acontecia com o servidor Glassfish, mas sim o âmbito [compile], que é o âmbito por predefinição quando não se especifica nenhum âmbito.
Adicionamos estas 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-16 foram adicionadas ao ficheiro [pom.xml]. Trata-se das dependências em relação ao JSF. São as mesmas utilizadas no projeto EJB / Glassfish. Note-se que não possuem a baliza <scope>. Por conseguinte, têm, por predefinição, o âmbito [compile]. A biblioteca JSF será, assim, incorporada no arquivo [war] do projeto web.
Depois de adicionar estas dependências ao ficheiro [pom.xml], compilamos o projeto para que sejam descarregadas.
4.3.3. Portagem 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 web do projeto antigo para o novo,
![]() |
- [1, 2, 3]: cópia dos códigos Java do projeto antigo para o novo. Existem erros. É normal. Iremos corrigi-los,
![]() |
- em [1], no separador [Files] do NetBeans, cria-se uma subpasta [resources] na pasta [main],
- o que cria, no separador [Projects], o ramo [Other Sources] [3],
![]() |
- [1, 2, 3]: os ficheiros de mensagens do projeto antigo são copiados para o novo projeto.
4.3.4. Alterações no projeto importado
Referimos que o código Java importado continha erros. Vamos analisá-los:
![]() |
- em [1], apenas o bean [Application] está incorreto,
- em [2], o erro deve-se exclusivamente à interface [IMetierLocal], que já não existe. Aqui, pode surpreender o facto de a linha 20 não ser assinalada como errada. A anotação @EJB faz referência explícita a EJB e é aqui reconhecida. Isto deve-se à presença da dependência [javaee-web-api-6.0] [3]. O Java EE 6 trouxe consigo uma arquitetura que permite implementar uma aplicação web baseada em EJB sem interface remota, em servidores que não dispõem de um contentor EJB. Basta que o servidor forneça a dependência [javaee-web-api-6.0]. De facto, verifica-se que esta tem o âmbito [provided] [3].
Aqui, não vamos utilizar a dependência [javaee-web-api-6.0]. Vamos eliminá-la: [1]:
![]() |
![]() |
Isto gera novos erros [2]. Vamos começar pelos do bean [Form]:
![]() |
- no [1], as linhas com erros estão relacionadas com a perda do pacote [javax]. Eliminamos todas as linhas do [2]. As linhas com erros transformavam a classe [Form] num bean de âmbito de sessão (linhas 18-20 do [1]). Além disso, o bean [Application] era injetado na linha 25. Estas informações serão transferidas para o ficheiro de configuração de JSF e [faces-config.xml].
Passemos agora ao bean [Application]:
![]() |
Eliminam-se todas as linhas com erros do [1] e altera-se a interface das linhas 13 e 21 do [IMetierLocal] para [IMetier]. No [2], já não há erros. No [1], eliminámos as linhas 15-16 que transformavam a classe [Application] num bean de âmbito application.. Esta informação será transferida para o ficheiro de configuração de JSF [faces-config.xml]. Também eliminámos a linha 20, que injetava uma referência da camada [métier] no bean. Agora, esta será inicializada pelo Spring. Já dispomos do ficheiro de configuração necessário, que é o do projeto Spring / Métier. Copiamo-lo:
![]() |
- para [1, 2]; copiamos o ficheiro 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 este ficheiro de configuração para obter uma referência à camada [métier]. Isto é feito no seu método [init]:
package beans;
...
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application {
// camada de negócio
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 os médicos e os clientes em cache
...
} catch (Throwable th) {
...
}
...
}
- linha 20: os beans do ficheiro 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 um modo geral, a instanciação dos beans do Spring deve ser feita no método init do bean de âmbito da aplicação. Existe outro método em que a instanciação dos beans é feita por um servlet do Spring. Isto implica alterar o ficheiro [web.xml] e adicionar uma dependência do artefacto [spring-web]. Não o fizemos aqui para nos mantermos em consonância com o que tinha sido utilizado nos códigos anteriores.
Eliminámos as anotações nas classes [Application] e [Form] que as transformavam em beans JSF. Estas classes devem continuar a ser beans JSF. Em vez das anotações, utiliza-se então o ficheiro de configuração de JSF [WEB-INF / faces.config.xml] para declarar os beans.
![]() |
Este ficheiro tem agora 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 ficheiro 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 do 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-19 configuram o ficheiro de mensagens. Esta era a única configuração que tínhamos no projeto JSF / EJB,
- as linhas 21-35 declaram os beans da aplicação JSF. Este 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 continua a ser suportado,
- linhas 21-25: declaram o bean applicationBean,
- linha 22: o nome do bean. Poderíamos sentir-nos tentados a usar o nome «application». Deve evitar-se, pois é o nome de um bean predefinido do JSF,
- linha 23: o nome completo da classe do bean,
- linha 24: o seu âmbito,
- linhas 27-35: definem o bean «form»,
- linha 28: o nome do bean,
- linha 29: o nome completo da classe do bean,
- linha 30: o seu âmbito,
- linhas 31-34: definem uma propriedade da classe [beans.Form],
- linha 32: nome da propriedade. A classe [beans.Form] deve ter um campo com este nome e o setter correspondente,
- linha 33: o valor do campo. Aqui, trata-se da referência ao bean applicationBean definido na linha 21. Estamos, portanto, a realizar a injeção do bean de âmbito application no bean de âmbito session, para que este tenha acesso aos dados do âmbito application.
Já referimos anteriormente que o campo [application] do bean [beans.Form] seria inicializado através de um setter. Por isso, é necessário adicioná-lo à classe [beans.Form], caso ainda não exista:
public void setApplication(Application application) {
this.application = application;
}
4.3.5. Teste da aplicação
A nossa aplicação está agora sem erros e pronta para os testes:
![]() |
- em [1], o projeto corrigido,
- em [2], compilamo-lo,
- em [3], executamo-lo. É necessário que o SGBD e o MySQL estejam a ser executados. O servidor Tomcat será então iniciado ([4]), caso ainda não o estivesse, e, em seguida, a página inicial da aplicação será apresentada ([5]):
![]() |
A partir daqui, voltamos à aplicação em análise. Deixamos que o leitor verifique se esta funciona. Agora, vamos encerrar a aplicação:
![]() |
- em [1], descarregamos a aplicação,
- em [2], ela já não está lá.
Vejamos agora os registos do Tomcat:
As linhas 2 e 4 indicam uma falha ao encerrar a aplicação. A linha 4 indica que existe um risco provável de fuga de memória. De facto, isso acontece e, passado algum tempo, o NetBeans deixa de estar utilizável. Este problema é particularmente irritante, pois obriga a reiniciar o NetBeans sempre que se executa o projeto. Este problema já foi abordado no documento «Introdução ao Struts 2 através de exemplos» [http://tahe.developpez.com/java/struts2].
Encontram-se na Internet muitas informações sobre este erro. Este surge quando se carrega e descarrega repetidamente uma aplicação do Tomcat. Após algum tempo, obtém-se o erro java.lang.OutOfMemoryError: PermGen space. Parece não haver solução para evitar este erro quando este provém de arquivos de terceiros (jar), como é o caso aqui. É, portanto, necessário reiniciar o Tomcat para que o erro desapareça.
No entanto, é possível adiar a ocorrência deste erro. Em primeiro lugar, aumenta-se o espaço da memória que sofreu o estouro.
![]() |
- em [1], acede-se às propriedades do servidor Tomcat,
- em [2], no separador [Platform], definimos o valor da memória que está a transbordar. Neste caso, definimos 1 GB porque tínhamos uma memória total de 8 GB. Poderemos definir 512M (512 megabytes) se a memória for menor.
Em seguida, coloca-se o controlador JDBC de MySQL em <tomcat>/lib, sendo que <tomcat> é o diretório de instalação do Tomcat.
![]() |
- em [1], nas propriedades do Tomcat, anota-se o seu diretório de instalação <tomcat>,
- em <tomcat>/lib [2], coloca-se um controlador JDBC mais recente do que MySQL e [3].
Em seguida, remove-se a dependência que o projeto tinha do controlador JDBC em relação a MySQL e [4].
![]() |
Feito isto, testamos a aplicação. Verificamos que é possível carregar e descarregar a aplicação repetidamente. No entanto, os problemas de fuga de memória não estão resolvidos. Simplesmente surgem mais tarde.
4.4. Conclusion
Transferimos a aplicação JSF / EJB / Glassfish para um ambiente JSF / Spring / Tomcat. Isto foi feito essencialmente através de copiar e colar entre os dois projetos. Isto foi possível porque as tecnologias Spring e EJB3 apresentam grandes semelhanças. De facto, o EJB3 foi criado depois de o Spring ter demonstrado um desempenho superior ao dos EJB2. O EJB3 incorporou então 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 executa-se-o no [3],
![]() |
![]() |
- em [4], o teste é bem-sucedido,
- em [5], os registos da consola.
![]() |
- em [6A] [6B], executa-se o cliente da consola da camada [métier],
- em [7], o ecrã da consola obtido,
![]() |
- em [8] [9], executa-se o projeto web num servidor Tomcat 7 [10],
![]() |
- e [11], a página inicial da aplicação é apresentada no navegador interno do Eclipse.





































































