A Caelum atua no mercado com consultoria, desenvolvimento e ensino em computao. Sua equipe
participou do desenvolvimento de projetos em vrios clientes e, aps apresentar os cursos de vero de Java
na Universidade de So Paulo, passou a oferecer treinamentos para o mercado. Toda a equipe tem uma
forte presena na comunidade atravs de eventos, artigos em diversas revistas, participao em muitos
projetos open source como o VRaptor e o Stella e atuao nos fruns e listas de discusso como o GUJ.
Com uma equipe de mais de 60 profissionais altamente qualificados e de destaque do mercado, oferece
treinamentos em Java, Ruby on Rails e Scrum em suas trs unidades - So Paulo, Rio de Janeiro e
Braslia. Mais de 8 mil alunos j buscaram qualificao nos treinamentos da Caelum tanto em nas unidades
como nas prprias empresas com os cursos incompany.
O compromisso da Caelum oferecer um treinamento de qualidade, com material constantemente
atualizado, uma metodologia de ensino cuidadosamente desenvolvida e instrutores capacitados
tecnicamente e didaticamente. E oferecer ainda servios de consultoria gil, mentoring e desenvolvimento
de projetos sob medida para empresas.
Comunidade
Nossa equipe escreve constantemente artigos no Blog da Caelum que j conta
com 150 artigos sobre vrios assuntos de Java, Rails e computao em geral.
Visite-nos e assine nosso RSS:
blog.caelum.com.br
O GUJ maior frum de Java em lngua portuguesa, com 700 mil posts e 70 mil
usurios. As pessoas da Caelum participam ativamente, participe tambm:
www.guj.com.br
FJ-25:
Java e Orientao a
objetos
FJ-16:
FJ-26:
FJ-19:
FJ-31:
Java EE avanado e
Web Services
FJ-21:
FJ-91:
Arquitetura e Design de
Projetos Java
RR-71:
RR-75:
ndice
1 O curso
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 O sistema
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 Cadastrando Produtos
17
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
31
40
ii
7.9 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.10 Atualizando e removendo produtos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.11 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
7.12 Discusso em sala - VRaptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8 Refatorando os DAOs
62
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.6 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
9 Validando formulrios
72
9.1 Validator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
9.2 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
9.3 Para saber mais: Hibernate Validator
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
79
87
99
112
122
126
130
iv
C APTULO
O curso
Dizem que os homens nunca se contentam e, quando se lhes d alguma coisa, pedem sempre um pouco
mais. Dizem ainda que essa uma das melhores qualidades da espcie e que foi ela que tornou o homem
superior aos animais, que se contentam com o que tm.
A prola, John Steibeck.
com.br/
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
O frum do GUJ tambm uma excelente fonte de informao, e abriga o frum oficial do VRaptor: http:
//www.guj.com.br/
C APTULO
O sistema
Veremos como ser o sistema que iremos desenvolver e as tecnologias que utilizaremos.
C APTULO
http://www.mysql.com/
Depois de instalado e configurado, o MySQL pode ser acessado com o usurio chamado root e a senha
vazia (sem senha). Para testar, basta abrir o terminal e digitar o comando:
mysql -u root
Esse comando tenta fazer o login no mysql, informando o usurio root (-u root) e omitindo a senha, j que
esse usurio no tem senha.
Para nos comunicarmos com o banco de dados, seja para uma consulta ou para algum tipo de alterao de
dado, podemos usar a API que o Java SE disponibiliza, o JDBC. Um dos problemas do JDBC, quando usado diretamente, conforme mostramos no curso FJ-21, que ele muito trabalhoso alm de que precisamos gerenciar
muitos recursos importantes. Todas as informaes tm de ser passadas uma a uma.
Para evitar ter que ficar fazendo todas as chamadas ao JDBC e ganharmos tempo e produtividade, vamos
utilizar um framework para a persistncia que j traz muitas funcionalidades j implementadas. O framework
que utilizaremos ser o Hibernate e ele ser o responsvel por fazer as chamadas API do JDBC.
Vale lembrar que, tudo que visto aqui no curso, aplicvel a outros frameworks de persistncia, como
seria o caso de usar o iBatis ou ento o EclipseLink atravs de JPA. muito importante perceber como vamos
integrar todas as nossas ferramentas de maneira coesa e desacoplada.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
http://www.caelum.com.br/curso/fj25
http://www.hibernate.org
Depois de fazer o download desses dois zips, basta descompact-los e utilizar os JARs que esto dentro
de cada projeto. No exerccio abaixo veremos quais JARs iremos precisar. A partir do Hibernate 3.5, esses jars
todos vem numa nica distribuio, a core.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
J o XML, por mais que seja um pouco mais difcil em relao ao properties, permite que toda a configurao
seja feita nele. Por isso faremos nossa configurao no XML. O arquivo XML que o Hibernate procurar ser o
hibernate.cfg.xml e ele deve estar no classpath.
Para nosso caso, vamos seguir a conveno e criar o arquivo hibernate.cfg.xml na pasta src, dentro do
nosso projeto. O contedo do arquivo ser esse:
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/fj28</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
</session-factory>
</hibernate-configuration>
As configuraes que passamos nesse arquivo so parecidas quando queremos nos conectar a um banco
de dados. Para conectar em um banco de dados, precisamos informar qual o usurio, a senha, algumas
informaes do banco, como host, porta, etc.
Um detalhe importante da nossa configurao o banco de dados que foi passado. Na configurao
hibernate.connection.url foi passado o nome do database que utilizaremos. Para esse caso escolhemos
o database fj28.
Abaixo segue a descrio de todas as configuraes que usamos.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Existem muitas outras configuraes possveis. Para maiores detalhes, acesse a documentao do Hibernate.
C APTULO
Cadastrando Produtos
No captulo anterior, configuramos o Hibernate para acessar o banco de dados. Neste captulo, nosso
objetivo cadastrar produtos.
BigDecimal
Em aplicaes normais, no se deve usar doubles para representar nmeros reais (ex. dinheiro,
porcentagem) por causa de problemas de arredondamento. Podemos usar o BigDecimal que tem
uma preciso fixa, portanto os problemas de arredondamento so facilmente contornveis. No
usaremos BigDecimal pois ele um pouco mais complicado de usar:
double a, b, c, d;
d = a+b/c;
com BigDecimal:
BigDecimal a, b, c, d;
d = a.add(b.divide(c));
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Quando anotamos uma classe com @Entity, devemos indicar qual ser o campo de chave primria. Para o
nosso caso, vamos criar um campo id para ser a chave primria da tabela.
Para indicar que o campo id ser a chave primria, utilizaremos a anotao @Id. Um detalhe importante
que o nome da anotao no devido ao nome do atributo, ou seja, o atributo e a anotao tem o nome id, mas
a anotao sempre ser @Id, j o atributo de chave primria pode ser qualquer nome.
Vrias vezes no queremos passar o valor do id porque o valor desse campo ser gerado no banco de
dados. Podemos informar isso pro Hibernate atravs da anotao @GeneratedValue. Para o nosso caso, esse
campo ser do tipo auto_increment no banco de dados.
@Entity
public class Produto {
@Id @GeneratedValue
private Long id;
private String nome;
private String descricao;
private Double preco;
// getter e setters
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Pronto, agora nossa entidade est devidamente anotada. S temos que fazer mais uma coisa: avisar o
Hibernate dessa entidade, isso porque ele no encontra a entidade automaticamente. Para informar isso ao
Hibernate, utilizaremos o arquivo de configurao dele, o hibernate.cfg.xml.
O contedo do arquivo ficar assim:
<hibernate-configuration>
<session-factory>
<!-- todas as propriedades configuradas anteriormente -->
<!-- entidades -->
<mapping class="br.com.caelum.goodbuy.modelo.Produto" />
</session-factory>
</hibernate-configuration>
Repare que temos que utilizar o nome completo da classe, ou seja, o nome da classe mais o pacote.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Depois de criar esse objeto, vamos chamar o mtodo responsvel por ler o arquivo hibernate.cfg.xml, o
configure().
// L o hibernate.cfg.xml
configuration.configure();
Aps configurar, precisamos criar um objeto que cria as sesses, uma fbrica.
SessionFactory, que pode ser obtida com:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Com a SessionFactory em mos conseguimos criar uma uma Session. O cdigo completo fica:
1 public class TesteDeSessao {
2
public static void main(String[] args) {
3
AnnotationConfiguration configuration = new AnnotationConfiguration();
4
configuration.configure();
5
6
SessionFactory factory = configuration.buildSessionFactory();
7
Session session = factory.openSession();
8
}
9 }
A partir desse objeto session, basta passarmos uma entidade que ele se encarregar de transformar do
modelo orientado a objetos para o modelo relacional.
Produto produto = new Produto();
produto.setNome("Prateleira");
produto.setDescricao("Uma prateleira para colocar livros");
produto.setPreco(35.90);
Transaction tx = session.beginTransaction();
session.save(produto);
tx.commit();
Note que utilizamos uma transao para salvar o produto. Essa transao do pacote org.hibernate, e ela
necessria para que o insert seja efetivado.
4.5 - Exerccios
1) Crie a seguinte classe, que adiciona produtos no banco.
1 package br.com.caelum.goodbuy.testes;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import
import
import
import
org.hibernate.Session;
org.hibernate.SessionFactory;
org.hibernate.Transaction;
org.hibernate.cfg.AnnotationConfiguration;
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
18
19
20
21
22
23
24
25 }
2) Execute a classe que adiciona o produto. Repare no console o sql que foi gerado.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
9
10
11
12
13
14
15
16
17 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
19 }
2) Execute a classe que altera o produto. Repare no console o sql que foi gerado.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
16
17
18 }
tx.commit();
}
5) Execute a classe que remove o produto. Repare no console o sql que foi gerado.
C APTULO
Refatorando
5.1 - Analisando o cdigo atual
Se olharmos para o nosso projeto agora, temos a seguinte situao:
5.2 - Refactoring
Uma prtica bastante comum e difundida no meio da Orientao a Objetos a chamada Refatorao (Refactoring). Refatorar um programa melhorar seu cdigo sem alterar sua funcionalidade. A ideia da refatorao
no corrigir bugs, por exemplo, mas melhorar a estrutura de seu cdigo, deix-lo mais OO, mais legvel.
H diversos tipos de refatoraes. Renomear uma varivel para um nome mais claro um exemplo simples.
Quebrar um mtodo grande em vrios mtodos menores um outro exemplo um pouco mais complicado.
Vrias IDEs de Java possuem suporte refatoraes comuns. O Eclipse possui timas opes automticas
que utilizaremos nesse curso.
O livro mais famoso sobre refatoraes foi escrito por Martin Fowler e chama-se Refactoring - Improving the
Design of existing code. um catlogo com dezenas de tcnicas de refatorao e instrues de como e quando
execut-las.
17
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Olhando para o mtodo main, podemos divid-lo em trs partes. Uma parte a aquisio de uma Session,
outra a criao do produto, e a outra adio do produto no banco de dados. Com base nessa diviso, vamos
pedir para o Eclipse nos ajudar a refatorar, para fazer essa diviso de uma maneira segura e automtica.
Apenas para ficar claro essa diviso, veja o cdigo novamente, agora com os comentrios mostrando onde
comea cada parte.
1 package br.com.caelum.goodbuy.testes;
2
3 // imports
4
5 public class AdicaoDeProduto {
6
7
public static void main(String[] args) {
8
// Aquisio da sesso
9
AnnotationConfiguration configuration = new AnnotationConfiguration();
10
configuration.configure();
11
12
SessionFactory factory = configuration.buildSessionFactory();
13
Session session = factory.openSession();
14
15
// Criao do produto
16
Produto produto = new Produto();
17
produto.setNome("Prateleira");
18
produto.setDescricao("Uma prateleira para colocar livros");
19
produto.setPreco(35.90);
Captulo 5 - Refatorando - Aprendendo a refatorar - Pgina 18
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
20
21
22
23
24
25
26 }
Selecionando apenas a parte da aquisio da sesso, vamos utilizar as teclas de atalho do Eclipse para criar
um mtodo com essas instrues. As teclas de atalho so Alt + Shift + M, que representa a opo Extract
Method.
Apertando essas teclas, o Eclipse mostrar uma tela perguntado o nome do mtodo que ser criado. O
nome desse mtodo ser getSession().
Colocando o nome do mtodo e apertando OK, o mtodo ser criado e a parte do cdigo que usa essas
instrues j muda automaticamente, fazendo a chamada ao mtodo recm-criado.
5.4 - Exerccios
1) Altere a classe AdicaoDeProduto, que est no pacote br.com.caelum.goodbuy.testes, colocando os
comentrios para deixar claro a diviso das partes.
1 package br.com.caelum.goodbuy.testes;
2
3 // imports
4
5 public class AdicaoDeProduto {
6
7
public static void main(String[] args) {
8
// Aquisio da sesso
9
AnnotationConfiguration configuration = new AnnotationConfiguration();
10
configuration.configure();
11
12
SessionFactory factory = configuration.buildSessionFactory();
13
Session session = factory.openSession();
14
15
// Criao do produto
16
Produto produto = new Produto();
17
produto.setNome("Prateleira");
18
produto.setDescricao("Uma prateleira para colocar livros");
19
produto.setPreco(35.90);
20
21
// Adio do produto no banco de dados
22
Transaction tx = session.beginTransaction();
23
session.save(produto);
24
tx.commit();
25
}
26 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Com as linhas selecionadas, pressione as teclas Alt + Shift + M (Extract Method), para criar um mtodo
com essas linhas. Aps pressionar essas teclas, aparecer a seguinte tela:
4) A tela que est sendo mostrada est esperando por um nome de mtodo. Digite getSession como nome do
mtodo e pressione OK.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
5) Note como o Eclipse mudou seu cdigo, criando o mtodo getSession e fazendo a chamada a este mtodo.
A classe deve estar assim:
public class AdicaoDeProduto {
6) Faa o mesmo para as linhas de criao de produto e adio de produto no banco de dados. No final,
ficaremos com 3 mtodos, um chamado getSession, que j foi feito, outro chamado criaProduto e outro
Captulo 5 - Refatorando - Exerccios - Pgina 21
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
chamado gravaProduto. Lembre-se de utilizar as teclas de atalho que vimos nesse exerccio.
7) Para conferncia, ficaremos com a classe da seguinte forma:
1 package br.com.caelum.goodbuy.testes;
2
3 // imports
4
5 public class AdicaoDeProduto {
6
7
public static void main(String[] args) {
8
// Aquisio da sesso
9
Session session = getSession();
10
11
// Criao do produto
12
Produto produto = criaProduto();
13
14
// Adio do produto no banco de dados
15
gravaProduto(session, produto);
16
}
17
18
private static void gravaProduto(Session session, Produto produto) {
19
Transaction tx = session.beginTransaction();
20
session.save(produto);
21
tx.commit();
22
}
23
24
private static Produto criaProduto() {
25
Produto produto = new Produto();
26
produto.setNome("Prateleira");
27
produto.setDescricao("Uma prateleira para colocar livros");
28
produto.setPreco(35.90);
29
return produto;
30
}
31
32
private static Session getSession() {
33
AnnotationConfiguration configuration = new AnnotationConfiguration();
34
configuration.configure();
35
36
SessionFactory factory = configuration.buildSessionFactory();
37
Session session = factory.openSession();
38
return session;
39
}
40 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Mas agora que o mtodo main foi quebrado em mtodos menores, ser que precisamos mesmo de comentrios? Olhe novamente para o cdigo, e veja que os nomes dos nossos mtodos j dizem muito o que
queremos, ento nem precisamos mais de comentrios.
J que no precisamos mais dos comentrios, vamos retir-los do nosso cdigo:
public class AdicaoDeProduto {
public static void main(String[] args) {
Session session = getSession();
Produto produto = criaProduto();
gravaProduto(session, produto);
}
// outros mtodos
}
5.7 - Exerccios
1) Crie uma classe chamada CriadorDeSession no pacote br.com.caelum.goodbuy.infra. Essa classe ser a
responsvel por criar uma Session.
2) Selecione o mtodo getSession da classe AdicaoDeProduto.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Com as linhas selecionadas, pressione as teclas Alt + Shift + V (Move Method), para criar um mtodo
com essas linhas. Aps pressionar essas teclas, aparecer a seguinte tela:
4) A tela que est sendo mostrada est esperando pela classe que ficar com esse mtodo. Pressione o boto
Browse..., e busque pela classe CriadorDeSession, conforme a imagem abaixo:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
5) Aps encontrar a classe CriadorDeSession, pressione o boto OK para escolher essa classe, depois OK
novamente para confirmar a mudana do mtodo para essa classe.
6) Aparecer uma tela avisando que a visibilidade do mtodo getSession ser alterada public. Confirme essa
alterao pressionando o boto Continue.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
8) Crie uma classe chamada ProdutoDao no pacote br.com.caelum.goodbuy.dao. Essa classe ser a responsvel por encapsular as chamadas ao Hibernate.
9) Mova o mtodo gravaProduto para a classe ProdutoDao. Utilize as teclas de atalho que vimos nesse exerccio.
10) Repare como ficou a classe aps essa alterao:
public class AdicaoDeProduto {
11) Vamos refatorar agora a classe ProdutoDao, que est no momento assim:
public class ProdutoDao {
12) A classe AdicaoDeProduto agora no compila. Vamos trocar o acesso esttico por acesso a uma instncia
do dao:
Captulo 5 - Refatorando - Exerccios - Pgina 27
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
13) O mtodo gravaProduto recebe uma Session. Mas o ProdutoDao deveria ser responsvel pelo acesso a
dados, ento ela mesma deve ser responsvel por criar a session. Mude isso no ProdutoDao:
public class ProdutoDao {
14) Por ltimo, no precisamos criar uma Session dentro do mtodo gravaProduto, vamos fazer isso dentro do
construtor da classe, e colocar a Session como atributo:
public class ProdutoDao {
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
15) Agora a chamada do mtodo gravaProduto est correta, porm repare como est um pouco redundante:
new ProdutoDao().gravaProduto(produto);
Vamos fazer essa mudana de nome de mtodo utilizando o Eclipse. Pressione as teclas Alt + Shift + R
(rename) mtodo da classe ProdutoDao, e o Eclipse pedir o novo nome para o mtodo.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
16) (Opcional) Refatore as outras classes de teste, AlteracaoDeProduto e RemocaoDeProduto, para utilizar o
ProdutoDao.
C APTULO
VRaptor
6.1 - Sobre o VRaptor
VRaptor 3 um framework MVC para web focado no desenvolvimento gil.
Atravs da inverso de controle e injeo de depndencias, ele diminui drasticamente o tempo de trabalho
que seria perdido com o cdigo repetitivo: validaes, converses, direcionamentos, ajax e lookups.
31
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Repare que essa classe no herda nem implementa nenhuma interface. Essa classe um exemplo de um
POJO. Isso muito importante porque a classe est bem simples, bem legvel.
Agora como fazer para chamar essa lgica? Queremos cham-la assim pelo browser:
http://localhost:8080/goodbuy/mundo/boasVindas
Para chamar essa lgica, temos que anotar nossa classe que contm a regra de negcio, para indicar para
o controlador do VRaptor que essa classe deve ser controlada por ele.
A anotao que utilizaremos para indicar isso a @Resource.
Nossa classe ficar assim:
1
2
3
4
5
6
7
8
9
package br.com.caelum.goodbuy;
import br.com.caelum.vraptor.Resource;
@Resource
public class Mundo {
public void boasVindas() {
System.out.println("ol mundo!");
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
10
11
12 }
O VRaptor vai usar uma conveno para chamar o nosso mtodo: para executar o mtodo boasVindas() da
classe Mundo, posso chamar no browser a URI /mundo/boasVindas a partir da nossa aplicao, ou seja, a URI
http://localhost:8080/goodbuy/mundo/boasVindas.
1
2
3
4
5
6
7
8
9
10
11
12
package br.com.caelum.goodbuy;
import br.com.caelum.vraptor.Resource;
@Resource
public class Mundo {
public void boasVindas() {
System.out.println("ol mundo!");
}
}
6.5 - Exerccios
1) Crie a classe Mundo no pacote br.com.caelum.goodbuy.
1 package br.com.caelum.goodbuy;
2
3 public class Mundo {
4
5
public void boasVindas() {
6
System.out.println("ol mundo!");
7
}
8
9 }
boasVindas
4) Ao acessar essa url, mostrada uma pgina com erro 404 (pgina no encontrada). Isso ocorreu porque
ainda no criamos a pgina de resultado. Ela ser criada na prxima seo. O importante verificar o log
da aplicao, na view Console do Eclipse, e ver que a mensagem apareceu.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
5) (Opcional) Crie outro mtodo, parecido com o que fizemos, e acesse pelo browser. Se quiser, esse mtodo
pode ser em outra classe.
Conforme foi dito anteriormente, o VRaptor prefere convenes do que configuraes. A conveno de
redirecionamento de pginas aps a lgica a seguinte:
/WEB-INF/jsp/{nomeDoResource}/{lgica}.jsp
{nomeDoResource} = Mundo
{lgica} = boasVindas()
O VRaptor ir fazer algumas modificaes nesses valores. A primeira letra do nome do resource, que para
ns o nome da classe, ser passado para minsculo. O resto do nome continuar igual. J o nome da lgica
continuar igual, mas sem os parnteses. Ento o VRaptor ir considerar os seguintes valores:
{nomeDoResource} = mundo
{lgica} = boasVindas
E a pgina que ele buscar ser a seguinte:
/WEB-INF/jsp/mundo/boasVindas.jsp
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
6.7 - Exerccios
1) Crie uma pasta chamada jsp dentro da pasta WEB-INF. Cuidado, o nome da pasta deve ser com letras
minsculas.
2) Crie uma pasta chamada mundo dentro da pasta WEB-INF/jsp. Cuidado, o nome da pasta deve ser com letras
minsculas.
3) Crie um jsp chamado boasVindas.jsp dentro da pasta WEB-INF/jsp/mundo. Seu projeto deve ficar assim:
5) Vamos testar nossas alteraes. Inicie o Tomcat na view Servers, depois abra um browser e digite a seguinte
url: http://localhost:8080/goodbuy/mundo/boasVindas
6) Verifique se o console mostra a mensagem ol mundo!, e verifique se o browser exibe a mensagem Ol
Mundo.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy;
@Resource
public class Mundo {
public String boasVindas() {
return "ol mundo!";
}
}
Agora o mtodo boasVindas() no imprime nada, apenas retorna uma string. Aps a execuo desse
mtodo, essa string j estar disponvel na view.
muito importante perceber a legibilidade desse mtodo. uma classe java normal, e um mtodo que
retorna uma string.
Para que a view possa imprimir esse valor, vamos utilizar Expression Language no nosso jsp. Nosso jsp
ficar assim:
Mensagem vinda da lgica:
${string}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Como utilizamos o retorno do mtodo para disponibilizar informaes para a view, no temos como dar um
nome significativo para utilizar depois.
O VRaptor usa a conveno de buscar o tipo do retorno, nesse nosso caso uma String, e disponibilizar para
a view com o mesmo nome, apenas passando a primeira letra para minsculo. Por esse motivo que foi utilizado
a expression language ${string}.
package br.com.caelum.goodbuy;
@Resource
public class Mundo {
public String boasVindas() {
return "ol mundo!";
}
public List<String> paises() {
List<String> result = new ArrayList<String>();
result.add("Brasil");
result.add("Portugal");
result.add("Japo");
result.add("Canad");
result.add("Paraguai");
return result;
}
}
Note que agora o valor passado na expression language foi ${stringList}. O VRaptor identifica que devolvemos uma lista, e essa lista genrica, informando que o tipo de informao dentro da lista ser String.
Para listas, o VRaptor ir adotar a seguinte conveno:
{tipoDaLista}List
Nosso exemplo era de uma lista de String, ou seja, List<String>, que virou ${stringList}.
Captulo 6 - VRaptor - Disponibilizando colees para a view - Pgina 37
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
6.10 - Exerccios
1) Altere o mtodo boasVindas() da classe Mundo para retornar uma String.
1 package br.com.caelum.goodbuy;
2
3 @Resource
4 public class Mundo {
5
6
public String boasVindas() {
7
return "ol mundo!";
8
}
9
10 }
4) Crie um mtodo chamado paises() na classe Mundo. Esse mtodo deve retornar uma lista de Strings. Seu
cdigo ficar assim:
public List<String> paises() {
List<String> result = new ArrayList<String>();
result.add("Brasil");
result.add("Portugal");
result.add("Japo");
result.add("Canad");
result.add("Paraguai");
return result;
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
http://localhost:8080/goodbuy/mundo/paises
Sua pgina deve exibir o seguinte resultado:
7) (Opcional) Crie outro mtodo que mande alguma informao para a view. Dessa vez, tente retornar um valor
inteiro ou um valor decimal.
8) (Opcional) Crie outro mtodo que retorne um Produto, com algumas informaes preenchidas. Tente imprimir essas informaes no jsp.
9) (Opcional) Na view que exibe os pases, itere sobre a coleo e mostre cada pas em uma linha. Dica: use
o JSTL, tag c:forEach.
10) (Opcional) Um mtodo s pode retornar uma informao. Mas como faramos para disponibilizar para a view
mais de uma informao?
C APTULO
Com essa listagem pronta, podemos us-la no ProdutosController, e criar uma lgica que lista produtos.
Coloque a classe no pacote br.com.caelum.goodbuy.controller:
package br.com.caelum.goodbuy.controller;
public class ProdutosController {
public List<Produto> lista() {
ProdutoDao dao = new ProdutoDao();
return dao.listaTudo();
}
}
40
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Olhando para esse mtodo, que no incio tnhamos definido para ser nossa regra de negcio, ser que temos
apenas isso? Ser que apenas listamos os produtos?
Na verdade esse mtodo est fazendo mais tarefas do que deveria. Ele possui algumas responsabilidades
extras, por exemplo criar o ProdutoDao. A nica parte que a regra de negcio para esse caso a chamada
ao dao.listaTudo(). O resto apenas infraestrutura para permitir executar essa tarefa.
Avaliando esse problema, podemos perceber que estamos buscando recursos, nesse caso um dao, e portanto nos preocupando demasiadamente com os mesmos. Se buscar um recurso ruim, seja pela complexidade ou dificuldade no acesso, e tambm por no fazer parte da regra de negcio, que tal se ao invs de cri-lo,
recebssemos o mesmo?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package br.com.caelum.goodbuy.controller;
// imports
@Resource
public class ProdutosController {
private ProdutoDao dao;
public ProdutosController(ProdutoDao dao) {
this.dao = dao;
}
public List<Produto> lista() {
return dao.listaTudo();
}
}
Repare como seria mais simples nosso mtodo que executa a regra de negcio, e tambm como o mtodo
s executa a parte que realmente lhe interessa. A busca dos recursos necessrios para a execuo de nessa
lgica de negcios - nesse caso a criao do dao - no interessa pra essa classe.
Um ponto muito importante que temos que notar que para que essa classe funcione corretamente, precisamos de uma instncia de ProdutoDao. Se no tivermos essa instncia, impossvel executar nossa regra de
negcios, pois no existe construtor que no receba um ProdutoDao.
Como vimos antes, essa lgica redireciona para a jsp /WEB-INF/jsp/produtos/lista.jsp, e como retornamos uma lista de produtos, existe uma varivel chamada ${produtoList} disponvel no jsp.
Para podermos mostrar a listagem de um jeito mais fcil, vamos usar uma taglib da JSTL chamada
c:forEach, que capaz de iterar sobre uma lista passada.
<c:forEach items="${produtoList}" var="produto">
</c:forEach>
Usando essa taglib, vamos criar uma tabela com todos os produtos do sistema:
<table>
<thead>
<tr>
<th>Nome</th>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<th>Descrio</th>
<th>Preo</th>
</tr>
</thead>
<tbody>
<c:forEach items="${produtoList}" var="produto">
<tr>
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
</tr>
</c:forEach>
</tbody>
</table>
Essa caracterstica de, com um objeto em mos, saber que seus mtodos podem ser invocados sem
necessidade de nenhuma dependncia externa extra, parte do que foi chamado de Good Citizen: http:
//docs.codehaus.org/display/PICO/Good+Citizen
Em um objeto todas as dependncias devem ser passadas durante o processo de construo evitando
assim um possvel estado incosistente.
Como poderia ento criar o meu ProdutosController?
1 ProdutoDao dao = new ProdutoDao();
2 ProdutosController controller = new ProdutosController(dao);
Repare que logo aps a segunda linha ser executada, tenho a confiana de que o objeto referenciado pela
varivel controller est preparado para ter seus mtodos invocados.
Dessa maneira injetamos todas as nossas dependncias durante a instanciao do controlador, um trabalho
manual que ficar extremamente repetitivo e propcio a erro a medida que aumenta o nmero de dependncias
de nossa classe.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora a classe ProdutoDao tambm ser controlada pelo VRaptor. Dessa forma, quando o VRaptor for instanciar a classe ProdutosController, ele verificar que ela depende de um ProdutoDao, e criar uma instncia
da mesma.
O que acabamos de fazer foi criar uma maneira de passar as dependncias para onde for necessrio, ou
seja, um mecanismo de injetar as dependncias. Por esse motivo, esse conceito chamado de Injeo de
Dependncias.
O VRaptor est fortemente baseado nesse conceito, uma vez que at ele mesmo utiliza o mesmo para
conectar seus componentes internos. O conceito bsico por trs de Dependency Injection (DI) que voc no
deve buscar aquilo que deseja acessar, mas tais necessidades devem ser fornecidas para voc.
Isso se traduz, por exemplo, na passagem de componentes atravs do construtor de seus controladores.
Imagine que seu controlador de clientes necessita acessar um dao. Sendo assim, especifique claramente essa
necessidade.
Testando sua aplicao
Ao usarmos injeo de dependncias, ganhamos uma caracterstica muito boa na nossa aplicao:
a testabilidade. Se recebemos nossas dependncias no construtor, conseguimos passar implementaes falsas ou controladas e, assim, testar unitariamente nossas classes.
Voc pode encontrar um contedo mais aprofundado sobre testes no curso FJ-16 - Laboratrio
Java com Testes, XML e Design Patterns.
7.4 - Exerccios
1) Abra a classe ProdutoDao e adicione o mtodo para listar todos os produtos:
Captulo 7 - Criando o Controlador de Produtos - Injeo de Dependncias - Pgina 43
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
// imports
import br.com.caelum.vraptor.Resource;
@Resource
public class ProdutosController {
public List<Produto> lista() {
}
}
4) Crie o construtor que recebe uma instncia de ProdutoDao, e guarde essa instncia em um atributo.
1 package br.com.caelum.goodbuy.controller;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// imports
import br.com.caelum.vraptor.Resource;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
public ProdutosController(ProdutoDao dao) {
this.dao = dao;
}
public List<Produto> lista() {
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// imports
import br.com.caelum.vraptor.Resource;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
public ProdutosController(ProdutoDao dao) {
this.dao = dao;
}
public List<Produto> lista() {
return dao.listaTudo();
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
9) Deu uma exception! A informao mais importante dela est no final da Stacktrace:
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [br.com.caelum.goodbuy.dao.ProdutoDao] is defined:
Unsatisfied dependency of type [class br.com.caelum.goodbuy.dao.ProdutoDao]:
expected at least 1 matching bean
at org.springframework.beans.factory.support....
Ou seja, o Spring -- que usado pelo VRaptor para gerenciar dependncias -- no conhece o ProdutoDao.
Precisamos indicar para o VRaptor que ele tem que registrar o ProdutoDao no Spring, com a anotao
@Component.
10) Anote a classe ProdutoDao com @Component, para indicar que essa classe uma dependncia e pode ser
instanciada pelo VRaptor sempre que necessrio.
package br.com.caelum.goodbuy.dao;
// imports
import br.com.caelum.vraptor.ioc.Component;
@Component
public class ProdutoDao {
//...
}
11) Se acessarmos de novo a URI que executa o mtodo lista: http://localhost:8080/goodbuy/produtos/lista veremos a listagem de produtos:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy.controller;
// imports
@Resource
public class ProdutosController {
public void adiciona(Produto produto) {
ProdutoDao dao = new ProdutoDao();
dao.salva(produto);
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
produto, o VRaptor vai usar um parmetro da requisio chamado produto.nome. Para o campo descricao o
parmetro vai ser produto.descricao. Ou seja, a partir do nome do parmetro do mtodo, podemos usar pontos
para setar os campos do parmetro, desde que voc tenha getters e setters para eles.
O que vale o nome do parmetro: se o mtodo adiciona fosse
public void adiciona(Produto novoProduto) {...}
o VRaptor iria usar os parmetros novoProduto.nome, novoProduto.descricao, etc para popular os campos
do produto.
Reflection no nome dos parmetros
Infelizmente, o Java no realiza reflection em cima de parmetros, esses dados no ficam disponveis em bytecode (a no ser se compilado em debug mode, porm algo opcional). Isso faz com
que a maioria dos frameworks que precisam desse tipo de informo criem uma anotao prpria
para isso, o que polui muito o cdigo (exemplo no JAX-WS, onde comum encontrar um mtodo
como o acima com a assinatura void add(@WebParam(name="cliente) Cliente cliente).
O VRaptor tira proveito do framework Paranamer (http://paranamer.codehaus.org), que consegue
tirar essa informao atravs de pr compilao ou dos dados de debug, evitando criar mais uma
anotao. Alguns dos desenvolvedores do VRaptor tambm participam no desenvolvimento do
Paranamer.
Agora que sabemos como o VRaptor popula os parmetros do mtodo, vamos criar um formulrio para
poder cadastrar produtos. Esse formulrio dever ficar na pasta WEB-INF/jsp/produtos, com o nome de
formulario.jsp.
<form action="adiciona">
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao"></textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
Como nossas pginas ficam sempre na pasta WEB-INF, no temos como acessar diretamente pelo browser
um jsp. Para acessar a pgina que acabamos de criar, vamos criar um mtodo na classe ProdutosController
para chamar nossa pgina, usando a conveno do VRaptor.
@Resource
public class ProdutosController {
Captulo 7 - Criando o Controlador de Produtos - Criando o formulrio HTML - Pgina 48
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
http://localhost:8080/goodbuy/produtos/formulario
Aps preencher as informaes do formulrio e enviar, o VRaptor vai invocar o mtodo adiciona, passando
o Produto populado com os campos que digitamos. E como diz a conveno do VRaptor, precisamos criar o
arquivo adiciona.jsp, na pasta WEB-INF/jsp/produtos. Por enquanto vamos colocar o seguinte contedo:
Produto adicionado com sucesso!
7.7 - Exerccios
1) Crie o mtodo adiciona que recebe um Produto como parmetro e faa a chamada ao mtodo salva do dao
passando esse produto.
1 package br.com.caelum.goodbuy.controller;
2 public class ProdutosController {
3
4
//...
5
6
public void adiciona(Produto produto) {
7
dao.salva(produto);
8
}
9
10 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Abra o arquivo que acabamos de criar e coloque os campos para de digitao, assim como o boto e o
formulrio.
<form action="adiciona">
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao"></textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora que temos essa instncia de Result podemos us-la para mudar o resultado da lgica adiciona do
ProdutosController. O jeito de fazer isso dizer: Como resultado, quero redirecionar para a lgica ProdutosController.lista(). Ou em java:
result.redirectTo(ProdutosController.class).lista();
Captulo 7 - Criando o Controlador de Produtos - Redirecionar para listagem depois de adicionar - Pgina 52
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Repare que no foi necessrio criar arquivos de configurao para fazer essa mudana de conveno,
tudo feito no prprio mtodo. Ao olhar para o mtodo adiciona fica claro que o resultado dele no vai ser o
adiciona.jsp, vai ser a lgica lista do ProdutosController.
Como o redirecionamento para uma lgica do mesmo controller voc pode ainda usar um comando mais
enxuto:
result.redirectTo(this).lista();
redirecionamento do lado do servidor, usando o mtodo forwardTo: o servidor ir redirecionar internamente para a lgica especificada, desse modo possvel mudar o resultado de uma lgica sem mudanas
do lado do cliente. No use esse redirecionamento em requisies POST.
Alm disso, podemos redirecionar nossa requisio para pginas usando outros mtodos:
result.use(Results.logic()).
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
result.use(Results.page()).
status
codes
Headers
HTTP.
Ex:
result.use(Results.referer()): Usa como resultado a pgina que originou a requisio. Usa um Header
HTTP chamado Referer que no obrigatrio, e que removido por alguns Proxies e Firewalls, ento
cuidado ao usar esse resultado.
7.9 - Exerccios
1) Receba um Result no construtor do ProdutosController e guarde-o em um atributo.
import br.com.caelum.vraptor.Result;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
private final Result result;
public ProdutosController(ProdutoDao dao, Result result) {
this.dao = dao;
this.result = result;
}
//...
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Reinicie o tomcat para que ele carregue as suas mudanas, abra o formulrio e adicione mais um produto.
Repita essa operao e adicione mais produtos.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Comeando pela atualizao, primeiro precisamos de uma lgica que mostre um formulrio com os dados
do produto preenchidos. Vamos dar a essa lgica o nome de edita.
public class ProdutosController {
public void edita() {...}
}
Precisamos passar para essa lgica o id do produto que queremos editar, para que possamos carregar suas
informaes do banco de dados. E para passar o Produto carregado para a jsp, basta retorn-lo:
public class ProdutosController {
public Produto edita(Long id) {
return dao.carrega(id);
}
}
Agora podemos criar o formulrio de edio, passando os valores do produto carregado para os inputs:
<form action="altera">
<fieldset>
<legend>Editar Produto</legend>
<input type="hidden" name="produto.id" value="${produto.id }" />
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
Repare que precisamos tambm colocar um input hidden com o valor do produto.id para que saibamos
qual produto ser alterado. A action do form para a lgica altera, ento precisamos cri-la no nosso controller.
Como usamos os nomes dos campos do formulrio do jeito certo, podemos simplesmente receber um Produto
como argumento na lgica altera.
public class ProdutosController {
//...
public void altera(Produto produto) {
dao.atualiza(produto);
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Assim como na lgica adiciona, fizemos um POST num formulrio, ento ao invs de colocar uma jsp com
uma mensagem de Produto alterado com sucesso, vamos redirecionar para a listagem de produtos.
public class ProdutosController {
//...
public void altera(Produto produto) {
dao.atualiza(produto);
result.redirectTo(this).lista();
}
Agora precisamos de um jeito fcil de editar um produto. Vamos ento adicionar um link para editar o
produto, na listagem:
<c:forEach items="${produtoList}" var="produto">
<tr>
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
<td><a href="edita?id=${produto.id }">Editar</a></td>
</tr>
</c:forEach>
Vamos tambm remover produtos no nosso controller. Para remover um produto precisamos apenas do seu
id, ento basta criar a lgica:
public void remove(Long id) {
Produto produto = dao.carrega(id);
dao.remove(produto);
}
Tambm no faz muito sentido criar uma pgina de resultado para a remoo, ento vamos redirecionar
para a listagem.
public void remove(Long id) {
Produto produto = dao.carrega(id);
dao.remove(produto);
result.redirectTo(ProdutosController.class).lista();
}
Assim como criamos o link para a edio, vamos criar um link para a remoo:
<c:forEach items="${produtoList}" var="produto">
<tr>
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
<td><a href="edita?id=${produto.id }">Editar</a></td>
<td><a href="remove?id=${produto.id }">Remover</a></td>
</tr>
</c:forEach>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
7.11 - Exerccios
1) Crie as lgicas para edio de produtos no ProdutosController:
public Produto edita(Long id) {
return dao.carrega(id);
}
public void altera(Produto produto) {
dao.atualiza(produto);
result.redirectTo(this).lista();
}
para
incluir
um
link
para
edio,
no
arquivo
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
<td><a href="edita?id=${produto.id }">Editar</a></td>
</tr>
</c:forEach>
5) Reinicie o tomcat e acesse a listagem http://localhost:8080/goodbuy/produtos/lista. Escolha um dos produtos e clique em Editar.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
C APTULO
Refatorando os DAOs
8.1 - Injeo de dependncias no DAO
Existe algo errado com o ProdutoDao: ele est criando uma Session, que uma dependncia dele. Usando
injeo de dependncias, podemos receber a Session no construtor, e o VRaptor vai se encarregar de criar
essa Session e passar pro construtor do dao.
@Component
public class ProdutoDao {
private final Session session;
public ProdutoDao(Session session) {
this.session = session;
}
//...
}
Mas como falar que a Session pode ser usada como dependncia?
Poderamos fazer a mesma alterao que fizemos no ProdutoDao, anotando a classe Session do pacote
org.hibernate com @Component. Assim, a sesso do Hibernate tambm seria gerenciada pelo VRaptor. Mas
ser que podemos anotar uma classe do Hibernate? S se baixssemos o cdigo fonte e compilssemos na
mo, junto com nosso projeto. Daria muito trabalho e seria muito estranho, nosso projeto alterando outro projeto.
Para resolver isso, o VRaptor possui um mecanismo que nos auxilia a passar dependncias de outros
projetos para nossas classes. Basta criarmos uma classe que implementa a interface ComponentFactory. Essa
interface define um nico mtodo, o getInstance.
Criando essa classe, podemos definir a lgica de criao de um determinado objetos, e o VRaptor usar a
instncia retornada para passar como dependncia para outras classes. No nosso caso, podemos criar uma
classe que cria sesses do Hibernate.
J temos uma classe que cria uma classe que nos d uma instncia de Session: a CriadorDeSession, ento
para transform-la em um ComponentFactory basta implementar a interface. Mas o mtodo que nos d a Session
se chama getSession, e a interface espera que o mtodo se chame getInstance. E, por ltimo, anotamos a
classe com @Component para que o VRaptor saiba como cri-la. A classe ficar assim:
import br.com.caelum.vraptor.ioc.ComponentFactory;
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
public Session getInstance() {
62
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Note que utilizamos Generics para informar qual ser o tipo de objeto criado por essa classe.
O mtodo getInstance ainda est fazendo coisas demais: ele cria uma AnnotationConfiguration, configura, constri uma SessionFactory, e s ento abre uma sesso. No seria suficiente s abrir uma sesso
a partir da SessionFactory? A SessionFactory uma dependncia para criar uma Session. Ento vamos
receb-la no construtor da classe.
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
private final SessionFactory factory;
public CriadorDeSession(SessionFactory factory) {
this.factory = factory;
}
public Session getInstance() {
return factory.openSession();
}
}
Podemos usar a mesma idia e criar um ComponentFactory para SessionFactory, com o cdigo que removemos da CriadorDeSession. No podemos nos esquecer da anotao @Component:
@Component
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
public SessionFactory getInstance() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
SessionFactory factory = configuration.buildSessionFactory();
return factory;
}
}
Poderamos continuar esse processo, e criar uma ComponentFactory para AnnotationConfiguration tambm, mas vamos parar por aqui. Resumindo o que acabamos de fazer, chegamos a seguinte situao:
ProdutosController depende de ProdutoDao que depende de Session que depende de SessionFactory.
primeira vista pode parecer que fizemos muita coisa s pra resolver o problema da Session. Mas a
vantagem de utilizarmos essa abordagem que para as nossas prximas lgicas, no precisaremos fazer
Captulo 8 - Refatorando os DAOs - Injeo de dependncias no DAO - Pgina 63
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
nada. Se tivermos que criar 50 DAOs no nosso sistema, no precisamos mais se preocupar com Sessions,
basta receber uma no construtor.
8.2 - Exerccios
1) Faa a classe ProdutoDao receber uma Session no construtor. Remova o mtodo getSession:
@Component
public class ProdutoDao {
private final Session session;
public ProdutoDao(Session session) {
this.session = session;
}
//...
}
2) Modifique a classe CriadorDeSession. Faa com que essa classe implemente a interface ComponentFactory,
passando a Session como tipo genrico. Renomeie o mtodo getSession para getInstance, e remova o
static.
import br.com.caelum.vraptor.ioc.ComponentFactory;
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
public Session getInstance() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
SessionFactory factory = configuration.buildSessionFactory();
Session session = factory.openSession();
return session;
}
}
3) A classe CriadorDeSession est fazendo coisas demais. Recorte a parte que cria uma SessionFactory e
receba uma SessionFactory no construtor. A classe deve ficar assim:
1 package br.com.caelum.goodbuy.infra;
2
3 // imports
4
5 public class CriadorDeSession implements ComponentFactory<Session> {
6
7
public CriadorDeSession(SessionFactory factory) {
8
}
9
10
public Session getInstance() {
11
Session session = factory.openSession();
12
return session;
Captulo 8 - Refatorando os DAOs - Exerccios - Pgina 64
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
13
14
15 }
4) Aps ter criado o construtor, guarde o parmetro factory em um atributo. Utilize as teclas de atalho do
Eclipse. Pressione as teclas Ctrl + 1 (quick fix) no parmetro factory, e o Eclipse nos mostrar algumas
sugestes. Selecione a primeira opo, Assign parameter to new field.
5) Anote a classe com @Component, para indicar que essa classe uma dependncia e pode ser instanciada
pelo VRaptor sempre que necessrio.
6) Crie a classe CriadorDeSessionFactory no pacote br.com.caelum.goodbuy.infra que implementa a
interface ComponentFactory<SessionFactory>.
1 package br.com.caelum.goodbuy.infra;
2
3 // imports
4
5 public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
6
7 }
7) A classe no compila, pois falta implementar o mtodo que foi definido na interface. Para isso utilize as
teclas de atalho do Eclipse: coloque o cursor no nome da classe e digite Ctrl+1. Selecione a opo Add
unimplemented methods.
Coloque a criao da fbrica de sesses que voc tinha recortado da outra classe nesse mtodo.
1 package br.com.caelum.goodbuy.infra;
2
3 // imports
4
5 public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
6
7
public SessionFactory getInstance() {
8
AnnotationConfiguration configuration = new AnnotationConfiguration();
9
configuration.configure();
10
11
SessionFactory factory = configuration.buildSessionFactory();
Captulo 8 - Refatorando os DAOs - Exerccios - Pgina 65
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
12
13
14
15 }
return factory;
}
8) Anote a classe com @Component, para indicar que essa classe uma dependncia e pode ser instanciada
pelo VRaptor sempre que necessrio.
9) As classes de teste que tnhamos criado no esto compilando mais, pois mudamos o construtor do
ProdutoDao. Mude as chamadas, usando as ComponentFactories para criar as dependncias. Note que na
aplicao web, o VRaptor que vai se encarregar executar esse cdigo.
SessionFactory factory = new CriadorDeSessionFactory().getInstance();
10) Acesse o sistema e veja que ele continua funcionando como antes.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Para alguns casos, um componente deve durar para todas as requisies, e esse componente pode ser
utilizado por todos os clientes. Esse caso poderia ser a fbrica de sesses do Hibernate. Essa fbrica ficaria
nica para toda a aplicao, e qualquer cliente que precisar de uma sesso, usaramos essa fbrica.
Para cada cenrio acima, o VRaptor definiu um tempo de vida. Esse tempo de vida no VRaptor chamado
de escopo. O VRaptor define quatro escopos, e para utiliz-los, basta anotarmos nossos componentes com
uma das seguintes anotaes:
@RequestScoped - o componente ser criado a cada requisio. o escopo padro dos componentes.
@SessionScoped - o componente ser criado uma vez por sesso (HttpSession) de usurio
@ApplicationScoped - o componente ser criado uma nica vez na aplicao.
@PrototypeScoped - o componente ser criado toda vez que for solicitado.
Para nossa classe CriadorDeSessionFactory, queremos ter apenas uma instncia dessa classe para toda
nossa aplicao, ento vamos anot-la com @ApplicationScoped.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package br.com.caelum.goodbuy.infra;
// imports
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
public SessionFactory getInstance() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
return configuration.buildSessionFactory();
}
}
Agora o VRaptor vai criar apenas uma instncia dessa classe. Mas repare que o mtodo getInstance cria
toda vez uma fbrica nova. Precisamos refatorar para que seja criada apenas uma fbrica.
Para resolver esse problema, poderamos criar um atributo do tipo SessionFactory e colocar no construtor
da classe a criao da fbrica.
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory>{
private final SessionFactory factory;
public CriadorDeSessionFactory() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
factory = configuration.buildSessionFactory();
}
Captulo 8 - Refatorando os DAOs - Escopos definidos pelo VRaptor - Pgina 67
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
os mtodos anotados com @PostConstruct sero executados assim que o escopo for iniciado, ou seja, no
comeo da requisio, sesso de usurio ou da aplicao.
os mtodos anotados com @PreDestroy sero executados assim que o escopo for finalizado, ou seja, no
final da requisio, sesso de usurio ou da aplicao.
Desse modo, podemos usar um mtodo anotado com @PostConstruct para criar a fbrica de sesses,
e como uma boa prtica fechar todos os recursos que abrimos, devemos criar um mtodo anotado com
@PreDestroy que vai fechar a fbrica.
Fazendo essas modificaes, nossa classe ficaria assim:
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory>{
private SessionFactory factory;
@PostConstruct
public void abre() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
this.factory = configuration.buildSessionFactory();;
}
public SessionFactory getInstance() {
return this.factory;
}
@PreDestroy
public void fecha() {
this.factory.close();
}
}
Note que no precisamos de um bloco esttico ou um singleton, pois o VRaptor que vai se encarregar
de criar apenas uma instncia da fbrica, e quando o recurso no for mais necessrio, o VRaptor vai fechar a
fbrica.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy.infra;
// imports
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
private final SessionFactory factory;
private Session session;
public CriadorDeSession(SessionFactory factory) {
this.factory = factory;
}
@PostConstruct
public void abre() {
this.session = factory.openSession();
}
public Session getInstance() {
return this.session;
}
@PreDestroy
public void fecha() {
this.session.close();
}
}
Note que criamos um atributo chamado session, que guardar uma sesso do Hibernate. Temos que
lembrar que componentes que controlados pelo VRaptor sempre tm um escopo, mesmo no estando anotado.
O escopo padro para componentes o de requisio, ou seja, daria na mesma anotar essa classe com
@RequestScoped. Desse modo, uma sesso vai ser aberta no comeo da requisio, e ser fechada assim que
acabar a requisio
8.6 - Exerccios
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
// imports
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
//...
}
2) Refatore essa classe para que seja criado apenas uma instncia da fbrica.
1 package br.com.caelum.goodbuy.infra;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// imports
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory>{
private SessionFactory factory;
@PostConstruct
public void abre() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
this.factory = configuration.buildSessionFactory();;
}
public SessionFactory getInstance() {
return this.factory;
}
@PreDestroy
public void fecha() {
this.factory.close();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
}
@PostConstruct
public void abre() {
this.session = factory.openSession();
}
public Session getInstance() {
return this.session;
}
@PreDestroy
public void fecha() {
this.session.close();
}
}
C APTULO
Validando formulrios
Quando temos um formulrio para adicionar dados no nosso sistema, geralmente precisamos verificar se os
dados esto corretos e consistentes. Por exemplo no gostaramos que o usurio digitasse vinte e trs reais
no campo preo, ou que o campo nome fosse vazio.
Ento, antes de salvar esses dados no banco precisamos verificar se tudo est como esperamos. Chamamos isso de validao. Existem duas maneiras de fazer validao:
Validao do lado do cliente: verificamos os dados antes de mandar os dados para o servidor, assim o
feedback da validao instantneo.
Validao do lado do servidor: os dados so enviados para o servidor, e se houver erros, o servidor
devolve esses erros para o cliente.
A validao do lado do cliente geralmente feita usando javascript, impedindo que o formulrio seja submetido caso acontea algum erro. A do lado do servidor feita em Java, e o VRaptor pode te ajudar a faz-la.
9.1 - Validator
O VRaptor possui um componente, chamado Validator, para ajudar a fazer validaes do lado do servidor.
Com ele, voc pode executar a lgica de validao, e caso haja algum erro, especificar para onde deve ser
redirecionada a requisio. Para obter um Validator basta receb-lo no construtor do seu Controller:
import br.com.caelum.vraptor.Validator;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
private final Result result;
private final Validator validator;
public ProdutosController(ProdutoDao dao, Result result, Validator validator) {
this.dao = dao;
this.result = result;
this.validator = validator;
}
//...
}
Com essa instncia do Validator podemos adicionar erros de validao para o caso em que os dados da
requisio no vieram corretos. Geralmente validamos quando estamos adicionando ou atualizando algo no
banco, ento vamos executar a validao no mtodo adiciona.
72
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
maneira fluente: voc diz o que quer que seja verdade, e caso no seja adiciona uma mensagem
internacionalizada:
public void adiciona(final Produto produto) {
validator.checking(new Validations() {{
that(produto.getNome() != null && produto.getNome().length() >= 3,
"produto.nome","nome.obrigatorio");
that(produto.getDescricao() != null && produto.getDescricao().length() <= 40,
"produto.descricao", "descricao.obrigatoria");
that(produto.getPreco() != null && produto.getPreco() > 0.0,
"produto.preco", "preco.positivo");
}});
dao.salva(produto);
result.redirectTo(this).lista();
}
Como a mensagem internacionalizada, voc precisa colocar essas chaves no seu arquivo messages.properties:
nome.obrigatorio = Nome obrigatrio e precisa ter mais de 3 letras
Captulo 9 - Validando formulrios - Validator - Pgina 73
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Em ambas as maneiras voc precisa especificar pra qual lgica voltar quando der erro de validao. No
nosso caso, ao dar erro queremos voltar pro formulrio, ento fazemos:
validator.onErrorUsePageOf(ProdutosController.class).formulario();
A lgica de redirecionamento a mesma que se voc estivesse usando o result.of(). Voc deve colocar essa
chamada logo no final da sua lgica de validao, no momento em que, se houver algum erro, a execuo deve
parar e voltar para o formulrio, mostrando os erros.
Quando existem erros de validao, o VRaptor disponibiliza para a jsp um atributo chamado ${errors}, que
contm a lista dos erros que aconteceram. Ento voc usar o seguinte cdigo para mostrar as mensagens no
seu jsp:
<ul>
<c:forEach items=${errors} var="error">
<li>${error.category} - ${error.message}</li>
</c:forEach>
</ul>
Alm disso, se quisermos que o formulrio volte preenchido quando houver algum erro de validao, precisamos passar como valor dos inputs, o que foi mandado para a requisio:
<form action="adiciona">
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
9.2 - Exerccios
1) Receba um Validator no construtor do ProdutosController e guarde-o num atributo:
import br.com.caelum.vraptor.Validator;
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Resource
public class ProdutosController {
private final ProdutoDao dao;
private final Result result;
private final Validator validator;
public ProdutosController(ProdutoDao dao, Result result, Validator validator) {
this.dao = dao;
this.result = result;
this.validator = validator;
}
//...
}
2) Modifique o mtodo adiciona para que inclua a validao dos campos do produto. Se preferir, faa a
validao na forma fluente.
public void adiciona(final Produto produto) {
if (produto.getNome() == null || produto.getNome().length() < 3) {
validator.add(new ValidationMessage(
"Nome obrigatrio e precisa ter mais de 3 letras", "produto.nome"));
}
if (produto.getDescricao() == null || produto.getDescricao().length() > 40) {
validator.add(new ValidationMessage(
"Descrio obrigatria no pode ter mais que 40 letras",
"produto.descricao"));
}
if (produto.getPreco() <= 0) {
validator.add(new ValidationMessage(
"Preo precisa ser positivo", "produto.preco"));
}
validator.onErrorUsePageOf(ProdutosController.class).formulario();
dao.salva(produto);
result.redirectTo(this).lista();
}
3) Abra o arquivo header.jspf, e adicione o cdigo para mostrar os erros de validao, dentro da div com
id="erros
.
<div id="erros">
<ul>
<c:forEach items="${errors}" var="error">
<li>${error.category } - ${error.message }</li>
</c:forEach>
</ul>
</div>
4) Adicione o cdigo para que o formulrio continue preenchido quando der erro de validao:
<form action="adiciona">
Captulo 9 - Validando formulrios - Exerccios - Pgina 75
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
6) (Opcional) O mtodo atualiza tambm precisa de validao. Voc consegue aproveitar o cdigo do adiciona
para fazer isso? Ele deve redirecionar para o mesmo lugar em caso de erro?
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
preco;
Com nosso modelo anotado, podemos substituir as validaes que fizemos pelo Hibernate Validator. O interface Validator do VRaptor possui um mtodo chamado validate que valida qualquer objeto usando o Hibernate
Validator. Assim, se voc chamar:
validator.validate(produto)
todos os erros de validao que ocorrerem sero adicionados ao validator automaticamente. Assim podemos substituir as validaes que tnhamos feito antes, pelas equivalentes do Hibernate Validator.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Entity
public class Produto {
@Id @GeneratedValue
private Long id;
@NotNull
@Length(min=3)
private String nome;
@NotNull
@Length(max=40)
private String descricao;
@Min(0)
private Double
preco;
3) Mude a lgica de validao do mtodo adiciona do ProdutosController para usar o Hibernate Validator:
@Resource
public class ProdutosController {
//...
public void adiciona(final Produto produto) {
validator.validate(produto);
validator.onErrorUsePageOf(ProdutosController.class).formulario();
dao.salva(produto);
result.redirectTo(this).lista();
}
}
4) Acesse o browser e adicione algum produto invlido, para ver as mensagens de validao.
As mensagens esto em ingls e por enquanto no possvel internacionaliz-las, mas isso ser implementado em prximas verses do VRaptor.
C APTULO
10
REST
10.1 - O que REST
REST um conjunto de restries que define um padro arquitetural com caractersticas especficas: benefcios e dificuldades determinadas aparecero ao implementar um sistema seguindo o padro REST.
restfulie-java
Substantivos: Recursos
Substantivos so os nomes dos recursos do seu sistema. Quando fazemos requisies Web, precisamos
falar o caminho da mesma, a URI (Unified Resource Identifier ), ou seja, o identificador nico de um recurso.
Ento ao criar as URIs do nosso sistema devemos levar em conta que elas representam recursos, no
aes. Em sistemas REST, nossas URIs devem conter apenas substantivos, que so nossos recursos:
/produtos/adiciona no boa, pois contm um verbo e no est identificando um recurso, mas sim uma
operao. Para representar a adio de um produtos podemos usar a URI /produtos com o mtodo HTTP
POST, que representa que estamos adicionando alguma informao no sistema.
79
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Verbos: Operaes
Uma das caracterstas mais importantes de REST que voc tenha um conjunto pequeno e fixo de operaes bem definidas, gerando uma interface uniforme. Desse modo, para duas aplicaes conversarem elas
no precisam implementar diversas operaes: basta usar as operaes definidas. Perceba que adicionar um
produto e adicionar um usurio no sistema so operaes equivalentes.
O protocolo HTTP possui sete operaes -- os mtodos GET, POST, PUT, DELETE, HEAD, OPTIONS e
TRACE. Duas delas (GET e POST) j so bem conhecidas e utilizadas: GET quando clicamos em links ou
digitamos o endereo no navegador, e POST quando preenchemos formulrios de cadastro. Cada mtodo tem
uma semntica diferente e juntando o mtodo URI deveramos conseguir representar todas as aes do nosso
sistema. As semnticas principais so:
GET - recupera informaes sobre o recurso identificado pela URI. Ex: listar produtos, visualizar o produto
45. Uma requisio GET no deve modificar nenhum recurso do seu sistema, ou seja, no deve ter
nenhum efeito colateral, voc apenas recupera informaes do sistema.
POST - adiciona informaes usando o recurso da URI passada. Ex: adicionar um produto. Pode adicionar informaes a um recurso ou criar um novo recurso.
PUT - adiciona (ou modifica) um recurso na URI passada. Ex: atualizar um produto. A diferena fundamental entre um PUT e um POST que no POST a URI significa o lugar que vai tratar a informao, e no
PUT significa o lugar em que a informao ser armazenada.
DELETE - remove o recurso representado pela URI passada. Ex: remover um produto.
HEAD, OPTIONS e TRACE - recuperam metadados da URI passada. Respectivamente o Header, quais
mtodos so possveis e informaes de debug.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
import br.com.caelum.vraptor.Path;
@Resource
public class ProdutosController {
@Path("/produtos")
public List<Produto> lista() {
return dao.listaTudo();
}
}
Dessa forma, o mtodo lista no vai mais estar acessvel pela URI /produto/lista, ele apenas poder ser
acessado pela URI /produtos.
Com a anotao @Path ainda possvel usar templates para extrair parmetros a partir da URI. Por exemplo,
no nosso mtodo edita, precisamos passar um query parameter para falar qual o produto que queremos
editar:
/produto/edita?id=10
As URIs devem identificar recursos (alm de no conter verbos, mas vamos tir-los em breve), e o id algo
que identifica o recurso que queremos acessar, ento ficaria melhor se passssemos esse id dentro da URI,
como por exemplo:
/produto/10/edita
ou seja, uma URI que representa a edio do produto de id 10. Para extrair esse 10 da URI, podemos
colocar um template no nosso @Path. Para isso basta colocar o nome do parmetro que queremos extrair entre
chaves, dentro da URI do @Path, e ento receber o parmetro como argumento do mtodo:
@Path("/produto/{id}/edita")
public Produto edita(Long id) {
return dao.carrega(id);
}
Assim possvel criar URIs mais representativas, que identificam verdadeiramente recursos especficos.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Get;
@Resource
public class ProdutosController {
@Path("/produtos")
@Get
public List<Produto> lista() {
return dao.listaTudo();
}
}
Se quisermos permitir outros verbos HTTP, podemos usar as anotaes @Post, @Put, @Delete, @Trace e
@Head. A partir do momento que colocamos uma anotao de verbo HTTP no nosso mtodo, ele no poder
ser acessado pelos outros verbos. Ento se tentarmos fazer uma requisio POST /produtos, ela no vai cair
no mtodo lista, a requisio vai retornar um status 405 (Mtodo no suportado), que quer dizer que existe
um recurso nessa URI, mas ele no suporta o verbo HTTP da requisio.
Assim, podemos colocar mais de um mtodo que responda mesma URI, desde que os verbos HTTP sejam
diferentes. Por exemplo, podemos fazer com que o mtodo adiciona tambm responda URI /produtos. Mas
o mtodo adiciona no idempotente: se eu repetir duas vezes uma requisio a esse mtodo, vou adicionar
dois produtos ao sistema. Estamos adicionando produtos, mas no sabemos qual vai ser a URI dele, ento
podemos colocar o verbo POST.
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Post;
@Resource
public class ProdutosController {
@Path("/produtos")
@Post
public void adiciona(Produto produto) {
//...
}
@Path("/produtos")
@Get
public List<Produto> lista() {
return dao.listaTudo();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
A URI /produtos representa a lista de todos os produtos do sistema. E as operaes possveis so:
GET /produtos => recupera a lista de todos os produtos. Mtodo lista.
POST /produtos => adiciona um produto na lista de todos os produtos. Mtodo adiciona.
As URIs do tipo /produtos/42 representam um produto especfico, no caso de id 42. As operaes possveis
so:
GET /produtos/4 => mostra o produto de id 4. Mtodo edita.
PUT /produtos/10 => atualiza o produto de id 10. Mtodo atualiza.
DELETE /produtos/3 => remove o produto de id 3. Mtodo remove.
Nem todas as URIs precisam representar recursos fixos, mas elas precisam semanticamente representar o
mesmo tipo de recurso. Por exemplo a URI /produtos/ultimo pode representar o ltimo produto adicionado e
a URI /produtos/maisVendidos pode representar uma lista dos produtos mais vendidos, que so recursos bem
definidos. No nosso caso precisamos de uma URI para identificar o formulrio de adio de um produto. Vamos
ento usar a seguinte:
GET /produtos/novo => mostra o formulrio para adicionar um novo produto. Mtodo formulario.
Com essa especificao de operaes no recurso Produto, podemos usar as anotaes vistas anteriormente
para deixar o ProdutosController de acordo com ela:
@Resource
public class ProdutosController {
@Get @Path("/produtos/novo")
public void formulario() {...}
@Get @Path("/produtos/{id}")
public Produto edita(Long id) {...}
@Put @Path("/produtos/{produto.id}")
public void altera(Produto produto) {...}
@Post @Path("/produtos")
public void adiciona(final Produto produto) {...}
@Delete @Path("/produtos/{id}")
public void remove(Long id) {...}
@Get @Path("/produtos")
public List<Produto> lista() {...}
}
Note no mtodo altera que o parmetro extrado o produto.id. O VRaptor vai se comportar da mesma
forma que se esse produto.id tivesse vindo da requisio: vai popular o campo id do produto com o valor
extrado.
Captulo 10 - REST - Refatorando o ProdutosController - Pgina 83
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora que mudamos as URIs dos mtodos, precisamos atualizar as nossas jsps para usar as URIs novas.
Podemos usar a tag c:url para poder usar as URIs absolutas, a partir do nosso nome de contexto.
Primeiro na jsp do formulrio, precisamos mudar a action do form para /produtos, e o method para POST.
<form action="<c:url value="/produtos"/>" method="POST">
Ainda existe o link de Remoo, mas o mtodo remove do nosso controller s aceita o verbo DELETE! Como
fazer uma requisio com o verbo DELETE de dentro da sua pgina? Infelizmente os browsers atuais s conseguem fazer requisies GET (atrves de links e formulrios) e POST(atravs de formulrios). Para conseguir usar
os outros verbos, podemos fazer duas coisas:
Por ltimo precisamos mudar o formulrio de edio, colocando a action correta, e mudando o mtodo
para PUT. Mas como colocar method="PUT no nosso formulrio no funciona, precisamos passar o parmetro
_method:
<form action="<c:url value="/produtos/${produto.id }"/>" method="POST">
<fieldset>
<legend>Editar Produto</legend>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<!-vvvvvvv
vvv -->
<button type="submit" name="_method" value="PUT">Enviar</button>
</fieldset>
</form>
No precisamos mais passar o input hidden com produto.id, porque essa informao j est na URI!
button e o Internet Explorer
Em algumas verses do Internet Explorer, no possvel usar o name e value do button para
mandar parmetros na requisio. Nesse caso voc precisa trocar o button por:
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="Enviar"/>
10.7 - Exerccios
1) Anote os mtodos do ProdutosController para seguirmos a nossa especificao de Produto:
Recurso Produto:
GET /produtos => recupera a lista de todos os produtos. Mtodo lista.
POST /produtos => adiciona um novo produto. Mtodo adiciona.
GET /produtos/4 => mostra o produto de id 4. Mtodo edita.
PUT /produtos/10 => atualiza o produto de id 10. Mtodo atualiza.
DELETE /produtos/3 => remove o produto de id 3. Mtodo remove.
GET /produtos/novo => mostra o formulrio para adicionar um novo produto. Mtodo formulario.
3) Abra o edita.jsp e mude a action e o mtodo do form. Lembre-se que voc no precisa mais do input
hidden do produto.id.
<form action="<c:url value="/produtos/${produto.id }"/>" method="POST">
<!-- ... -->
<button type="submit" name="_method" value="PUT">Enviar</button>
</form>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<td>
<form action="<c:url value="/produtos/${produto.id}"/>" method="POST">
<button class="link" name="_method" value="DELETE">Remover</button>
</form>
</td>
C APTULO
11
Seletores
O JQuery suporta XPath e CSS 3, com seletores avanadssimos (alm dos clssicos id e class).
Veja mais aqui: http://docs.jquery.com/Selectors
Depois de selecionar o(s) elemento(s) desejado(s), podemos chamar mtodos para as mais variadas coisas.
O Hello World do JQuery mostra como exibir e esconder um div especifico:
87
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
$(#meuDiv).show()
$(#meuDiv).hide()
Um outro ponto forte do JQuery que ele possui diversos plugins que fazem vrias coisas interessantes
como validao de formulrios, autocomplete, efeitos visuais, drag n drop, etc. Uma lista de plugins disponveis
pode ser encontrada em http://plugins.jquery.com/. Uma documentao mais completa sobre o JQuery se
encontra em http://docs.jquery.com/ e uma documentao visual em http://www.visualjquery.com/.
Mais sobre javascript
Nesse curso no daremos foco no desenvolvimento de cdigo javascript, boas prticas e caractersticas dessa linguagem, apenas usaremos plugins do JQuery. Mas o cdigo javascript uma parte
bastante importante do desenvolvimento Web, e precisamos ter com ele os mesmos cuidados que
temos com o cdigo java.
Um contedo aprofundado sobre javascript, alm de CSS e HTML, pode ser encontrado no curso
WD-43 | Desenvolvimento Web com HTML, CSS e JavaScript.
um
plugin
Voc tambm precisa falar qual vai ser o form que vai ser validado. Para isso voc pode adicionar um id ao
seu formulrio, e usar um seletor do JQuery para torn-lo validvel":
<form id="produtosForm" action="<c:url value="/produtos"/>" method="POST">
...
<script type="text/javascript">
$(#produtosForm).validate();
</script>
Assim voc pode adicionar as restries aos seus campos, lembrando que o nome do produto obrigatrio
e tem que ter o tamanho maior que 3, a descrio tambm obrigatria e o tamanho tem que ser menor que
40, e o preo maior que zero:
<form action="<c:url value="/produtos"/>" method="POST">
<fieldset>
<legend>Adicionar produtos</legend>
<label for="nome">Nome:</label>
<input id="nome" class="required" minlength="3"
Captulo 11 - AJAX e efeitos visuais - Validando formulrios com o JQuery - Pgina 88
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Se voc no gosta de colocar mais atributos nos seus campos de formulrio, voc ainda pode definir todas
as regras de validao de uma vez s, usando os names dos inputs, dentro da parte rules:
<script type="text/javascript">
$(#produtosForm).validate({
rules: {
"produto.nome": {
required: true,
minlength: 3
},
"produto.descricao": {
required: true,
maxlength: 40
},
"produto.preco": {
min: 0.0
}
}
});
</script>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Repare que o nosso dao ainda no possui o mtodo busca, mas podemos usar o eclipse para criar esse
mtodo pra ns. Para isso, coloque o cursor em cima do erro de compilao e aperte Ctrl+1. No menu que
apareceu selecione Create method busca(String) in type ProdutoDao.
Agora podemos modificar o mtodo criado no ProdutoDao para realmente buscar os produtos que contm
a string passada no nome. Para isso usaremos a API de Criteria do Hibernate:
@Component
public class ProdutoDao {
//...
public List<Produto> busca(String nome) {
return session.createCriteria(Produto.class)
.add(Restrictions.ilike("nome", nome, MatchMode.ANYWHERE))
.list();
}
}
Traduzindo essa chamada: crie uma Criteria de produtos, com a restrio de que o nome contenha a string
passada em qualquer lugar, ignorando maisculas e minsculas.
Agora precisamos criar o jsp que mostra os resultados da busca, em /WEB-INF/jsp/produto/busca.jsp.
Podemos aproveitar o jsp de listagem para mostrar a tabela:
<h3>Resultados da busca pelo nome <b>"${nome }"</b></h3>
<%@ include file="lista.jsp" %>
Precisamos tambm passar o nome que foi buscado, ento podemos mudar o mtodo busca do
ProdutosController:
public List<Produto> busca(String nome) {
result.include("nome", nome);
return dao.busca(nome);
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Por ltimo, vamos criar um formulrio para poder acessar essa busca. Abra o arquivo header.jspf e modifique o div menu:
<div id="menu">
<ul>
<li><a href="<c:url value="/produtos/novo"/>">Novo Produto</a></li>
<li><a href="<c:url value="/produtos"/>">Lista Produtos</a></li>
<li><form action="<c:url value="/produto/busca"/>">
<input name="nome"/>
</form>
</li>
</ul>
</div>
Para que no fique um input perdido no menu, sem que as pessoas saibam o que ele faz, podemos usar
um plugin do JQuery bem simples chamado Puts (http://github.com/cairesvs/Puts). Esse plugin coloca um
texto em cima do input, e quando a pessoa clica nele, o texto some. Assim podemos colocar o texto Busca de
produtos por nome no nosso input. Para usar esse plugin, precisamos baixar o javascript e import-lo na nossa
pgina, e ento usar o mtodo puts() para colocar nosso texto.
<script type="text/javascript" src="<c:url value="/javascripts/jquery.puts.js"/>"></script>
...
<li><form action="<c:url value="/produto/busca"/>">
<input id="busca" name="nome"/>
</form>
<script type="text/javascript">
$("#busca").puts("Busca produtos por nome");
</script>
</li>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Podemos ainda melhorar nossa busca, mostrando dicas de produtos j existentes enquanto o usurio est
digitando. Queremos um resultado parecido com este:
Ou seja, voc passa para o mtodo from() o que voc quer serializar em JSON. Pode ser um objeto
qualquer, mas no nosso caso vai ser uma lista. Ao chamarmos a URI desse mtodo no browser, passando um
nome qualquer, por exemplo http://localhost:8080/goodbuy/produtos/busca.json?nome=a retornado um JSON
parecido com:
{"list": [
{
"id": 1,
"nome": "Sabonete",
"descricao": "Um sabonete com perfume de lavanda",
"preco": "3.53"
},
{
"id": 3,
"nome": "Sapato",
"descricao": "um sapato de couro",
"preco": "132.00"
}
]}
Mas como vamos buscar os produtos apenas por nome, no precisamos de todas essas informaes, s
precisamos de uma lista de nomes, talvez com o preo. Para isso podemos excluir as outras propriedades:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora estamos prontos para usar o plugin de autocomplete. Para isso, abra o arquivo header.jspf, e
adicione o javascript e o css do plugin. Use o mesmo lugar do plugin Puts:
<link href="<c:url value="/javascripts/jquery.autocomplete.css"/>"
rel="stylesheet" type="text/css" media="screen" />
<script type="text/javascript"
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
src="<c:url value="/javascripts/jquery.autocomplete.min.js"/>"></script>
...
<script type="text/javascript">
$("#busca").puts("Busca produtos por nome");
$("#busca").autocomplete(/goodbuy/produtos/busca.json);
</script>
Isso deveria ser o suficiente, mas o plugin AutoComplete espera que voc mande os dados separados por
pipe(|) ou um dado por linha. Como queremos usar JSON, precisamos configurar isso no plugin:
$("#busca").autocomplete(/goodbuy/produtos/busca.json, {
dataType: "json", // pra falar que vamos tratar um json
parse: function(produtos) { // para tratar o json
// a funo map vai iterar por toda a lista,
// e transformar os dados usando a funo passada
return $.map(produtos, function(produto) {
return {
data: produto, // todos os dados do produto
value: produto.nome, // o valor lgico do produto
result: produto.nome // o que vai aparecer ao selecionar
};
});
},
formatItem: function(produto) { // o que vai aparecer na lista de autocomplete
return produto.nome + "(" + produto.preco + ")";
}
});
Alm disso, o plugin passa como parmetro da requisio o que voc digitou no input, numa varivel chamada q ento voc precisa modicar a lgica de busca para o parmetro se chamar q:
@Get @Path("/produtos/busca.json")
public void buscaJson(String q) {
result.use(json()).withoutRoot()
.from(dao.busca(q))
.exclude("id", "descricao")
.serialize();
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
11.6 - Exerccios
1) Modifique os formulrios de adio e edio de produtos para incluir a validao do lado do cliente. Os javascripts do plugin j esto importados no projeto base. Se preferir use a outra forma de validao, passando
as opes para o mtodo validate().
<form id="produtosForm" action="<c:url value="/produtos"/>" method="POST">
<fieldset>
<legend>Adicionar produtos</legend>
<label for="nome">Nome:</label>
<input id="nome" class="required" minlength="3"
type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" class="required" maxlength="40"
name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" min="0"
type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
<script type="text/javascript">
$(#produtosForm).validate();
</script>
3) (Opcional) As mensagens de erro esto em ingls. Tente procurar na documentao do plugin como fazer
para que as mensagens fiquem em portugus.
4) Crie um mtodo em ProdutosController para a listagem de produtos.
@Resource
Captulo 11 - AJAX e efeitos visuais - Exerccios - Pgina 95
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
10) Modifique o formulrio de busca para usar o plugin Puts e deixar uma mensagem dentro do input.
<div id="menu">
<ul>
<li><a href="<c:url value="/produtos/novo"/>">Novo Produto</a></li>
<li><a href="<c:url value="/produtos"/>">Lista Produtos</a></li>
<li><form action="<c:url value="/produto/busca"/>">
<input id="busca" name="nome"/>
</form>
<script type="text/javascript">
$("#busca").puts("Busca produtos por nome");
</script>
</li>
</ul>
</div>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
.from(dao.busca(q))
.exclude("id", "descricao")
.serialize();
}
C APTULO
12
Alm disso precisamos criar uma classe que representa um item do carrinho: um produto com a quantidade
selecionada.
public class Item {
private Produto produto;
private Integer quantidade;
// getters e setters
}
Pergunta importante: precisamos guardar esse carrinho no banco? O usurio no comprou ainda os produtos, ento no faz muito sentido guardar no banco de dados. Esse carrinho precisa ficar disponvel enquanto
o usurio estiver navegando no sistema, ento podemos apenas colocar o carrinho na sesso. Como vimos
antes, para que um componente seja nico durante a sesso do usurio basta anot-lo com @SessionScoped,
ento basta modificar a classe do carrinho para incluir as anotaes:
@Component
@SessionScoped
public class Carrinho {
private List<Item> itens = new ArrayList<Item>();
99
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora podemos receber esse carrinho no construtor de algum controller, com a certeza de que ele ser
nico durante a sesso do usurio.
O novo formulrio vai adicionar um item ao carrinho, passando a quantidade e o id do produto. Precisamos
agora criar um controlador que trate das operaes no carrinho de compras, que seja capaz de adicionar um
item ao carrinho. Esse controlador vai se chamar CarrinhoController:
Captulo 12 - Criando o Carrinho de Compras - Controlando o carrinho de compras - Pgina 100
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy.controller;
import br.com.caelum.vraptor.Resource;
@Resource
public class CarrinhoController {
}
Para adicionar o item ao carrinho, temos que receber um Carrinho no construtor, assim o VRaptor vai passar
para o CarrinhoController o carrinho da sesso do usurio:
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
public CarrinhoController(Carrinho carrinho) {
this.carrinho = carrinho;
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
carrinho.adiciona(item);
}
}
No existe ainda o mtodo adiciona no Carrinho, ento use o Ctrl+1 para criar o mtodo. Ao adicionar um
item no carrinho precisamos atualizar o total:
public class Carrinho {
private List<Item> itens = new ArrayList<Item>();
private Double total = 0.0;
public void adiciona(Item item) {
itens.add(item);
total += item.getProduto().getPreco() * item.getQuantidade();
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
//...
}
Repare que estamos usando o preo do produto para atualizar o total do carrinho, mas no formulrio s
estamos passando o id do produto. Precisamos carregar as outras informaes do Produto no banco de dados,
usando o ProdutoDao:
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
private final ProdutoDao dao;
public CarrinhoController(Carrinho carrinho, ProdutoDao dao) {
this.carrinho = carrinho;
this.dao = dao;
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
}
}
Para implementar o mtodo recarrega no ProdutoDao, vamos usar um mtodo da Session que busca as
informaes no banco e coloca no prprio objeto passado. Esse mtodo se chama refresh.
@Component
public class ProdutoDao {
//...
public void recarrega(Produto produto) {
session.refresh(produto);
}
}
Por ltimo, precisamos ir para alguma pgina aps adicionar algum item no carrinho. Por ora, vamos voltar
pra pgina de listagem de produtos. Repare que como o redirecionamento para uma lgica de outro controller,
precisamos usar a classe deste controller:
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
private final ProdutoDao dao;
private final Result result;
public CarrinhoController(Carrinho carrinho, ProdutoDao dao, Result result) {
this.carrinho = carrinho;
this.dao = dao;
this.result = result;
Captulo 12 - Criando o Carrinho de Compras - Controlando o carrinho de compras - Pgina 102
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(ProdutosController.class).lista();
}
}
Estamos usando a expresso ${carrinho.totalDeItens} mas classe Carrinho no possui um getter para
esse total de itens. Ento vamos adicionar:
public class Carrinho {
//...
public Integer getTotalDeItens() {
return itens.size();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora ao adicionar produtos ao carrinho, conseguimos ver a quantidade de produtos adicionados e o valor
total do carrinho nessa div que acabamos de criar:
Ainda no conseguimos visualizar os itens do carrinho, ento vamos criar uma lgica que liste os itens, no
CarrinhoController.
@Get @Path("/carrinho")
public void visualiza() {
}
Esse mtodo vai atender mesma URI que o mtodo adiciona, mas com outro verbo HTTP: o GET. Agora
podemos criar a pgina que lista os itens. Crie essa pgina em /WEB-INF/jsp/carrinho/visualiza.jsp.
<h3>Itens do seu carrinho de compras</h3>
<table>
<thead>
<tr>
<th>Nome</th>
<th>Descrio</th>
<th>Preo</th>
<th>Qtde</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<c:forEach items="${carrinho.itens}" var="item">
<tr>
<td>${item.produto.nome }</td>
<td>${item.produto.descricao }</td>
<td><fmt:formatNumber type="currency"
value="${item.produto.preco }"/></td>
<td>${item.quantidade }</td>
<td><fmt:formatNumber type="currency"
value="${item.quantidade * item.produto.preco }"/></td>
</tr>
</c:forEach>
</tbody>
<tfoot>
<tr>
Captulo 12 - Criando o Carrinho de Compras - Visualizando os itens do carrinho - Pgina 104
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<td colspan="2"></td>
<th colspan="2">Total:</th>
<th><fmt:formatNumber type="currency" value="${carrinho.total }"/></th>
</tr>
</tfoot>
</table>
Agora que temos uma pgina de visualizao, podemos redirecionar para ela quando adicionamos algum
produto ao carrinho.
@Resource
public class CarrinhoController {
//...
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(this).visualiza();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</c:forEach>
</tbody>
...
</table>
12.5 - Exerccios
1) Crie os modelos Carrinho e Item:
package br.com.caelum.goodbuy.modelo;
@Component
@SessionScoped
public class Carrinho {
private List<Item> itens = new ArrayList<Item>();
private Double total = 0.0;
//getters e setters
}
package br.com.caelum.goodbuy.modelo;
public class Item {
private Produto produto;
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</div>
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
private final ProdutoDao dao;
private final Result result;
public CarrinhoController(Carrinho carrinho, ProdutoDao dao, Result result) {
this.carrinho = carrinho;
this.dao = dao;
this.result = result;
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(ProdutosController.class).lista();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
6) Crie a lgica e o jsp que visualiza os itens do carrinho. Mude tambm o redirecionamento do mtodo
adiciona para a nova lgica.
@Resource
public class CarrinhoController {
//...
@Get @Path("/carrinho")
public void visualiza() {
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(this).visualiza();
}
}
/WEB-INF/jsp/carrinho/visualiza.jsp
<h3>Itens do seu carrinho de compras</h3>
<table>
<thead>
<tr>
<th>Nome</th>
<th>Descrio</th>
<th>Preo</th>
<th>Qtde</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<c:forEach items="${carrinho.itens}" var="item" varStatus="s">
<tr>
<td>${item.produto.nome }</td>
<td>${item.produto.descricao }</td>
<td><fmt:formatNumber type="currency" value="${item.produto.preco }"/></td>
<td>${item.quantidade }</td>
<td><fmt:formatNumber type="currency"
value="${item.quantidade * item.produto.preco }"/></td>
</tr>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</c:forEach>
</tbody>
<tfoot>
<tr>
<td colspan="2"></td>
<th colspan="2">Total:</th>
<th><fmt:formatNumber type="currency" value="${carrinho.total }"/></th>
</tr>
</tfoot>
</table>
8) Crie a lgica de remover produtos do carrinho e adicione o boto de remoo na visualizao do carrinho.
public class CarrinhoController {
//...
@Delete @Path("/carrinho/{indiceItem}")
public void remove(int indiceItem) {
carrinho.remove(indiceItem);
result.redirectTo(this).visualiza();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</c:forEach>
</tbody>
...
</table>
C APTULO
13
Autenticao
13.1 - Criando Usurios
No nosso sistema, atualmente, qualquer um pode adicionar, editar ou remover produtos. Ser que isso o
desejvel? No seria melhor apenas habilitar essas funcionalidades para os administradores do sistema?
Ento vamos criar um sistema de login para a nossa aplicao, comeando pelo modelo de usurios:
package br.com.caelum.goodbuy.modelo;
public class Usuario {
private String login;
private String senha;
private String nome;
//getters e setters
}
uma boa idia guardar os usurios no banco de dados, ento vamos adicionar as anotaes do hibernate.
No h a necessidade de criar um campo id para usurios, pois o login j ser um identificador nico.
package br.com.caelum.goodbuy.modelo;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Usuario {
@Id
private String login;
private String senha;
private String nome;
//getters e setters
}
Como estamos adicionando uma entidade nova, precisamos coloc-la no hibernate.cfg.xml. Ainda vamos
adicionar a propriedade hibernate.hbm2ddl.auto com o valor update, assim o hibernate far as mudanas nas
112
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Com o modelo pronto j possvel criar as lgicas de cadastro e de login de usurios. Vamos comear pelo
cadastro, criando o controlador de usurios com uma lgica para mostrar o formulrio:
package br.com.caelum.goodbuy.controller;
import br.com.caelum.vraptor.Resource;
@Resource
public class UsuariosController {
public void novo() {
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<script type="text/javascript">
$(#usuariosForm).validate();
</script>
Precisamos tambm criar um link para esse cadastro. Para isso criaremos uma div no cabealho que
mostrar as informaes do usurio. Abra o arquivo header.jspf e modifique a div header:
<div id="header">
<div id="usuario">
Voc no est logado. <a href="<c:url value="/usuarios/novo"/>">Cadastre-se</a>
</div>
...
</div>
Para completar o cadastro, vamos criar a lgica que adiciona o usurio de fato, validando se o login escolhido
ainda no existe no sistema:
@Resource
public class UsuariosController {
private final UsuarioDao dao;
private final Result result;
private final Validator validator;
public UsuariosController(UsuarioDao dao, Result result, Validator validator) {
this.dao = dao;
this.result = result;
this.validator = validator;
}
@Post @Path("/usuarios")
public void adiciona(Usuario usuario) {
if (dao.existeUsuario(usuario)) {
validator.add(new ValidationMessage("Login j existe", "usuario.login"));
}
validator.onErrorUsePageOf(UsuariosController.class).novo();
dao.adiciona(usuario);
result.redirectTo(ProdutosController.class).lista();
}
//...
}
O UsuarioDao ainda no existe. Use o Ctrl+1 para criar o dao e os seus mtodos:
@Component
public class UsuarioDao {
private final Session session;
public UsuarioDao(Session session) {
this.session = session;
}
public boolean existeUsuario(Usuario usuario) {
Captulo 13 - Autenticao - Criando Usurios - Pgina 114
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Quando o usurio faz o login, precisamos guardar a informao de que ele j est logado. A melhor forma
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
guardar a informao de login na sesso, que mantm os dados enquanto o usurio estiver navegando pela
aplicao. Para isso, vamos criar uma classe que guarda o usurio logado. Essa classe ser acessada nos
jsps para acessar as informaes do usurio, ento adicionamos alguns getters para expor as informaes
relevantes.
@Component
@SessionScoped
public class UsuarioWeb {
private Usuario logado;
public void login(Usuario usuario) {
this.logado = usuario;
}
public String getNome() {
return logado.getNome();
}
public boolean isLogado() {
return logado != null;
}
}
Ento na nossa lgica de login, podemos colocar o usurio logado dentro da classe acima, depois de
verificar que o usurio digitou o login e senha certos.
@Resource
public class UsuariosController {
private final UsuarioWeb usuarioWeb;
//...
public UsuariosController(UsuarioDao dao, Result result, Validator validator,
UsuarioWeb usuarioWeb) {
//...
this.usuarioWeb = usuarioWeb;
}
@Post @Path("/login")
public void login(Usuario usuario) {
Usuario carregado = dao.carrega(usuario);
if (carregado == null) {
validator.add(new ValidationMessage("Login e/ou senha invlidos", "usuario.login"));
}
validator.onErrorUsePageOf(UsuariosController.class).loginForm();
usuarioWeb.login(carregado);
result.redirectTo(ProdutosController.class).lista();
}
}
Para carregar o usurio, crie o mtodo no UsuarioDao que busca um usurio por login e senha:
Captulo 13 - Autenticao - Efetuando o login - Pgina 116
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Component
public class UsuarioDao {
//...
public Usuario carrega(Usuario usuario) {
return (Usuario) session.createCriteria(Usuario.class)
.add(Restrictions.eq("login", usuario.getLogin()))
.add(Restrictions.eq("senha", usuario.getSenha()))
.uniqueResult();
}
}
E para mostrar que o usurio est logado mesmo, vamos modificar o cabealho:
<div id="header">
<div id="usuario">
<c:if test="${usuarioWeb.logado}">
Ol, ${usuarioWeb.nome }! <a href="<c:url value="/logout"/>">Logout</a>
</c:if>
<c:if test="${empty usuarioWeb or not usuarioWeb.logado}">
Voc no est logado. <a href="<c:url value="/login"/>">Login</a>
<a href="<c:url value="/usuarios/novo"/>">Cadastre-se</a>
</c:if>
</div>
...
</div>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Como falamos no comeo do captulo, no legal que todo mundo consiga adicionar, remover e editar
produtos. Vamos, ento, fazer com que s usurios logados consigam acessar essas funcionalidades. Um
primeiro passo retirar os links para elas quando o usurio no est logado:
/header.jspf:
<div id="menu">
<ul>
<c:if test="${usuarioWeb.logado }">
<li><a href="<c:url value="/produtos/novo"/>">Novo Produto</a></li>
</c:if>
...
/WEB-INF/jsp/produto/lista.jsp
<table>
...
<tbody>
<c:forEach items="${produtoList}" var="produto">
<tr>
...
<c:if test="${usuarioWeb.logado }">
<td><a href="<c:url value="/produtos/${produto.id}"/>">Editar</a></td>
<td>
<form action="<c:url value="/produtos/${produto.id}"/>" method="POST">
<button class="link" name="_method" value="DELETE">Remover</button>
</form>
</td>
</c:if>
</tr>
</c:forEach>
</tbody>
</table>
Agora os usurios que no esto logados no conseguem mais ver os links das aes que ele no pode
executar. Mas ser que isso o suficiente? O que impede o usurio de digitar na barra de endereos do browser:
http://localhost:8080/goodbuy/produtos/novo? Nada! Ele s precisa conhecer qual a URI. Precisamos, de
algum jeito, impedir que os usurios acessem certas URIs se eles no estiverem logados, monitorar todas as
aes do usurio para que quando ele acessar uma URI proibida, redirecionar para uma pgina de erro, ou
melhor, para o login.
13.4 - Interceptor
Um Interceptor no VRaptor como se fosse um Servlet Filter: ele pode interceptar requisies, executando
algo antes e/ou depois da sua lgica. Mais ainda, ele pode impedir que sua lgica seja executada, redirecionando a requisio para outro lugar. Isso exatamente o que a gente queria: verificar se o usurio est logado
antes de ir pro controller, e se ele no estiver, redirecionar para o login.
Para implementar um Interceptor do VRaptor precisamos de duas coisas: anotar a classe com @Intercepts
e implementar a interface Interceptor:
@Intercepts
Captulo 13 - Autenticao - Interceptor - Pgina 118
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Como qualquer classe registrada no VRaptor, voc pode receber qualquer componente da sua aplicao (e
do VRaptor) pelo construtor do seu interceptor. No nosso caso precisamos verificar se o usurio est logado, e
essa informao est no UsuarioWeb.
@Intercepts
public class AutorizacaoInterceptor implements Interceptor {
private final UsuarioWeb usuario;
public AutorizacaoInterceptor(UsuarioWeb usuario) {
this.usuario = usuario;
}
}
Mas s precisamos executar a lgica de autenticao caso o usurio no esteja logado, ento vamos implementar o mtodo accepts:
public boolean accepts(ResourceMethod method) {
return !this.usuario.isLogado();
}
Para o mtodo intercepts, sabemos j que o usurio no est logado, ento vamos redirecionar para a lgica
de login. Para isso precisamos do Result.
@Intercepts
public class AutorizacaoInterceptor implements Interceptor {
private final UsuarioWeb usuario;
private final Result result;
public AutorizacaoInterceptor(UsuarioWeb usuario, Result result) {
this.usuario = usuario;
Captulo 13 - Autenticao - Interceptor - Pgina 119
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
this.result = result;
}
public boolean accepts(ResourceMethod method) {
return false;
}
public void intercept(InterceptorStack stack, ResourceMethod method,
Object resourceInstance) throws InterceptionException {
result.redirectTo(UsuariosController.class).loginForm();
}
}
Mas temos um pequeno problema: se o usurio no estiver logado ele no vai conseguir acessar nada no
sistema, nem o login! Na verdade, s queremos proibir que o usurio adicione e modifique produtos, ento
nosso interceptor s pode executar para essas operaes.
Poderamos at colocar no Interceptor uma lista dos mtodos que sero interceptados, mas assim toda vez
que adicionarmos uma operao nova que precise de autenticao precisaramos mudar o interceptor.
Um jeito mais legal de fazer isso marcar os mtodos que precisam de autenticao. Para isso podemos
criar uma anotao para ser colocada nos mtodos:
@Retention(RetentionPolicy.RUNTIME) //a anotao vai ficar disponvel em tempo de execucao
@Target(ElementType.METHOD) // anotao para mtodos
public @interface Restrito {
}
Assim, podemos anotar os mtodos restritos (adiciona, atualiza, remove, formulario e edita do ProdutosController):
@Resource
public class ProdutosController {
@Restrito
public void formulario() {}
@Restrito
public Produto edita(Long id) {...}
@Restrito
public void altera(Produto produto) {...}
@Restrito
public void adiciona(final Produto produto) {...}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Restrito
public void remove(Long id) {...}
...
}
Pronto. Nosso sistema de autenticao est pronto. Poderamos trocar a anotao @Restrito por uma, por
exemplo, @Liberado caso tenha bem mais operaes liberadas do que restritas, depende do seu sistema.
C APTULO
14
14.1 - Exerccios
1) Modifique a pgina de edio de Produtos (/WEB-INF/jsp/produtos/edita.jsp), para incluir um formulrio
de Upload de imagem. Esse formulrio vai submeter para uma lgica nova que vamos criar a seguir.
<!--...-->
<form action="<c:url value="/produtos/${produto.id }/imagem"/>" method="POST"
enctype="multipart/form-data">
<fieldset>
<legend>Upload de Imagem</legend>
<input type="file" name="imagem" />
<button type="submit">Enviar</button>
</fieldset>
</form>
2) Crie o Controller abaixo, que vai tratar dos uploads e downloads de imagens.
package br.com.caelum.goodbuy.controller;
import br.com.caelum.vraptor.interceptor.multipart.UploadedFile;
@Resource
public class ImagensController {
@Post @Path("/produtos/{produto.id}/imagem")
public void upload(Produto produto, UploadedFile imagem) {
}
}
3) S estamos interessados em fazer uploads de imagens. Ento vamos validar se o upload foi uma imagem
mesmo. O ContentType de imagens comea com image:
@Post @Path("/produtos/{produto.id}/imagem")
public void upload(Produto produto, final UploadedFile imagem) {
122
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
validator.checking(new Validations() {{
if (that(imagem, is(notNullValue()), "imagem", "imagem.nula")) {
that(imagem.getContentType(), startsWith("image"), "imagem", "nao.eh.imagem");
}
}});
validator.onErrorRedirectTo(ProdutosController.class).edita(produto.getId());
}
5) Crie o componente do VRaptor que ser responsvel por guardar as imagens no servidor. Esse componente
vai criar uma pasta fixa onde todas as imagens sero guardadas. Para isso, precisamos conseguir o
caminho de uma pasta do servidor, e a classe que consegue nos dar essa informao o ServletContext.
package br.com.caelum.goodbuy.imagens;
import java.io.File;
import javax.servlet.ServletContext;
import br.com.caelum.vraptor.ioc.Component;
@Component
public class Imagens {
private File pastaImagens;
public Imagens(ServletContext context) {
String caminhoImagens = context.getRealPath("/WEB-INF/imagens");
pastaImagens = new File(caminhoImagens);
pastaImagens.mkdir();
}
}
6) Crie um mtodo que salve a imagem do upload na pasta padro. Colocaremos uma extenso fixa para
facilitar. O IOUtils vem do projeto commons-io e copia um InputStream para algum OutputStream
package br.com.caelum.goodbuy.imagens;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
@Component
public class Imagens {
//...
public void salva(UploadedFile imagem, Produto produto) {
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
7) Use a classe Imagens para salvar a imagem que veio no upload. E a classe Result para voltar para o
formulario de edio.
package br.com.caelum.goodbuy.controller;
@Resource
public class ImagensController {
private final Validator validator;
private final Imagens imagens;
private final Result result;
public ImagensController(Validator validator, Imagens imagens, Result result) {
this.validator = validator;
this.imagens = imagens;
this.result = result;
}
@Post @Path("/produtos/{produto.id}/imagem")
public void upload(Produto produto, final UploadedFile imagem) {
validator.checking(new Validations() {{
if (that(imagem, is(notNullValue()), "imagem", "imagem.nula")) {
that(imagem.getContentType(), startsWith("image"), "imagem", "nao.eh.imagem");
}
}});
validator.onErrorRedirectTo(ProdutosController.class).edita(produto.getId());
imagens.salva(imagem, produto);
result.redirectTo(ProdutosController.class).edita(produto.getId());
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
10) Mude o formulrio de edio para incluir a imagem do produto. Repare que estamos usando nosso controller
para mostrar a imagem. Coloque essa imagem na listagem de produtos tambm.
<img src="<c:url value="/produtos/${produto.id}/imagem"/>" width="100" height="100"/>
C APTULO
15
126
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Esse componente de transaes genrico: funciona tanto com a JTA direto, com Hibernate/JPA, com JDBC,
etc. Ento para especificar qual dos gerenciadores de transao vai ser utilizado, voc precisa adicionar um
bean na configurao. No nosso caso, vamos usar transaes do Hibernate:
<beans xmlns...>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
4) O transactionManager precisa de uma SessionFactory configurada. J temos uma no VRaptor, mas ela no
poder ser usada. Remova a anotao @Component do CriadorDeSessionFactory, e adicione o bean que cria
uma SessionFactory no Spring:
<beans xmlns....>
<!--...-->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation">
<value>classpath:/hibernate.cfg.xml</value>
</property>
</bean>
</beans>
5) Ao usar a SessionFactory do Spring estamos presos ao seu controle de Sessions. Isso significa que no
podemos fazer o sessionFactory.openSession para obter Sessions. O Spring controla isso para voc, ento
voc obrigado a usar componentes do Spring para conseguir uma Session, para poder usar a mesma que
est participando da transao. Mas ao invs de mudar todo o nosso sistema para usar as sessions do
Spring, vamos modificar o nosso CriadorDeSession para usar-las.
Por causa do jeito que o Spring trabalha, temos que pedir uma Session para ele no momento que vamos
us-la, e no na criao do DAO, por exemplo. Ento vamos usar um Proxy Dinmico para poder passar
Captulo 15 - Apndice - Integrando VRaptor e Spring - Exerccios: Transaction Manager - Pgina 127
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
uma Session no construtor do DAO, mas mesmo assim s pedir a Session pro Spring na hora de chamar
algum mtodo.
Modifique o CriadorDeSession.
import net.vidageek.mirror.dsl.Mirror;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import br.com.caelum.vraptor.proxy.MethodInvocation;
import br.com.caelum.vraptor.proxy.Proxifier;
import br.com.caelum.vraptor.proxy.SuperMethod;
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
private final SessionFactory factory;
private final Proxifier proxifier;
private Session session;
public CriadorDeSession(SessionFactory factory, Proxifier proxifier) {
this.factory = factory;
this.proxifier = proxifier;
}
@PostConstruct
public void abre() {
this.session = proxifier.proxify(Session.class, new MethodInvocation<Session>() {
public Object intercept(Session proxy, Method method, Object[] args,
SuperMethod superMethod) {
Session sessionDoSpring = SessionFactoryUtils.doGetSession(factory, true);
return new Mirror().on(sessionDoSpring).invoke().method(method).withArgs(args);
}
});
}
public Session getInstance() {
return this.session;
}
@PreDestroy
public void fecha() {
this.session.close();
}
}
6) Para usar o controle de transaes do Spring, remova a abertura e o fechamento de transaes dos
mtodos do DAO, e anote o mtodo com @Transactional. Repare que nem todos os mtodos precisam de
transaes, s os que modificam o banco.
import org.springframework.transaction.annotation.Transactional;
@Component
public class ProdutoDao {
@Transactional
Captulo 15 - Apndice - Integrando VRaptor e Spring - Exerccios: Transaction Manager - Pgina 128
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Captulo 15 - Apndice - Integrando VRaptor e Spring - Exerccios: Transaction Manager - Pgina 129
C APTULO
16
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
import javax.servlet.http.HttpServletRequest;
import br.com.caelum.vraptor.view.AcceptHeaderToFormat;
import br.com.caelum.vraptor.view.DefaultPathResolver;
@Component
public class VelocityPathResolver extends DefaultPathResolver {
Captulo 16 - Apndice: Mudando a View Padro: Velocity - Exerccios: Mudando o resultado de todas as lgicas para Velocity Pgina 131
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
2) Crie a pasta /WEB-INF/velocity, e dentro dela a pasta teste. Mova o arquivo olamundo.vm para a pasta
teste e o renomeie para teste.vm.
3) Remova o redirecionamento para a pgina olamundo.vm de dentro do TesteController:
public class TesteController {
private Result result;
public TesteController(Result result) {
this.result = result;
}
@Path("/teste")
public void teste() {
result.include("mensagem", "Estou usando o Velocity");
}
}
4) Acesse a url do mtodo teste (http://localhost:8080/goodbuy/teste), e veja que continua funcionando: ele
vai redirecionar automaticamente para a pgina /WEB-INF/velocity/teste/teste.vm.
5) Veja que agora nenhuma das outras pginas do sistema esto acessveis. Agora qualquer chamada vai dar
404, pois ele estar usando a nova conveno. O comportamento do VRaptor agora o da sua classe, e
no mais a padro dele!
Captulo 16 - Apndice: Mudando a View Padro: Velocity - Exerccios: Mudando o resultado de todas as lgicas para Velocity Pgina 132
ndice Remissivo
AnnotationConfiguration, 11
Criteria, 40
Driver, 5
Escopo, 67
Hibernate, 4
Hibernate Annotations, 5
Hibernate Core, 5
Injeo de Dependncias, 43
JPA, 4
Log4J, 5
Logging, 5
MySQL, 4
ORM, 4
POJO, 32
Result, 52
SL4J, 5
Validao, 72
133
Desmistificando o AJAX
5.
6.
7. CONCLUSO ..............................................................................................................................................................15
APNDICE.......................................................................................................................................................................16
1/16
1. O que AJAX
Antes de explicar o que AJAX, deve-se explicar o que ele no ; AJAX no um
framework, uma API nem uma tecnologia em si, uma funcionalidade implementada por um
conjunto de objetos de JavaScript, sendo o mais importante chamado XMLHttpRequest.
Este objeto, que trata uma requisio ou resposta de servidor com um documento XML
DOM, contm uma srie de mtodos que possibilita que o browser possa realizar requisies e
receber respostas do servidor sem que este tenha que atualizar(refresh) a tela.
2. A finalidade do AJAX
O principal problema resolvido com AJAX a substituio da conhecida tela escondida ou
hidden frame, que era implementado como nica soluo para a realizao de uma requisio sem
refresh da pgina principal.
Com hidden frame tnhamos vrios problemas:
Problemas com Hidden Frame
Quando ocorriam erros na pgina escondida, estes no eram rastreados com facilidade.
Dificuldade de manuteno.
Normalmente deveria ser implementado um novo jsp para cada mtodo, assim, caso
fosse necessrio a utilizao de vrias funcionalidades, eram necessrios mais pginas
escondidas na mesma pgina.
AJAX resolve este problema, possibilitando, atravs de um nico mtodo, realizar request e
receber responses com ilimitadas respostas, ou seja, a comunicao cliente-servidor fica
transparente, fazendo com que sem nenhuma dificuldade o desenvolvedor possa acessar mtodos do
servidor quase como se fosse um mtodo JavaScript.
2/16
3. A Aplicabilidade do AJAX
Alguns usos mais comuns do AJAX podem ser listados:
1. Validao em tempo real: Validaes que no possam ser feitas do lado do cliente, como,
por exemplo, verificar se usurio j est cadastrado ou se a data informada anterior data
atual.
2. Auto Completion: Possibilita que o ao mesmo tempo em que o usurio for digitando, possa
aparecer uma lista de possveis respostas.
-Um bom exemplo o Google Suggest (http://www.google.com/webhp?complete=1&hl=en)
3. Visualizao de detalhes de um item: Ao invs de carregar todos os dados na tela ou ento
necessitar de popups, pode-se montar a lista de itens-pai e dependendo da escolha, montar
os detalhes do item.
-Para melhor exemplificao, um bom exemplo a seo de notcias e de empregos do portal
JavaFree(www.javafree.org).
4. Controles de interface de usurio sofisticados: Controles dinmicos como arvore de
diretrios, menus, barras de progresso e interface ricas como aplicaes RIA ou at mesmo
jogos podem ser implementados sem necessidade de refresh.
-Um exemplo de interface rica pode ser visualizada no site Flickr(http://flickr.com/), onde o
usurio pode organizar uma coleo de fotos com diversos recursos, como por exemplo com
utilizao de drag n drop.
-Um exemplo do famoso jogo Lemmings desenvolvido utilizando AJAX pode ser visto em
(http://193.151.73.87/games/lemmings/)
5. Atualizao de dados na pgina: Atualizao de informaes na pgina em tempo real sem
a necessidade de refresh possibilita, por exemplo, o desenvolvimento de chats,
acompanhamento de aes de bolsa, notcias ou aplicaes semelhantes.
-Um exemplo de chat existe no site QWAD(http://www.qwadchat.com).
-Um exemplo de atualizao de notcias segundo a segundo est em (http://digg.com/spy)
3/16
4.
A arquitetura do AJAX
AJAX(Asynchronous Javascript And XML) modifica um pouco a arquitetura das
aplicaes atuais.
Enquanto as aplicaes clssicas tinham seqncia de troca de informaes totalmente
sncrona, AJAX, por default tem uma seqncia assncrona, porm esta pode ser configurada para
ser sncrona como ser explicado depois.
4/16
4.1
Sincronismo X Assincronismo
A diferena entre requisies sncronas e assncronas quando se espera receber a
resposta.
Num modelo sncrono das aplicaes clssicas, temos chamadas e respostas seqenciais,
ou seja, necessrio recebermos a resposta da requisio anterior antes que passemos para a
prxima requisio. Este tipo de modelo o usado na arquitetura clssica, onde, caso precise chamar
o servidor, preciso realizar um refresh da pgina, e na pgina atualizada (ou na prxima pgina)
receber a resposta.
A ilustrao a seguir descreve a diferena dos modelos:
5/16
6/16
5.
1. function chamaAjax(){
2. var req;
3. var isIE;
4. if (window.XMLHttpRequest) {
5.
req = new XMLHttpRequest();
6. }else if (window.ActiveXObject) {
7.
isIE = true;
8.
req = new ActiveXObject("Microsoft.XMLHTTP");
9. }
Com o cdigo acima, teremos nossa varivel req instanciada com um objeto
XMLHttpRequest para qualquer browser.
OBS: em algumas verses antigas do IE, deve ser instanciado o objeto
Msxml2.XMLHTTP ao invs de Microsoft.XMLHTTP. Um exemplo de cdigo mais
detalhado para todos os casos ser demonstrado no decorrer do tutorial.
10.
12.
13.
14.
15. }
7/16
1. <servlet>
2. <servlet-name>ServletAjax</servlet-name>
3. <servlet-class>ServletAjax</servlet-class>
4. </servlet>
5. <servlet-mapping>
6. <servlet-name>ServletAjax</servlet-name>
7. <url-pattern>/ajax</url-pattern>
8. </servlet-mapping>
Acima demonstra como mapear no web.xml da aplicao o servlet que ficar responsvel por
receber as requisies. Qualquer requisio feita com o comando ajax ser redirecionada para a
Servlet descrita a seguir.
Implementando a Servlet que receber as requisies das pginas
1. import javax.servlet.*;
2. import javax.servlet.http.*;
3. public class ServletAjax extends HttpServlet {
4. protected void service(HttpServletRequest request, HttpServletResponse response)
5.
throws ServletException, IOException {
6.
String nome = request.getParameter("nome ").trim();
7.
String tecnologia = request.getParameter("tecnologia ").trim();
8.
//LGICAS NECESSRIAS
9.
response.getWriter().write(nome + gosta de + tecnologia);
10. }
11.}
Acima exemplificado o Servlet que ler toda requisio feita pelo Ajax neste nosso
exemplo. Nela, possvel pegar todos os parmetros passados na requisio, realizar as lgicas
necessrias, como acesso a banco, validao de valores, etc.
Atravs do mtodo write da classe PrintWriter, pode-se retornar qualquer valor que seja
String,
int
ou
array
de
chars
(para
maiores
detalhes,
JavaDoc
8/16
9/16
6.
cliente e conseguimos execut-la com sucesso, ela j serve para algumas implementaes simples de
pedido de retorno de um dado simples para o usurio, porm, AJAX pode fazer bem mais! No
retornando apenas um dado, mas uma lista destes, podendo inclusive trabalhar com folhas-deestilo(CSS) para melhor visualizao do usurio.
J existem uma srie de frameworks que trabalham com AJAX, e estes podem ser
incorporados a qualquer aplicao web, com a utilizao de qualquer framework MVC, ou seja..
no ache que voc est preso ao framework ou ao AJAX, voc pode adicion-lo e retir-lo de onde
quiser e quando quiser.
Alguns exemplos de framework AJAX do mercado:
Framework e APIs de AJAX mais utilizadas na atualidade:
DWR - http://getahead.ltd.uk/dwr/
AjaxAnywhere - http://ajaxanywhere.sourceforge.net/
SaJax - http://www.modernmethod.com/sajax/
Rico - http://openrico.org/rico/home.page
BackBase - http://www.backbase.com
AjaxTags - http://ajaxtags.sourceforge.net/
Todos estes frameworks acima so bastante utilizados no mercado mundial, tm grande
suporte e uma srie de funcionalidades visuais, como dragn drop de componentes, preenchimento
de grids em tempo real, sugesto de escrita(como no google suggest), etc.
O exemplo que iremos dar neste tutorial no utilizar de nenhum desses frameworks, iremos
implementar o AJAX de forma pura, dessa vez retornando do servidor um XML com todos os
valores (ao invs de apenas um texto, como no exemplo anterior) e iremos preencher um uma lista
de valores.
10/16
O que iremos implementar para este exemplo preenchimento de um combobox com uma
pequena lista de cidades de acordo com o estado selecionado em outra combo, e aps a seleo da
cidade, nos ser mostrado a quantidade de fs do Java.
Abaixo, a lista de cidades de acordo com o estado:
Estado
Cidade
CE
-Caucaia
-Fortaleza
-Sobral
PB
-Joo Pessoa
PE
-Caruaru
-Recife
N de Javaneses
60
400
1
200
40
550
Iremos dessa vez, ao invs de retornamos um texto para o servidor, um XML com os dados
limpos de formatao HTML, no caso, um arquivo com a seguinte caracterstica:
Arquivo xml de retorno para o cliente
<?xml version='
1.0'?>
<root>
<estado>
<cidade nome='
Caucaia'
>
<javaneses>15</javaneses>
</cidade>
<cidade nome='
Fortaleza'
>
<javaneses>400</javaneses>
</cidade>
<cidade nome='
Sobral'
>
<javaneses>1</javaneses>
</cidade>
</estado>
</root>
Para a utilizao de xml como passagem de valores, deveremos ter algumas modificaes
tanto no Servlet, quanto no javascript que tratar do retorno. O web.xml continuar o mesmo.
Nosso Servlet, que estamos chamando de ServletAjax sofrer duas pequenas mudanas
(alm da lgica de negcio).
Sero adicionadas duas linhas a serem passadas como parmetro para o HTML, uma para
informarmos que nossa resposta ser um xml e estar codificado no padro UTF-8; a segunda
apenas uma informao para que o browser no utilize seu cache.
11/16
12/16
O cdigo do SerletAjax acima espera receber como parmetro (como visto na linha 6) a UF
desejada pelo usurio, atravs dela faz-se uma pequena lgica para retornar o XML resultante.
Perceba que a linha 37 MUITO importante, sem ela, frases com caracteres com acento, por
exemplo, no seriam reconhecidas; no nosso caso, Joo Pessoa iria para o browser como Jo?o
Pessoa.
Logo abaixo, veremos como ficar a tela do nosso jsp:
E adiante, o fonte completo da jsp. Note que o javascript, a chamada ao AJAX e o HTML
esto na mesma pgina apenas para fins didticos:
13/16
14/16
7. Concluso
No nosso caso, demonstramos um exemplo bem simples, porm capaz de apresentar a ponta
do iceberg do desenvolvimento Web com AJAX.
A popularizao do AJAX ajudou e muito no nascimento de novos termos, como a Web 2.0
(http://www.oreillynet.com/pub/a/oreilly/tim/news/2005/09/30/what-is-web-20.html),
uma
quase
renascena da Internet, desta vez, liderada pelo Google; e foi justamente o Google onde tivemos a
primeira aplicao com alta popularidade que utilizou o AJAX, o GMail.
O mais interessante que AJAX no novo! J era utilizado por inmeros desenvolvedores
mundo afora, porm, com a criao do acrnimo para referenci-lo a Javascript, XML e chamadas
assncronas, popularizou-se em poucas semanas; o artigo que batizou a velha tecnologia no
muito
difundida
foi
escrito
por
Jesse
James
Garret
no
site
(http://www.adaptivepath.com/publications/essays/archives/000385.php),
da
onde
Adaptive
me
serviu
Path
de
primeira base de estudos (e onde colei as duas figuras sobre o modelo assncrono e a arquitetura
do AJAX).
Aps a leitura do artigo, me interessei e fui atrs de novas fontes, principalmente com
exemplos, e onde eu fiz meu primeiro test-drive foi no catlogo de BluePrints do Java.net
(https://bpcatalog.dev.java.net/nonav/ajax/). A partir da, e at hoje, estou pesquisando mais sobre o
assunto
aos
poucos.
maior
melhor
fonte
que
posso
indicar
wikipedia
).
Apndice
CRIANDO UM OBJETO
FORMA
BROWSER
var req = new XMLHttpRequest();
Mozilla e outros
var req = new ActiveXObject("Microsoft.XMLHTTP");
Internet Explorer
var req = new ActiveXObject("Msxml2.XMLHTTP");
Internet Explorer
MTODOS DO OBJETO
MTODO
FUNO
abort()
Cancela a atual requisio
getAllResponseHeaders()
Retorna a coleo de cabealhos do http como uma String
getResponseHeader("campoCabecalho") Retorna o valor do campo do cabealho
open("metodo","URL",assincrono)
Especifica o mtodo, a URL e outros atributos opcionais de uma requisio.
O parmetro mtodo pode ser GET, POST ou PUT.
O parmetro URL pode ser uma url relativa ou completa.
O parmetro assncrono informa se a requisio ser assncrona ou sncrona:
true significa que o script continuar aps a execuo do mtodo send; false
significa que o script ficar esperando a resposta do servidor no mtodo send.
send(conteudo)
Envia a requisio
setRequestHeader("campo","valor")
Adiciona um par (campo/valor) ao cabealho http a ser enviado
PROPRIEDADES DO OBJETO
PROPRIEDADE
DESCRIO
onreadystatechange
Indica o evento a ser chamado cada vez que o estado do objeto muda.
readyState
Informa o estado do objeto da requisio:
0 = objeto no inicializado
1 = carregando
2 = carregado
3 = interagindo
4 = completado
responseText
Retorna a resposta como uma string
responseXML
Retorna a resposta como um XML (que pode ser tratada como um W3C
DOM)
status
Retorna o estado da requisio como um nmero (ex: 404 para no
encontrado e 200 para OK)
statusText
Retorna o estado da requisio como uma string (ex: No encontrado ou
OK)
16/16
FJ-28
A Caelum atua no mercado com consultoria, desenvolvimento e ensino em computao. Sua equipe
participou do desenvolvimento de projetos em vrios clientes e, aps apresentar os cursos de vero de Java
na Universidade de So Paulo, passou a oferecer treinamentos para o mercado. Toda a equipe tem uma
forte presena na comunidade atravs de eventos, artigos em diversas revistas, participao em muitos
projetos open source como o VRaptor e o Stella e atuao nos fruns e listas de discusso como o GUJ.
Com uma equipe de mais de 60 profissionais altamente qualificados e de destaque do mercado, oferece
treinamentos em Java, Ruby on Rails e Scrum em suas trs unidades - So Paulo, Rio de Janeiro e
Braslia. Mais de 8 mil alunos j buscaram qualificao nos treinamentos da Caelum tanto em nas unidades
como nas prprias empresas com os cursos incompany.
O compromisso da Caelum oferecer um treinamento de qualidade, com material constantemente
atualizado, uma metodologia de ensino cuidadosamente desenvolvida e instrutores capacitados
tecnicamente e didaticamente. E oferecer ainda servios de consultoria gil, mentoring e desenvolvimento
de projetos sob medida para empresas.
Comunidade
Nossa equipe escreve constantemente artigos no Blog da Caelum que j conta
com 150 artigos sobre vrios assuntos de Java, Rails e computao em geral.
Visite-nos e assine nosso RSS:
blog.caelum.com.br
O GUJ maior frum de Java em lngua portuguesa, com 700 mil posts e 70 mil
usurios. As pessoas da Caelum participam ativamente, participe tambm:
www.guj.com.br
FJ-25:
Java e Orientao a
objetos
FJ-16:
FJ-26:
FJ-19:
FJ-31:
Java EE avanado e
Web Services
FJ-21:
FJ-91:
Arquitetura e Design de
Projetos Java
RR-71:
RR-75:
ndice
1 O curso
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2 O sistema
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4 Cadastrando Produtos
17
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
31
40
ii
7.9 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
7.10 Atualizando e removendo produtos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
7.11 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
7.12 Discusso em sala - VRaptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8 Refatorando os DAOs
62
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
8.6 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
9 Validando formulrios
72
9.1 Validator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
9.2 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
9.3 Para saber mais: Hibernate Validator
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
79
87
99
112
122
126
130
iv
C APTULO
O curso
Dizem que os homens nunca se contentam e, quando se lhes d alguma coisa, pedem sempre um pouco
mais. Dizem ainda que essa uma das melhores qualidades da espcie e que foi ela que tornou o homem
superior aos animais, que se contentam com o que tm.
A prola, John Steibeck.
com.br/
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
O frum do GUJ tambm uma excelente fonte de informao, e abriga o frum oficial do VRaptor: http:
//www.guj.com.br/
C APTULO
O sistema
Veremos como ser o sistema que iremos desenvolver e as tecnologias que utilizaremos.
C APTULO
http://www.mysql.com/
Depois de instalado e configurado, o MySQL pode ser acessado com o usurio chamado root e a senha
vazia (sem senha). Para testar, basta abrir o terminal e digitar o comando:
mysql -u root
Esse comando tenta fazer o login no mysql, informando o usurio root (-u root) e omitindo a senha, j que
esse usurio no tem senha.
Para nos comunicarmos com o banco de dados, seja para uma consulta ou para algum tipo de alterao de
dado, podemos usar a API que o Java SE disponibiliza, o JDBC. Um dos problemas do JDBC, quando usado diretamente, conforme mostramos no curso FJ-21, que ele muito trabalhoso alm de que precisamos gerenciar
muitos recursos importantes. Todas as informaes tm de ser passadas uma a uma.
Para evitar ter que ficar fazendo todas as chamadas ao JDBC e ganharmos tempo e produtividade, vamos
utilizar um framework para a persistncia que j traz muitas funcionalidades j implementadas. O framework
que utilizaremos ser o Hibernate e ele ser o responsvel por fazer as chamadas API do JDBC.
Vale lembrar que, tudo que visto aqui no curso, aplicvel a outros frameworks de persistncia, como
seria o caso de usar o iBatis ou ento o EclipseLink atravs de JPA. muito importante perceber como vamos
integrar todas as nossas ferramentas de maneira coesa e desacoplada.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
http://www.caelum.com.br/curso/fj25
http://www.hibernate.org
Depois de fazer o download desses dois zips, basta descompact-los e utilizar os JARs que esto dentro
de cada projeto. No exerccio abaixo veremos quais JARs iremos precisar. A partir do Hibernate 3.5, esses jars
todos vem numa nica distribuio, a core.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
J o XML, por mais que seja um pouco mais difcil em relao ao properties, permite que toda a configurao
seja feita nele. Por isso faremos nossa configurao no XML. O arquivo XML que o Hibernate procurar ser o
hibernate.cfg.xml e ele deve estar no classpath.
Para nosso caso, vamos seguir a conveno e criar o arquivo hibernate.cfg.xml na pasta src, dentro do
nosso projeto. O contedo do arquivo ser esse:
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.url">jdbc:mysql://localhost/fj28</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="show_sql">true</property>
<property name="format_sql">true</property>
</session-factory>
</hibernate-configuration>
As configuraes que passamos nesse arquivo so parecidas quando queremos nos conectar a um banco
de dados. Para conectar em um banco de dados, precisamos informar qual o usurio, a senha, algumas
informaes do banco, como host, porta, etc.
Um detalhe importante da nossa configurao o banco de dados que foi passado. Na configurao
hibernate.connection.url foi passado o nome do database que utilizaremos. Para esse caso escolhemos
o database fj28.
Abaixo segue a descrio de todas as configuraes que usamos.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Existem muitas outras configuraes possveis. Para maiores detalhes, acesse a documentao do Hibernate.
C APTULO
Cadastrando Produtos
No captulo anterior, configuramos o Hibernate para acessar o banco de dados. Neste captulo, nosso
objetivo cadastrar produtos.
BigDecimal
Em aplicaes normais, no se deve usar doubles para representar nmeros reais (ex. dinheiro,
porcentagem) por causa de problemas de arredondamento. Podemos usar o BigDecimal que tem
uma preciso fixa, portanto os problemas de arredondamento so facilmente contornveis. No
usaremos BigDecimal pois ele um pouco mais complicado de usar:
double a, b, c, d;
d = a+b/c;
com BigDecimal:
BigDecimal a, b, c, d;
d = a.add(b.divide(c));
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Quando anotamos uma classe com @Entity, devemos indicar qual ser o campo de chave primria. Para o
nosso caso, vamos criar um campo id para ser a chave primria da tabela.
Para indicar que o campo id ser a chave primria, utilizaremos a anotao @Id. Um detalhe importante
que o nome da anotao no devido ao nome do atributo, ou seja, o atributo e a anotao tem o nome id, mas
a anotao sempre ser @Id, j o atributo de chave primria pode ser qualquer nome.
Vrias vezes no queremos passar o valor do id porque o valor desse campo ser gerado no banco de
dados. Podemos informar isso pro Hibernate atravs da anotao @GeneratedValue. Para o nosso caso, esse
campo ser do tipo auto_increment no banco de dados.
@Entity
public class Produto {
@Id @GeneratedValue
private Long id;
private String nome;
private String descricao;
private Double preco;
// getter e setters
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Pronto, agora nossa entidade est devidamente anotada. S temos que fazer mais uma coisa: avisar o
Hibernate dessa entidade, isso porque ele no encontra a entidade automaticamente. Para informar isso ao
Hibernate, utilizaremos o arquivo de configurao dele, o hibernate.cfg.xml.
O contedo do arquivo ficar assim:
<hibernate-configuration>
<session-factory>
<!-- todas as propriedades configuradas anteriormente -->
<!-- entidades -->
<mapping class="br.com.caelum.goodbuy.modelo.Produto" />
</session-factory>
</hibernate-configuration>
Repare que temos que utilizar o nome completo da classe, ou seja, o nome da classe mais o pacote.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Depois de criar esse objeto, vamos chamar o mtodo responsvel por ler o arquivo hibernate.cfg.xml, o
configure().
// L o hibernate.cfg.xml
configuration.configure();
Aps configurar, precisamos criar um objeto que cria as sesses, uma fbrica.
SessionFactory, que pode ser obtida com:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Com a SessionFactory em mos conseguimos criar uma uma Session. O cdigo completo fica:
1 public class TesteDeSessao {
2
public static void main(String[] args) {
3
AnnotationConfiguration configuration = new AnnotationConfiguration();
4
configuration.configure();
5
6
SessionFactory factory = configuration.buildSessionFactory();
7
Session session = factory.openSession();
8
}
9 }
A partir desse objeto session, basta passarmos uma entidade que ele se encarregar de transformar do
modelo orientado a objetos para o modelo relacional.
Produto produto = new Produto();
produto.setNome("Prateleira");
produto.setDescricao("Uma prateleira para colocar livros");
produto.setPreco(35.90);
Transaction tx = session.beginTransaction();
session.save(produto);
tx.commit();
Note que utilizamos uma transao para salvar o produto. Essa transao do pacote org.hibernate, e ela
necessria para que o insert seja efetivado.
4.5 - Exerccios
1) Crie a seguinte classe, que adiciona produtos no banco.
1 package br.com.caelum.goodbuy.testes;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import
import
import
import
org.hibernate.Session;
org.hibernate.SessionFactory;
org.hibernate.Transaction;
org.hibernate.cfg.AnnotationConfiguration;
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
18
19
20
21
22
23
24
25 }
2) Execute a classe que adiciona o produto. Repare no console o sql que foi gerado.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
9
10
11
12
13
14
15
16
17 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
19 }
2) Execute a classe que altera o produto. Repare no console o sql que foi gerado.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
16
17
18 }
tx.commit();
}
5) Execute a classe que remove o produto. Repare no console o sql que foi gerado.
C APTULO
Refatorando
5.1 - Analisando o cdigo atual
Se olharmos para o nosso projeto agora, temos a seguinte situao:
5.2 - Refactoring
Uma prtica bastante comum e difundida no meio da Orientao a Objetos a chamada Refatorao (Refactoring). Refatorar um programa melhorar seu cdigo sem alterar sua funcionalidade. A ideia da refatorao
no corrigir bugs, por exemplo, mas melhorar a estrutura de seu cdigo, deix-lo mais OO, mais legvel.
H diversos tipos de refatoraes. Renomear uma varivel para um nome mais claro um exemplo simples.
Quebrar um mtodo grande em vrios mtodos menores um outro exemplo um pouco mais complicado.
Vrias IDEs de Java possuem suporte refatoraes comuns. O Eclipse possui timas opes automticas
que utilizaremos nesse curso.
O livro mais famoso sobre refatoraes foi escrito por Martin Fowler e chama-se Refactoring - Improving the
Design of existing code. um catlogo com dezenas de tcnicas de refatorao e instrues de como e quando
execut-las.
17
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Olhando para o mtodo main, podemos divid-lo em trs partes. Uma parte a aquisio de uma Session,
outra a criao do produto, e a outra adio do produto no banco de dados. Com base nessa diviso, vamos
pedir para o Eclipse nos ajudar a refatorar, para fazer essa diviso de uma maneira segura e automtica.
Apenas para ficar claro essa diviso, veja o cdigo novamente, agora com os comentrios mostrando onde
comea cada parte.
1 package br.com.caelum.goodbuy.testes;
2
3 // imports
4
5 public class AdicaoDeProduto {
6
7
public static void main(String[] args) {
8
// Aquisio da sesso
9
AnnotationConfiguration configuration = new AnnotationConfiguration();
10
configuration.configure();
11
12
SessionFactory factory = configuration.buildSessionFactory();
13
Session session = factory.openSession();
14
15
// Criao do produto
16
Produto produto = new Produto();
17
produto.setNome("Prateleira");
18
produto.setDescricao("Uma prateleira para colocar livros");
19
produto.setPreco(35.90);
Captulo 5 - Refatorando - Aprendendo a refatorar - Pgina 18
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
20
21
22
23
24
25
26 }
Selecionando apenas a parte da aquisio da sesso, vamos utilizar as teclas de atalho do Eclipse para criar
um mtodo com essas instrues. As teclas de atalho so Alt + Shift + M, que representa a opo Extract
Method.
Apertando essas teclas, o Eclipse mostrar uma tela perguntado o nome do mtodo que ser criado. O
nome desse mtodo ser getSession().
Colocando o nome do mtodo e apertando OK, o mtodo ser criado e a parte do cdigo que usa essas
instrues j muda automaticamente, fazendo a chamada ao mtodo recm-criado.
5.4 - Exerccios
1) Altere a classe AdicaoDeProduto, que est no pacote br.com.caelum.goodbuy.testes, colocando os
comentrios para deixar claro a diviso das partes.
1 package br.com.caelum.goodbuy.testes;
2
3 // imports
4
5 public class AdicaoDeProduto {
6
7
public static void main(String[] args) {
8
// Aquisio da sesso
9
AnnotationConfiguration configuration = new AnnotationConfiguration();
10
configuration.configure();
11
12
SessionFactory factory = configuration.buildSessionFactory();
13
Session session = factory.openSession();
14
15
// Criao do produto
16
Produto produto = new Produto();
17
produto.setNome("Prateleira");
18
produto.setDescricao("Uma prateleira para colocar livros");
19
produto.setPreco(35.90);
20
21
// Adio do produto no banco de dados
22
Transaction tx = session.beginTransaction();
23
session.save(produto);
24
tx.commit();
25
}
26 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Com as linhas selecionadas, pressione as teclas Alt + Shift + M (Extract Method), para criar um mtodo
com essas linhas. Aps pressionar essas teclas, aparecer a seguinte tela:
4) A tela que est sendo mostrada est esperando por um nome de mtodo. Digite getSession como nome do
mtodo e pressione OK.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
5) Note como o Eclipse mudou seu cdigo, criando o mtodo getSession e fazendo a chamada a este mtodo.
A classe deve estar assim:
public class AdicaoDeProduto {
6) Faa o mesmo para as linhas de criao de produto e adio de produto no banco de dados. No final,
ficaremos com 3 mtodos, um chamado getSession, que j foi feito, outro chamado criaProduto e outro
Captulo 5 - Refatorando - Exerccios - Pgina 21
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
chamado gravaProduto. Lembre-se de utilizar as teclas de atalho que vimos nesse exerccio.
7) Para conferncia, ficaremos com a classe da seguinte forma:
1 package br.com.caelum.goodbuy.testes;
2
3 // imports
4
5 public class AdicaoDeProduto {
6
7
public static void main(String[] args) {
8
// Aquisio da sesso
9
Session session = getSession();
10
11
// Criao do produto
12
Produto produto = criaProduto();
13
14
// Adio do produto no banco de dados
15
gravaProduto(session, produto);
16
}
17
18
private static void gravaProduto(Session session, Produto produto) {
19
Transaction tx = session.beginTransaction();
20
session.save(produto);
21
tx.commit();
22
}
23
24
private static Produto criaProduto() {
25
Produto produto = new Produto();
26
produto.setNome("Prateleira");
27
produto.setDescricao("Uma prateleira para colocar livros");
28
produto.setPreco(35.90);
29
return produto;
30
}
31
32
private static Session getSession() {
33
AnnotationConfiguration configuration = new AnnotationConfiguration();
34
configuration.configure();
35
36
SessionFactory factory = configuration.buildSessionFactory();
37
Session session = factory.openSession();
38
return session;
39
}
40 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Mas agora que o mtodo main foi quebrado em mtodos menores, ser que precisamos mesmo de comentrios? Olhe novamente para o cdigo, e veja que os nomes dos nossos mtodos j dizem muito o que
queremos, ento nem precisamos mais de comentrios.
J que no precisamos mais dos comentrios, vamos retir-los do nosso cdigo:
public class AdicaoDeProduto {
public static void main(String[] args) {
Session session = getSession();
Produto produto = criaProduto();
gravaProduto(session, produto);
}
// outros mtodos
}
5.7 - Exerccios
1) Crie uma classe chamada CriadorDeSession no pacote br.com.caelum.goodbuy.infra. Essa classe ser a
responsvel por criar uma Session.
2) Selecione o mtodo getSession da classe AdicaoDeProduto.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Com as linhas selecionadas, pressione as teclas Alt + Shift + V (Move Method), para criar um mtodo
com essas linhas. Aps pressionar essas teclas, aparecer a seguinte tela:
4) A tela que est sendo mostrada est esperando pela classe que ficar com esse mtodo. Pressione o boto
Browse..., e busque pela classe CriadorDeSession, conforme a imagem abaixo:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
5) Aps encontrar a classe CriadorDeSession, pressione o boto OK para escolher essa classe, depois OK
novamente para confirmar a mudana do mtodo para essa classe.
6) Aparecer uma tela avisando que a visibilidade do mtodo getSession ser alterada public. Confirme essa
alterao pressionando o boto Continue.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
8) Crie uma classe chamada ProdutoDao no pacote br.com.caelum.goodbuy.dao. Essa classe ser a responsvel por encapsular as chamadas ao Hibernate.
9) Mova o mtodo gravaProduto para a classe ProdutoDao. Utilize as teclas de atalho que vimos nesse exerccio.
10) Repare como ficou a classe aps essa alterao:
public class AdicaoDeProduto {
11) Vamos refatorar agora a classe ProdutoDao, que est no momento assim:
public class ProdutoDao {
12) A classe AdicaoDeProduto agora no compila. Vamos trocar o acesso esttico por acesso a uma instncia
do dao:
Captulo 5 - Refatorando - Exerccios - Pgina 27
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
13) O mtodo gravaProduto recebe uma Session. Mas o ProdutoDao deveria ser responsvel pelo acesso a
dados, ento ela mesma deve ser responsvel por criar a session. Mude isso no ProdutoDao:
public class ProdutoDao {
14) Por ltimo, no precisamos criar uma Session dentro do mtodo gravaProduto, vamos fazer isso dentro do
construtor da classe, e colocar a Session como atributo:
public class ProdutoDao {
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
15) Agora a chamada do mtodo gravaProduto est correta, porm repare como est um pouco redundante:
new ProdutoDao().gravaProduto(produto);
Vamos fazer essa mudana de nome de mtodo utilizando o Eclipse. Pressione as teclas Alt + Shift + R
(rename) mtodo da classe ProdutoDao, e o Eclipse pedir o novo nome para o mtodo.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
16) (Opcional) Refatore as outras classes de teste, AlteracaoDeProduto e RemocaoDeProduto, para utilizar o
ProdutoDao.
C APTULO
VRaptor
6.1 - Sobre o VRaptor
VRaptor 3 um framework MVC para web focado no desenvolvimento gil.
Atravs da inverso de controle e injeo de depndencias, ele diminui drasticamente o tempo de trabalho
que seria perdido com o cdigo repetitivo: validaes, converses, direcionamentos, ajax e lookups.
31
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Repare que essa classe no herda nem implementa nenhuma interface. Essa classe um exemplo de um
POJO. Isso muito importante porque a classe est bem simples, bem legvel.
Agora como fazer para chamar essa lgica? Queremos cham-la assim pelo browser:
http://localhost:8080/goodbuy/mundo/boasVindas
Para chamar essa lgica, temos que anotar nossa classe que contm a regra de negcio, para indicar para
o controlador do VRaptor que essa classe deve ser controlada por ele.
A anotao que utilizaremos para indicar isso a @Resource.
Nossa classe ficar assim:
1
2
3
4
5
6
7
8
9
package br.com.caelum.goodbuy;
import br.com.caelum.vraptor.Resource;
@Resource
public class Mundo {
public void boasVindas() {
System.out.println("ol mundo!");
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
10
11
12 }
O VRaptor vai usar uma conveno para chamar o nosso mtodo: para executar o mtodo boasVindas() da
classe Mundo, posso chamar no browser a URI /mundo/boasVindas a partir da nossa aplicao, ou seja, a URI
http://localhost:8080/goodbuy/mundo/boasVindas.
1
2
3
4
5
6
7
8
9
10
11
12
package br.com.caelum.goodbuy;
import br.com.caelum.vraptor.Resource;
@Resource
public class Mundo {
public void boasVindas() {
System.out.println("ol mundo!");
}
}
6.5 - Exerccios
1) Crie a classe Mundo no pacote br.com.caelum.goodbuy.
1 package br.com.caelum.goodbuy;
2
3 public class Mundo {
4
5
public void boasVindas() {
6
System.out.println("ol mundo!");
7
}
8
9 }
boasVindas
4) Ao acessar essa url, mostrada uma pgina com erro 404 (pgina no encontrada). Isso ocorreu porque
ainda no criamos a pgina de resultado. Ela ser criada na prxima seo. O importante verificar o log
da aplicao, na view Console do Eclipse, e ver que a mensagem apareceu.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
5) (Opcional) Crie outro mtodo, parecido com o que fizemos, e acesse pelo browser. Se quiser, esse mtodo
pode ser em outra classe.
Conforme foi dito anteriormente, o VRaptor prefere convenes do que configuraes. A conveno de
redirecionamento de pginas aps a lgica a seguinte:
/WEB-INF/jsp/{nomeDoResource}/{lgica}.jsp
{nomeDoResource} = Mundo
{lgica} = boasVindas()
O VRaptor ir fazer algumas modificaes nesses valores. A primeira letra do nome do resource, que para
ns o nome da classe, ser passado para minsculo. O resto do nome continuar igual. J o nome da lgica
continuar igual, mas sem os parnteses. Ento o VRaptor ir considerar os seguintes valores:
{nomeDoResource} = mundo
{lgica} = boasVindas
E a pgina que ele buscar ser a seguinte:
/WEB-INF/jsp/mundo/boasVindas.jsp
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
6.7 - Exerccios
1) Crie uma pasta chamada jsp dentro da pasta WEB-INF. Cuidado, o nome da pasta deve ser com letras
minsculas.
2) Crie uma pasta chamada mundo dentro da pasta WEB-INF/jsp. Cuidado, o nome da pasta deve ser com letras
minsculas.
3) Crie um jsp chamado boasVindas.jsp dentro da pasta WEB-INF/jsp/mundo. Seu projeto deve ficar assim:
5) Vamos testar nossas alteraes. Inicie o Tomcat na view Servers, depois abra um browser e digite a seguinte
url: http://localhost:8080/goodbuy/mundo/boasVindas
6) Verifique se o console mostra a mensagem ol mundo!, e verifique se o browser exibe a mensagem Ol
Mundo.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy;
@Resource
public class Mundo {
public String boasVindas() {
return "ol mundo!";
}
}
Agora o mtodo boasVindas() no imprime nada, apenas retorna uma string. Aps a execuo desse
mtodo, essa string j estar disponvel na view.
muito importante perceber a legibilidade desse mtodo. uma classe java normal, e um mtodo que
retorna uma string.
Para que a view possa imprimir esse valor, vamos utilizar Expression Language no nosso jsp. Nosso jsp
ficar assim:
Mensagem vinda da lgica:
${string}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Como utilizamos o retorno do mtodo para disponibilizar informaes para a view, no temos como dar um
nome significativo para utilizar depois.
O VRaptor usa a conveno de buscar o tipo do retorno, nesse nosso caso uma String, e disponibilizar para
a view com o mesmo nome, apenas passando a primeira letra para minsculo. Por esse motivo que foi utilizado
a expression language ${string}.
package br.com.caelum.goodbuy;
@Resource
public class Mundo {
public String boasVindas() {
return "ol mundo!";
}
public List<String> paises() {
List<String> result = new ArrayList<String>();
result.add("Brasil");
result.add("Portugal");
result.add("Japo");
result.add("Canad");
result.add("Paraguai");
return result;
}
}
Note que agora o valor passado na expression language foi ${stringList}. O VRaptor identifica que devolvemos uma lista, e essa lista genrica, informando que o tipo de informao dentro da lista ser String.
Para listas, o VRaptor ir adotar a seguinte conveno:
{tipoDaLista}List
Nosso exemplo era de uma lista de String, ou seja, List<String>, que virou ${stringList}.
Captulo 6 - VRaptor - Disponibilizando colees para a view - Pgina 37
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
6.10 - Exerccios
1) Altere o mtodo boasVindas() da classe Mundo para retornar uma String.
1 package br.com.caelum.goodbuy;
2
3 @Resource
4 public class Mundo {
5
6
public String boasVindas() {
7
return "ol mundo!";
8
}
9
10 }
4) Crie um mtodo chamado paises() na classe Mundo. Esse mtodo deve retornar uma lista de Strings. Seu
cdigo ficar assim:
public List<String> paises() {
List<String> result = new ArrayList<String>();
result.add("Brasil");
result.add("Portugal");
result.add("Japo");
result.add("Canad");
result.add("Paraguai");
return result;
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
http://localhost:8080/goodbuy/mundo/paises
Sua pgina deve exibir o seguinte resultado:
7) (Opcional) Crie outro mtodo que mande alguma informao para a view. Dessa vez, tente retornar um valor
inteiro ou um valor decimal.
8) (Opcional) Crie outro mtodo que retorne um Produto, com algumas informaes preenchidas. Tente imprimir essas informaes no jsp.
9) (Opcional) Na view que exibe os pases, itere sobre a coleo e mostre cada pas em uma linha. Dica: use
o JSTL, tag c:forEach.
10) (Opcional) Um mtodo s pode retornar uma informao. Mas como faramos para disponibilizar para a view
mais de uma informao?
C APTULO
Com essa listagem pronta, podemos us-la no ProdutosController, e criar uma lgica que lista produtos.
Coloque a classe no pacote br.com.caelum.goodbuy.controller:
package br.com.caelum.goodbuy.controller;
public class ProdutosController {
public List<Produto> lista() {
ProdutoDao dao = new ProdutoDao();
return dao.listaTudo();
}
}
40
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Olhando para esse mtodo, que no incio tnhamos definido para ser nossa regra de negcio, ser que temos
apenas isso? Ser que apenas listamos os produtos?
Na verdade esse mtodo est fazendo mais tarefas do que deveria. Ele possui algumas responsabilidades
extras, por exemplo criar o ProdutoDao. A nica parte que a regra de negcio para esse caso a chamada
ao dao.listaTudo(). O resto apenas infraestrutura para permitir executar essa tarefa.
Avaliando esse problema, podemos perceber que estamos buscando recursos, nesse caso um dao, e portanto nos preocupando demasiadamente com os mesmos. Se buscar um recurso ruim, seja pela complexidade ou dificuldade no acesso, e tambm por no fazer parte da regra de negcio, que tal se ao invs de cri-lo,
recebssemos o mesmo?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package br.com.caelum.goodbuy.controller;
// imports
@Resource
public class ProdutosController {
private ProdutoDao dao;
public ProdutosController(ProdutoDao dao) {
this.dao = dao;
}
public List<Produto> lista() {
return dao.listaTudo();
}
}
Repare como seria mais simples nosso mtodo que executa a regra de negcio, e tambm como o mtodo
s executa a parte que realmente lhe interessa. A busca dos recursos necessrios para a execuo de nessa
lgica de negcios - nesse caso a criao do dao - no interessa pra essa classe.
Um ponto muito importante que temos que notar que para que essa classe funcione corretamente, precisamos de uma instncia de ProdutoDao. Se no tivermos essa instncia, impossvel executar nossa regra de
negcios, pois no existe construtor que no receba um ProdutoDao.
Como vimos antes, essa lgica redireciona para a jsp /WEB-INF/jsp/produtos/lista.jsp, e como retornamos uma lista de produtos, existe uma varivel chamada ${produtoList} disponvel no jsp.
Para podermos mostrar a listagem de um jeito mais fcil, vamos usar uma taglib da JSTL chamada
c:forEach, que capaz de iterar sobre uma lista passada.
<c:forEach items="${produtoList}" var="produto">
</c:forEach>
Usando essa taglib, vamos criar uma tabela com todos os produtos do sistema:
<table>
<thead>
<tr>
<th>Nome</th>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<th>Descrio</th>
<th>Preo</th>
</tr>
</thead>
<tbody>
<c:forEach items="${produtoList}" var="produto">
<tr>
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
</tr>
</c:forEach>
</tbody>
</table>
Essa caracterstica de, com um objeto em mos, saber que seus mtodos podem ser invocados sem
necessidade de nenhuma dependncia externa extra, parte do que foi chamado de Good Citizen: http:
//docs.codehaus.org/display/PICO/Good+Citizen
Em um objeto todas as dependncias devem ser passadas durante o processo de construo evitando
assim um possvel estado incosistente.
Como poderia ento criar o meu ProdutosController?
1 ProdutoDao dao = new ProdutoDao();
2 ProdutosController controller = new ProdutosController(dao);
Repare que logo aps a segunda linha ser executada, tenho a confiana de que o objeto referenciado pela
varivel controller est preparado para ter seus mtodos invocados.
Dessa maneira injetamos todas as nossas dependncias durante a instanciao do controlador, um trabalho
manual que ficar extremamente repetitivo e propcio a erro a medida que aumenta o nmero de dependncias
de nossa classe.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora a classe ProdutoDao tambm ser controlada pelo VRaptor. Dessa forma, quando o VRaptor for instanciar a classe ProdutosController, ele verificar que ela depende de um ProdutoDao, e criar uma instncia
da mesma.
O que acabamos de fazer foi criar uma maneira de passar as dependncias para onde for necessrio, ou
seja, um mecanismo de injetar as dependncias. Por esse motivo, esse conceito chamado de Injeo de
Dependncias.
O VRaptor est fortemente baseado nesse conceito, uma vez que at ele mesmo utiliza o mesmo para
conectar seus componentes internos. O conceito bsico por trs de Dependency Injection (DI) que voc no
deve buscar aquilo que deseja acessar, mas tais necessidades devem ser fornecidas para voc.
Isso se traduz, por exemplo, na passagem de componentes atravs do construtor de seus controladores.
Imagine que seu controlador de clientes necessita acessar um dao. Sendo assim, especifique claramente essa
necessidade.
Testando sua aplicao
Ao usarmos injeo de dependncias, ganhamos uma caracterstica muito boa na nossa aplicao:
a testabilidade. Se recebemos nossas dependncias no construtor, conseguimos passar implementaes falsas ou controladas e, assim, testar unitariamente nossas classes.
Voc pode encontrar um contedo mais aprofundado sobre testes no curso FJ-16 - Laboratrio
Java com Testes, XML e Design Patterns.
7.4 - Exerccios
1) Abra a classe ProdutoDao e adicione o mtodo para listar todos os produtos:
Captulo 7 - Criando o Controlador de Produtos - Injeo de Dependncias - Pgina 43
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
// imports
import br.com.caelum.vraptor.Resource;
@Resource
public class ProdutosController {
public List<Produto> lista() {
}
}
4) Crie o construtor que recebe uma instncia de ProdutoDao, e guarde essa instncia em um atributo.
1 package br.com.caelum.goodbuy.controller;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// imports
import br.com.caelum.vraptor.Resource;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
public ProdutosController(ProdutoDao dao) {
this.dao = dao;
}
public List<Produto> lista() {
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// imports
import br.com.caelum.vraptor.Resource;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
public ProdutosController(ProdutoDao dao) {
this.dao = dao;
}
public List<Produto> lista() {
return dao.listaTudo();
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
9) Deu uma exception! A informao mais importante dela est no final da Stacktrace:
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [br.com.caelum.goodbuy.dao.ProdutoDao] is defined:
Unsatisfied dependency of type [class br.com.caelum.goodbuy.dao.ProdutoDao]:
expected at least 1 matching bean
at org.springframework.beans.factory.support....
Ou seja, o Spring -- que usado pelo VRaptor para gerenciar dependncias -- no conhece o ProdutoDao.
Precisamos indicar para o VRaptor que ele tem que registrar o ProdutoDao no Spring, com a anotao
@Component.
10) Anote a classe ProdutoDao com @Component, para indicar que essa classe uma dependncia e pode ser
instanciada pelo VRaptor sempre que necessrio.
package br.com.caelum.goodbuy.dao;
// imports
import br.com.caelum.vraptor.ioc.Component;
@Component
public class ProdutoDao {
//...
}
11) Se acessarmos de novo a URI que executa o mtodo lista: http://localhost:8080/goodbuy/produtos/lista veremos a listagem de produtos:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy.controller;
// imports
@Resource
public class ProdutosController {
public void adiciona(Produto produto) {
ProdutoDao dao = new ProdutoDao();
dao.salva(produto);
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
produto, o VRaptor vai usar um parmetro da requisio chamado produto.nome. Para o campo descricao o
parmetro vai ser produto.descricao. Ou seja, a partir do nome do parmetro do mtodo, podemos usar pontos
para setar os campos do parmetro, desde que voc tenha getters e setters para eles.
O que vale o nome do parmetro: se o mtodo adiciona fosse
public void adiciona(Produto novoProduto) {...}
o VRaptor iria usar os parmetros novoProduto.nome, novoProduto.descricao, etc para popular os campos
do produto.
Reflection no nome dos parmetros
Infelizmente, o Java no realiza reflection em cima de parmetros, esses dados no ficam disponveis em bytecode (a no ser se compilado em debug mode, porm algo opcional). Isso faz com
que a maioria dos frameworks que precisam desse tipo de informo criem uma anotao prpria
para isso, o que polui muito o cdigo (exemplo no JAX-WS, onde comum encontrar um mtodo
como o acima com a assinatura void add(@WebParam(name="cliente) Cliente cliente).
O VRaptor tira proveito do framework Paranamer (http://paranamer.codehaus.org), que consegue
tirar essa informao atravs de pr compilao ou dos dados de debug, evitando criar mais uma
anotao. Alguns dos desenvolvedores do VRaptor tambm participam no desenvolvimento do
Paranamer.
Agora que sabemos como o VRaptor popula os parmetros do mtodo, vamos criar um formulrio para
poder cadastrar produtos. Esse formulrio dever ficar na pasta WEB-INF/jsp/produtos, com o nome de
formulario.jsp.
<form action="adiciona">
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao"></textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
Como nossas pginas ficam sempre na pasta WEB-INF, no temos como acessar diretamente pelo browser
um jsp. Para acessar a pgina que acabamos de criar, vamos criar um mtodo na classe ProdutosController
para chamar nossa pgina, usando a conveno do VRaptor.
@Resource
public class ProdutosController {
Captulo 7 - Criando o Controlador de Produtos - Criando o formulrio HTML - Pgina 48
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
http://localhost:8080/goodbuy/produtos/formulario
Aps preencher as informaes do formulrio e enviar, o VRaptor vai invocar o mtodo adiciona, passando
o Produto populado com os campos que digitamos. E como diz a conveno do VRaptor, precisamos criar o
arquivo adiciona.jsp, na pasta WEB-INF/jsp/produtos. Por enquanto vamos colocar o seguinte contedo:
Produto adicionado com sucesso!
7.7 - Exerccios
1) Crie o mtodo adiciona que recebe um Produto como parmetro e faa a chamada ao mtodo salva do dao
passando esse produto.
1 package br.com.caelum.goodbuy.controller;
2 public class ProdutosController {
3
4
//...
5
6
public void adiciona(Produto produto) {
7
dao.salva(produto);
8
}
9
10 }
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Abra o arquivo que acabamos de criar e coloque os campos para de digitao, assim como o boto e o
formulrio.
<form action="adiciona">
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao"></textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora que temos essa instncia de Result podemos us-la para mudar o resultado da lgica adiciona do
ProdutosController. O jeito de fazer isso dizer: Como resultado, quero redirecionar para a lgica ProdutosController.lista(). Ou em java:
result.redirectTo(ProdutosController.class).lista();
Captulo 7 - Criando o Controlador de Produtos - Redirecionar para listagem depois de adicionar - Pgina 52
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Repare que no foi necessrio criar arquivos de configurao para fazer essa mudana de conveno,
tudo feito no prprio mtodo. Ao olhar para o mtodo adiciona fica claro que o resultado dele no vai ser o
adiciona.jsp, vai ser a lgica lista do ProdutosController.
Como o redirecionamento para uma lgica do mesmo controller voc pode ainda usar um comando mais
enxuto:
result.redirectTo(this).lista();
redirecionamento do lado do servidor, usando o mtodo forwardTo: o servidor ir redirecionar internamente para a lgica especificada, desse modo possvel mudar o resultado de uma lgica sem mudanas
do lado do cliente. No use esse redirecionamento em requisies POST.
Alm disso, podemos redirecionar nossa requisio para pginas usando outros mtodos:
result.use(Results.logic()).
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
result.use(Results.page()).
status
codes
Headers
HTTP.
Ex:
result.use(Results.referer()): Usa como resultado a pgina que originou a requisio. Usa um Header
HTTP chamado Referer que no obrigatrio, e que removido por alguns Proxies e Firewalls, ento
cuidado ao usar esse resultado.
7.9 - Exerccios
1) Receba um Result no construtor do ProdutosController e guarde-o em um atributo.
import br.com.caelum.vraptor.Result;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
private final Result result;
public ProdutosController(ProdutoDao dao, Result result) {
this.dao = dao;
this.result = result;
}
//...
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Reinicie o tomcat para que ele carregue as suas mudanas, abra o formulrio e adicione mais um produto.
Repita essa operao e adicione mais produtos.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Comeando pela atualizao, primeiro precisamos de uma lgica que mostre um formulrio com os dados
do produto preenchidos. Vamos dar a essa lgica o nome de edita.
public class ProdutosController {
public void edita() {...}
}
Precisamos passar para essa lgica o id do produto que queremos editar, para que possamos carregar suas
informaes do banco de dados. E para passar o Produto carregado para a jsp, basta retorn-lo:
public class ProdutosController {
public Produto edita(Long id) {
return dao.carrega(id);
}
}
Agora podemos criar o formulrio de edio, passando os valores do produto carregado para os inputs:
<form action="altera">
<fieldset>
<legend>Editar Produto</legend>
<input type="hidden" name="produto.id" value="${produto.id }" />
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
Repare que precisamos tambm colocar um input hidden com o valor do produto.id para que saibamos
qual produto ser alterado. A action do form para a lgica altera, ento precisamos cri-la no nosso controller.
Como usamos os nomes dos campos do formulrio do jeito certo, podemos simplesmente receber um Produto
como argumento na lgica altera.
public class ProdutosController {
//...
public void altera(Produto produto) {
dao.atualiza(produto);
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Assim como na lgica adiciona, fizemos um POST num formulrio, ento ao invs de colocar uma jsp com
uma mensagem de Produto alterado com sucesso, vamos redirecionar para a listagem de produtos.
public class ProdutosController {
//...
public void altera(Produto produto) {
dao.atualiza(produto);
result.redirectTo(this).lista();
}
Agora precisamos de um jeito fcil de editar um produto. Vamos ento adicionar um link para editar o
produto, na listagem:
<c:forEach items="${produtoList}" var="produto">
<tr>
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
<td><a href="edita?id=${produto.id }">Editar</a></td>
</tr>
</c:forEach>
Vamos tambm remover produtos no nosso controller. Para remover um produto precisamos apenas do seu
id, ento basta criar a lgica:
public void remove(Long id) {
Produto produto = dao.carrega(id);
dao.remove(produto);
}
Tambm no faz muito sentido criar uma pgina de resultado para a remoo, ento vamos redirecionar
para a listagem.
public void remove(Long id) {
Produto produto = dao.carrega(id);
dao.remove(produto);
result.redirectTo(ProdutosController.class).lista();
}
Assim como criamos o link para a edio, vamos criar um link para a remoo:
<c:forEach items="${produtoList}" var="produto">
<tr>
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
<td><a href="edita?id=${produto.id }">Editar</a></td>
<td><a href="remove?id=${produto.id }">Remover</a></td>
</tr>
</c:forEach>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
7.11 - Exerccios
1) Crie as lgicas para edio de produtos no ProdutosController:
public Produto edita(Long id) {
return dao.carrega(id);
}
public void altera(Produto produto) {
dao.atualiza(produto);
result.redirectTo(this).lista();
}
para
incluir
um
link
para
edio,
no
arquivo
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<td>${produto.nome }</td>
<td>${produto.descricao }</td>
<td>${produto.preco }</td>
<td><a href="edita?id=${produto.id }">Editar</a></td>
</tr>
</c:forEach>
5) Reinicie o tomcat e acesse a listagem http://localhost:8080/goodbuy/produtos/lista. Escolha um dos produtos e clique em Editar.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
C APTULO
Refatorando os DAOs
8.1 - Injeo de dependncias no DAO
Existe algo errado com o ProdutoDao: ele est criando uma Session, que uma dependncia dele. Usando
injeo de dependncias, podemos receber a Session no construtor, e o VRaptor vai se encarregar de criar
essa Session e passar pro construtor do dao.
@Component
public class ProdutoDao {
private final Session session;
public ProdutoDao(Session session) {
this.session = session;
}
//...
}
Mas como falar que a Session pode ser usada como dependncia?
Poderamos fazer a mesma alterao que fizemos no ProdutoDao, anotando a classe Session do pacote
org.hibernate com @Component. Assim, a sesso do Hibernate tambm seria gerenciada pelo VRaptor. Mas
ser que podemos anotar uma classe do Hibernate? S se baixssemos o cdigo fonte e compilssemos na
mo, junto com nosso projeto. Daria muito trabalho e seria muito estranho, nosso projeto alterando outro projeto.
Para resolver isso, o VRaptor possui um mecanismo que nos auxilia a passar dependncias de outros
projetos para nossas classes. Basta criarmos uma classe que implementa a interface ComponentFactory. Essa
interface define um nico mtodo, o getInstance.
Criando essa classe, podemos definir a lgica de criao de um determinado objetos, e o VRaptor usar a
instncia retornada para passar como dependncia para outras classes. No nosso caso, podemos criar uma
classe que cria sesses do Hibernate.
J temos uma classe que cria uma classe que nos d uma instncia de Session: a CriadorDeSession, ento
para transform-la em um ComponentFactory basta implementar a interface. Mas o mtodo que nos d a Session
se chama getSession, e a interface espera que o mtodo se chame getInstance. E, por ltimo, anotamos a
classe com @Component para que o VRaptor saiba como cri-la. A classe ficar assim:
import br.com.caelum.vraptor.ioc.ComponentFactory;
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
public Session getInstance() {
62
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Note que utilizamos Generics para informar qual ser o tipo de objeto criado por essa classe.
O mtodo getInstance ainda est fazendo coisas demais: ele cria uma AnnotationConfiguration, configura, constri uma SessionFactory, e s ento abre uma sesso. No seria suficiente s abrir uma sesso
a partir da SessionFactory? A SessionFactory uma dependncia para criar uma Session. Ento vamos
receb-la no construtor da classe.
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
private final SessionFactory factory;
public CriadorDeSession(SessionFactory factory) {
this.factory = factory;
}
public Session getInstance() {
return factory.openSession();
}
}
Podemos usar a mesma idia e criar um ComponentFactory para SessionFactory, com o cdigo que removemos da CriadorDeSession. No podemos nos esquecer da anotao @Component:
@Component
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
public SessionFactory getInstance() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
SessionFactory factory = configuration.buildSessionFactory();
return factory;
}
}
Poderamos continuar esse processo, e criar uma ComponentFactory para AnnotationConfiguration tambm, mas vamos parar por aqui. Resumindo o que acabamos de fazer, chegamos a seguinte situao:
ProdutosController depende de ProdutoDao que depende de Session que depende de SessionFactory.
primeira vista pode parecer que fizemos muita coisa s pra resolver o problema da Session. Mas a
vantagem de utilizarmos essa abordagem que para as nossas prximas lgicas, no precisaremos fazer
Captulo 8 - Refatorando os DAOs - Injeo de dependncias no DAO - Pgina 63
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
nada. Se tivermos que criar 50 DAOs no nosso sistema, no precisamos mais se preocupar com Sessions,
basta receber uma no construtor.
8.2 - Exerccios
1) Faa a classe ProdutoDao receber uma Session no construtor. Remova o mtodo getSession:
@Component
public class ProdutoDao {
private final Session session;
public ProdutoDao(Session session) {
this.session = session;
}
//...
}
2) Modifique a classe CriadorDeSession. Faa com que essa classe implemente a interface ComponentFactory,
passando a Session como tipo genrico. Renomeie o mtodo getSession para getInstance, e remova o
static.
import br.com.caelum.vraptor.ioc.ComponentFactory;
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
public Session getInstance() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
SessionFactory factory = configuration.buildSessionFactory();
Session session = factory.openSession();
return session;
}
}
3) A classe CriadorDeSession est fazendo coisas demais. Recorte a parte que cria uma SessionFactory e
receba uma SessionFactory no construtor. A classe deve ficar assim:
1 package br.com.caelum.goodbuy.infra;
2
3 // imports
4
5 public class CriadorDeSession implements ComponentFactory<Session> {
6
7
public CriadorDeSession(SessionFactory factory) {
8
}
9
10
public Session getInstance() {
11
Session session = factory.openSession();
12
return session;
Captulo 8 - Refatorando os DAOs - Exerccios - Pgina 64
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
13
14
15 }
4) Aps ter criado o construtor, guarde o parmetro factory em um atributo. Utilize as teclas de atalho do
Eclipse. Pressione as teclas Ctrl + 1 (quick fix) no parmetro factory, e o Eclipse nos mostrar algumas
sugestes. Selecione a primeira opo, Assign parameter to new field.
5) Anote a classe com @Component, para indicar que essa classe uma dependncia e pode ser instanciada
pelo VRaptor sempre que necessrio.
6) Crie a classe CriadorDeSessionFactory no pacote br.com.caelum.goodbuy.infra que implementa a
interface ComponentFactory<SessionFactory>.
1 package br.com.caelum.goodbuy.infra;
2
3 // imports
4
5 public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
6
7 }
7) A classe no compila, pois falta implementar o mtodo que foi definido na interface. Para isso utilize as
teclas de atalho do Eclipse: coloque o cursor no nome da classe e digite Ctrl+1. Selecione a opo Add
unimplemented methods.
Coloque a criao da fbrica de sesses que voc tinha recortado da outra classe nesse mtodo.
1 package br.com.caelum.goodbuy.infra;
2
3 // imports
4
5 public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
6
7
public SessionFactory getInstance() {
8
AnnotationConfiguration configuration = new AnnotationConfiguration();
9
configuration.configure();
10
11
SessionFactory factory = configuration.buildSessionFactory();
Captulo 8 - Refatorando os DAOs - Exerccios - Pgina 65
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
12
13
14
15 }
return factory;
}
8) Anote a classe com @Component, para indicar que essa classe uma dependncia e pode ser instanciada
pelo VRaptor sempre que necessrio.
9) As classes de teste que tnhamos criado no esto compilando mais, pois mudamos o construtor do
ProdutoDao. Mude as chamadas, usando as ComponentFactories para criar as dependncias. Note que na
aplicao web, o VRaptor que vai se encarregar executar esse cdigo.
SessionFactory factory = new CriadorDeSessionFactory().getInstance();
10) Acesse o sistema e veja que ele continua funcionando como antes.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Para alguns casos, um componente deve durar para todas as requisies, e esse componente pode ser
utilizado por todos os clientes. Esse caso poderia ser a fbrica de sesses do Hibernate. Essa fbrica ficaria
nica para toda a aplicao, e qualquer cliente que precisar de uma sesso, usaramos essa fbrica.
Para cada cenrio acima, o VRaptor definiu um tempo de vida. Esse tempo de vida no VRaptor chamado
de escopo. O VRaptor define quatro escopos, e para utiliz-los, basta anotarmos nossos componentes com
uma das seguintes anotaes:
@RequestScoped - o componente ser criado a cada requisio. o escopo padro dos componentes.
@SessionScoped - o componente ser criado uma vez por sesso (HttpSession) de usurio
@ApplicationScoped - o componente ser criado uma nica vez na aplicao.
@PrototypeScoped - o componente ser criado toda vez que for solicitado.
Para nossa classe CriadorDeSessionFactory, queremos ter apenas uma instncia dessa classe para toda
nossa aplicao, ento vamos anot-la com @ApplicationScoped.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package br.com.caelum.goodbuy.infra;
// imports
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
public SessionFactory getInstance() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
return configuration.buildSessionFactory();
}
}
Agora o VRaptor vai criar apenas uma instncia dessa classe. Mas repare que o mtodo getInstance cria
toda vez uma fbrica nova. Precisamos refatorar para que seja criada apenas uma fbrica.
Para resolver esse problema, poderamos criar um atributo do tipo SessionFactory e colocar no construtor
da classe a criao da fbrica.
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory>{
private final SessionFactory factory;
public CriadorDeSessionFactory() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
factory = configuration.buildSessionFactory();
}
Captulo 8 - Refatorando os DAOs - Escopos definidos pelo VRaptor - Pgina 67
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
os mtodos anotados com @PostConstruct sero executados assim que o escopo for iniciado, ou seja, no
comeo da requisio, sesso de usurio ou da aplicao.
os mtodos anotados com @PreDestroy sero executados assim que o escopo for finalizado, ou seja, no
final da requisio, sesso de usurio ou da aplicao.
Desse modo, podemos usar um mtodo anotado com @PostConstruct para criar a fbrica de sesses,
e como uma boa prtica fechar todos os recursos que abrimos, devemos criar um mtodo anotado com
@PreDestroy que vai fechar a fbrica.
Fazendo essas modificaes, nossa classe ficaria assim:
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory>{
private SessionFactory factory;
@PostConstruct
public void abre() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
this.factory = configuration.buildSessionFactory();;
}
public SessionFactory getInstance() {
return this.factory;
}
@PreDestroy
public void fecha() {
this.factory.close();
}
}
Note que no precisamos de um bloco esttico ou um singleton, pois o VRaptor que vai se encarregar
de criar apenas uma instncia da fbrica, e quando o recurso no for mais necessrio, o VRaptor vai fechar a
fbrica.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy.infra;
// imports
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
private final SessionFactory factory;
private Session session;
public CriadorDeSession(SessionFactory factory) {
this.factory = factory;
}
@PostConstruct
public void abre() {
this.session = factory.openSession();
}
public Session getInstance() {
return this.session;
}
@PreDestroy
public void fecha() {
this.session.close();
}
}
Note que criamos um atributo chamado session, que guardar uma sesso do Hibernate. Temos que
lembrar que componentes que controlados pelo VRaptor sempre tm um escopo, mesmo no estando anotado.
O escopo padro para componentes o de requisio, ou seja, daria na mesma anotar essa classe com
@RequestScoped. Desse modo, uma sesso vai ser aberta no comeo da requisio, e ser fechada assim que
acabar a requisio
8.6 - Exerccios
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
// imports
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory> {
//...
}
2) Refatore essa classe para que seja criado apenas uma instncia da fbrica.
1 package br.com.caelum.goodbuy.infra;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// imports
@Component
@ApplicationScoped
public class CriadorDeSessionFactory implements ComponentFactory<SessionFactory>{
private SessionFactory factory;
@PostConstruct
public void abre() {
AnnotationConfiguration configuration = new AnnotationConfiguration();
configuration.configure();
this.factory = configuration.buildSessionFactory();;
}
public SessionFactory getInstance() {
return this.factory;
}
@PreDestroy
public void fecha() {
this.factory.close();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
}
@PostConstruct
public void abre() {
this.session = factory.openSession();
}
public Session getInstance() {
return this.session;
}
@PreDestroy
public void fecha() {
this.session.close();
}
}
C APTULO
Validando formulrios
Quando temos um formulrio para adicionar dados no nosso sistema, geralmente precisamos verificar se os
dados esto corretos e consistentes. Por exemplo no gostaramos que o usurio digitasse vinte e trs reais
no campo preo, ou que o campo nome fosse vazio.
Ento, antes de salvar esses dados no banco precisamos verificar se tudo est como esperamos. Chamamos isso de validao. Existem duas maneiras de fazer validao:
Validao do lado do cliente: verificamos os dados antes de mandar os dados para o servidor, assim o
feedback da validao instantneo.
Validao do lado do servidor: os dados so enviados para o servidor, e se houver erros, o servidor
devolve esses erros para o cliente.
A validao do lado do cliente geralmente feita usando javascript, impedindo que o formulrio seja submetido caso acontea algum erro. A do lado do servidor feita em Java, e o VRaptor pode te ajudar a faz-la.
9.1 - Validator
O VRaptor possui um componente, chamado Validator, para ajudar a fazer validaes do lado do servidor.
Com ele, voc pode executar a lgica de validao, e caso haja algum erro, especificar para onde deve ser
redirecionada a requisio. Para obter um Validator basta receb-lo no construtor do seu Controller:
import br.com.caelum.vraptor.Validator;
@Resource
public class ProdutosController {
private final ProdutoDao dao;
private final Result result;
private final Validator validator;
public ProdutosController(ProdutoDao dao, Result result, Validator validator) {
this.dao = dao;
this.result = result;
this.validator = validator;
}
//...
}
Com essa instncia do Validator podemos adicionar erros de validao para o caso em que os dados da
requisio no vieram corretos. Geralmente validamos quando estamos adicionando ou atualizando algo no
banco, ento vamos executar a validao no mtodo adiciona.
72
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
maneira fluente: voc diz o que quer que seja verdade, e caso no seja adiciona uma mensagem
internacionalizada:
public void adiciona(final Produto produto) {
validator.checking(new Validations() {{
that(produto.getNome() != null && produto.getNome().length() >= 3,
"produto.nome","nome.obrigatorio");
that(produto.getDescricao() != null && produto.getDescricao().length() <= 40,
"produto.descricao", "descricao.obrigatoria");
that(produto.getPreco() != null && produto.getPreco() > 0.0,
"produto.preco", "preco.positivo");
}});
dao.salva(produto);
result.redirectTo(this).lista();
}
Como a mensagem internacionalizada, voc precisa colocar essas chaves no seu arquivo messages.properties:
nome.obrigatorio = Nome obrigatrio e precisa ter mais de 3 letras
Captulo 9 - Validando formulrios - Validator - Pgina 73
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Em ambas as maneiras voc precisa especificar pra qual lgica voltar quando der erro de validao. No
nosso caso, ao dar erro queremos voltar pro formulrio, ento fazemos:
validator.onErrorUsePageOf(ProdutosController.class).formulario();
A lgica de redirecionamento a mesma que se voc estivesse usando o result.of(). Voc deve colocar essa
chamada logo no final da sua lgica de validao, no momento em que, se houver algum erro, a execuo deve
parar e voltar para o formulrio, mostrando os erros.
Quando existem erros de validao, o VRaptor disponibiliza para a jsp um atributo chamado ${errors}, que
contm a lista dos erros que aconteceram. Ento voc usar o seguinte cdigo para mostrar as mensagens no
seu jsp:
<ul>
<c:forEach items=${errors} var="error">
<li>${error.category} - ${error.message}</li>
</c:forEach>
</ul>
Alm disso, se quisermos que o formulrio volte preenchido quando houver algum erro de validao, precisamos passar como valor dos inputs, o que foi mandado para a requisio:
<form action="adiciona">
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
9.2 - Exerccios
1) Receba um Validator no construtor do ProdutosController e guarde-o num atributo:
import br.com.caelum.vraptor.Validator;
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Resource
public class ProdutosController {
private final ProdutoDao dao;
private final Result result;
private final Validator validator;
public ProdutosController(ProdutoDao dao, Result result, Validator validator) {
this.dao = dao;
this.result = result;
this.validator = validator;
}
//...
}
2) Modifique o mtodo adiciona para que inclua a validao dos campos do produto. Se preferir, faa a
validao na forma fluente.
public void adiciona(final Produto produto) {
if (produto.getNome() == null || produto.getNome().length() < 3) {
validator.add(new ValidationMessage(
"Nome obrigatrio e precisa ter mais de 3 letras", "produto.nome"));
}
if (produto.getDescricao() == null || produto.getDescricao().length() > 40) {
validator.add(new ValidationMessage(
"Descrio obrigatria no pode ter mais que 40 letras",
"produto.descricao"));
}
if (produto.getPreco() <= 0) {
validator.add(new ValidationMessage(
"Preo precisa ser positivo", "produto.preco"));
}
validator.onErrorUsePageOf(ProdutosController.class).formulario();
dao.salva(produto);
result.redirectTo(this).lista();
}
3) Abra o arquivo header.jspf, e adicione o cdigo para mostrar os erros de validao, dentro da div com
id="erros
.
<div id="erros">
<ul>
<c:forEach items="${errors}" var="error">
<li>${error.category } - ${error.message }</li>
</c:forEach>
</ul>
</div>
4) Adicione o cdigo para que o formulrio continue preenchido quando der erro de validao:
<form action="adiciona">
Captulo 9 - Validando formulrios - Exerccios - Pgina 75
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<fieldset>
<legend>Adicionar Produto</legend>
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
6) (Opcional) O mtodo atualiza tambm precisa de validao. Voc consegue aproveitar o cdigo do adiciona
para fazer isso? Ele deve redirecionar para o mesmo lugar em caso de erro?
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
preco;
Com nosso modelo anotado, podemos substituir as validaes que fizemos pelo Hibernate Validator. O interface Validator do VRaptor possui um mtodo chamado validate que valida qualquer objeto usando o Hibernate
Validator. Assim, se voc chamar:
validator.validate(produto)
todos os erros de validao que ocorrerem sero adicionados ao validator automaticamente. Assim podemos substituir as validaes que tnhamos feito antes, pelas equivalentes do Hibernate Validator.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Entity
public class Produto {
@Id @GeneratedValue
private Long id;
@NotNull
@Length(min=3)
private String nome;
@NotNull
@Length(max=40)
private String descricao;
@Min(0)
private Double
preco;
3) Mude a lgica de validao do mtodo adiciona do ProdutosController para usar o Hibernate Validator:
@Resource
public class ProdutosController {
//...
public void adiciona(final Produto produto) {
validator.validate(produto);
validator.onErrorUsePageOf(ProdutosController.class).formulario();
dao.salva(produto);
result.redirectTo(this).lista();
}
}
4) Acesse o browser e adicione algum produto invlido, para ver as mensagens de validao.
As mensagens esto em ingls e por enquanto no possvel internacionaliz-las, mas isso ser implementado em prximas verses do VRaptor.
C APTULO
10
REST
10.1 - O que REST
REST um conjunto de restries que define um padro arquitetural com caractersticas especficas: benefcios e dificuldades determinadas aparecero ao implementar um sistema seguindo o padro REST.
restfulie-java
Substantivos: Recursos
Substantivos so os nomes dos recursos do seu sistema. Quando fazemos requisies Web, precisamos
falar o caminho da mesma, a URI (Unified Resource Identifier ), ou seja, o identificador nico de um recurso.
Ento ao criar as URIs do nosso sistema devemos levar em conta que elas representam recursos, no
aes. Em sistemas REST, nossas URIs devem conter apenas substantivos, que so nossos recursos:
/produtos/adiciona no boa, pois contm um verbo e no est identificando um recurso, mas sim uma
operao. Para representar a adio de um produtos podemos usar a URI /produtos com o mtodo HTTP
POST, que representa que estamos adicionando alguma informao no sistema.
79
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Verbos: Operaes
Uma das caracterstas mais importantes de REST que voc tenha um conjunto pequeno e fixo de operaes bem definidas, gerando uma interface uniforme. Desse modo, para duas aplicaes conversarem elas
no precisam implementar diversas operaes: basta usar as operaes definidas. Perceba que adicionar um
produto e adicionar um usurio no sistema so operaes equivalentes.
O protocolo HTTP possui sete operaes -- os mtodos GET, POST, PUT, DELETE, HEAD, OPTIONS e
TRACE. Duas delas (GET e POST) j so bem conhecidas e utilizadas: GET quando clicamos em links ou
digitamos o endereo no navegador, e POST quando preenchemos formulrios de cadastro. Cada mtodo tem
uma semntica diferente e juntando o mtodo URI deveramos conseguir representar todas as aes do nosso
sistema. As semnticas principais so:
GET - recupera informaes sobre o recurso identificado pela URI. Ex: listar produtos, visualizar o produto
45. Uma requisio GET no deve modificar nenhum recurso do seu sistema, ou seja, no deve ter
nenhum efeito colateral, voc apenas recupera informaes do sistema.
POST - adiciona informaes usando o recurso da URI passada. Ex: adicionar um produto. Pode adicionar informaes a um recurso ou criar um novo recurso.
PUT - adiciona (ou modifica) um recurso na URI passada. Ex: atualizar um produto. A diferena fundamental entre um PUT e um POST que no POST a URI significa o lugar que vai tratar a informao, e no
PUT significa o lugar em que a informao ser armazenada.
DELETE - remove o recurso representado pela URI passada. Ex: remover um produto.
HEAD, OPTIONS e TRACE - recuperam metadados da URI passada. Respectivamente o Header, quais
mtodos so possveis e informaes de debug.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
import br.com.caelum.vraptor.Path;
@Resource
public class ProdutosController {
@Path("/produtos")
public List<Produto> lista() {
return dao.listaTudo();
}
}
Dessa forma, o mtodo lista no vai mais estar acessvel pela URI /produto/lista, ele apenas poder ser
acessado pela URI /produtos.
Com a anotao @Path ainda possvel usar templates para extrair parmetros a partir da URI. Por exemplo,
no nosso mtodo edita, precisamos passar um query parameter para falar qual o produto que queremos
editar:
/produto/edita?id=10
As URIs devem identificar recursos (alm de no conter verbos, mas vamos tir-los em breve), e o id algo
que identifica o recurso que queremos acessar, ento ficaria melhor se passssemos esse id dentro da URI,
como por exemplo:
/produto/10/edita
ou seja, uma URI que representa a edio do produto de id 10. Para extrair esse 10 da URI, podemos
colocar um template no nosso @Path. Para isso basta colocar o nome do parmetro que queremos extrair entre
chaves, dentro da URI do @Path, e ento receber o parmetro como argumento do mtodo:
@Path("/produto/{id}/edita")
public Produto edita(Long id) {
return dao.carrega(id);
}
Assim possvel criar URIs mais representativas, que identificam verdadeiramente recursos especficos.
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Get;
@Resource
public class ProdutosController {
@Path("/produtos")
@Get
public List<Produto> lista() {
return dao.listaTudo();
}
}
Se quisermos permitir outros verbos HTTP, podemos usar as anotaes @Post, @Put, @Delete, @Trace e
@Head. A partir do momento que colocamos uma anotao de verbo HTTP no nosso mtodo, ele no poder
ser acessado pelos outros verbos. Ento se tentarmos fazer uma requisio POST /produtos, ela no vai cair
no mtodo lista, a requisio vai retornar um status 405 (Mtodo no suportado), que quer dizer que existe
um recurso nessa URI, mas ele no suporta o verbo HTTP da requisio.
Assim, podemos colocar mais de um mtodo que responda mesma URI, desde que os verbos HTTP sejam
diferentes. Por exemplo, podemos fazer com que o mtodo adiciona tambm responda URI /produtos. Mas
o mtodo adiciona no idempotente: se eu repetir duas vezes uma requisio a esse mtodo, vou adicionar
dois produtos ao sistema. Estamos adicionando produtos, mas no sabemos qual vai ser a URI dele, ento
podemos colocar o verbo POST.
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Post;
@Resource
public class ProdutosController {
@Path("/produtos")
@Post
public void adiciona(Produto produto) {
//...
}
@Path("/produtos")
@Get
public List<Produto> lista() {
return dao.listaTudo();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
A URI /produtos representa a lista de todos os produtos do sistema. E as operaes possveis so:
GET /produtos => recupera a lista de todos os produtos. Mtodo lista.
POST /produtos => adiciona um produto na lista de todos os produtos. Mtodo adiciona.
As URIs do tipo /produtos/42 representam um produto especfico, no caso de id 42. As operaes possveis
so:
GET /produtos/4 => mostra o produto de id 4. Mtodo edita.
PUT /produtos/10 => atualiza o produto de id 10. Mtodo atualiza.
DELETE /produtos/3 => remove o produto de id 3. Mtodo remove.
Nem todas as URIs precisam representar recursos fixos, mas elas precisam semanticamente representar o
mesmo tipo de recurso. Por exemplo a URI /produtos/ultimo pode representar o ltimo produto adicionado e
a URI /produtos/maisVendidos pode representar uma lista dos produtos mais vendidos, que so recursos bem
definidos. No nosso caso precisamos de uma URI para identificar o formulrio de adio de um produto. Vamos
ento usar a seguinte:
GET /produtos/novo => mostra o formulrio para adicionar um novo produto. Mtodo formulario.
Com essa especificao de operaes no recurso Produto, podemos usar as anotaes vistas anteriormente
para deixar o ProdutosController de acordo com ela:
@Resource
public class ProdutosController {
@Get @Path("/produtos/novo")
public void formulario() {...}
@Get @Path("/produtos/{id}")
public Produto edita(Long id) {...}
@Put @Path("/produtos/{produto.id}")
public void altera(Produto produto) {...}
@Post @Path("/produtos")
public void adiciona(final Produto produto) {...}
@Delete @Path("/produtos/{id}")
public void remove(Long id) {...}
@Get @Path("/produtos")
public List<Produto> lista() {...}
}
Note no mtodo altera que o parmetro extrado o produto.id. O VRaptor vai se comportar da mesma
forma que se esse produto.id tivesse vindo da requisio: vai popular o campo id do produto com o valor
extrado.
Captulo 10 - REST - Refatorando o ProdutosController - Pgina 83
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora que mudamos as URIs dos mtodos, precisamos atualizar as nossas jsps para usar as URIs novas.
Podemos usar a tag c:url para poder usar as URIs absolutas, a partir do nosso nome de contexto.
Primeiro na jsp do formulrio, precisamos mudar a action do form para /produtos, e o method para POST.
<form action="<c:url value="/produtos"/>" method="POST">
Ainda existe o link de Remoo, mas o mtodo remove do nosso controller s aceita o verbo DELETE! Como
fazer uma requisio com o verbo DELETE de dentro da sua pgina? Infelizmente os browsers atuais s conseguem fazer requisies GET (atrves de links e formulrios) e POST(atravs de formulrios). Para conseguir usar
os outros verbos, podemos fazer duas coisas:
Por ltimo precisamos mudar o formulrio de edio, colocando a action correta, e mudando o mtodo
para PUT. Mas como colocar method="PUT no nosso formulrio no funciona, precisamos passar o parmetro
_method:
<form action="<c:url value="/produtos/${produto.id }"/>" method="POST">
<fieldset>
<legend>Editar Produto</legend>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<label for="nome">Nome:</label>
<input id="nome" type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" type="text" name="produto.preco" value="${produto.preco }"/>
<!-vvvvvvv
vvv -->
<button type="submit" name="_method" value="PUT">Enviar</button>
</fieldset>
</form>
No precisamos mais passar o input hidden com produto.id, porque essa informao j est na URI!
button e o Internet Explorer
Em algumas verses do Internet Explorer, no possvel usar o name e value do button para
mandar parmetros na requisio. Nesse caso voc precisa trocar o button por:
<input type="hidden" name="_method" value="PUT"/>
<input type="submit" value="Enviar"/>
10.7 - Exerccios
1) Anote os mtodos do ProdutosController para seguirmos a nossa especificao de Produto:
Recurso Produto:
GET /produtos => recupera a lista de todos os produtos. Mtodo lista.
POST /produtos => adiciona um novo produto. Mtodo adiciona.
GET /produtos/4 => mostra o produto de id 4. Mtodo edita.
PUT /produtos/10 => atualiza o produto de id 10. Mtodo atualiza.
DELETE /produtos/3 => remove o produto de id 3. Mtodo remove.
GET /produtos/novo => mostra o formulrio para adicionar um novo produto. Mtodo formulario.
3) Abra o edita.jsp e mude a action e o mtodo do form. Lembre-se que voc no precisa mais do input
hidden do produto.id.
<form action="<c:url value="/produtos/${produto.id }"/>" method="POST">
<!-- ... -->
<button type="submit" name="_method" value="PUT">Enviar</button>
</form>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<td>
<form action="<c:url value="/produtos/${produto.id}"/>" method="POST">
<button class="link" name="_method" value="DELETE">Remover</button>
</form>
</td>
C APTULO
11
Seletores
O JQuery suporta XPath e CSS 3, com seletores avanadssimos (alm dos clssicos id e class).
Veja mais aqui: http://docs.jquery.com/Selectors
Depois de selecionar o(s) elemento(s) desejado(s), podemos chamar mtodos para as mais variadas coisas.
O Hello World do JQuery mostra como exibir e esconder um div especifico:
87
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
$(#meuDiv).show()
$(#meuDiv).hide()
Um outro ponto forte do JQuery que ele possui diversos plugins que fazem vrias coisas interessantes
como validao de formulrios, autocomplete, efeitos visuais, drag n drop, etc. Uma lista de plugins disponveis
pode ser encontrada em http://plugins.jquery.com/. Uma documentao mais completa sobre o JQuery se
encontra em http://docs.jquery.com/ e uma documentao visual em http://www.visualjquery.com/.
Mais sobre javascript
Nesse curso no daremos foco no desenvolvimento de cdigo javascript, boas prticas e caractersticas dessa linguagem, apenas usaremos plugins do JQuery. Mas o cdigo javascript uma parte
bastante importante do desenvolvimento Web, e precisamos ter com ele os mesmos cuidados que
temos com o cdigo java.
Um contedo aprofundado sobre javascript, alm de CSS e HTML, pode ser encontrado no curso
WD-43 | Desenvolvimento Web com HTML, CSS e JavaScript.
um
plugin
Voc tambm precisa falar qual vai ser o form que vai ser validado. Para isso voc pode adicionar um id ao
seu formulrio, e usar um seletor do JQuery para torn-lo validvel":
<form id="produtosForm" action="<c:url value="/produtos"/>" method="POST">
...
<script type="text/javascript">
$(#produtosForm).validate();
</script>
Assim voc pode adicionar as restries aos seus campos, lembrando que o nome do produto obrigatrio
e tem que ter o tamanho maior que 3, a descrio tambm obrigatria e o tamanho tem que ser menor que
40, e o preo maior que zero:
<form action="<c:url value="/produtos"/>" method="POST">
<fieldset>
<legend>Adicionar produtos</legend>
<label for="nome">Nome:</label>
<input id="nome" class="required" minlength="3"
Captulo 11 - AJAX e efeitos visuais - Validando formulrios com o JQuery - Pgina 88
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Se voc no gosta de colocar mais atributos nos seus campos de formulrio, voc ainda pode definir todas
as regras de validao de uma vez s, usando os names dos inputs, dentro da parte rules:
<script type="text/javascript">
$(#produtosForm).validate({
rules: {
"produto.nome": {
required: true,
minlength: 3
},
"produto.descricao": {
required: true,
maxlength: 40
},
"produto.preco": {
min: 0.0
}
}
});
</script>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Repare que o nosso dao ainda no possui o mtodo busca, mas podemos usar o eclipse para criar esse
mtodo pra ns. Para isso, coloque o cursor em cima do erro de compilao e aperte Ctrl+1. No menu que
apareceu selecione Create method busca(String) in type ProdutoDao.
Agora podemos modificar o mtodo criado no ProdutoDao para realmente buscar os produtos que contm
a string passada no nome. Para isso usaremos a API de Criteria do Hibernate:
@Component
public class ProdutoDao {
//...
public List<Produto> busca(String nome) {
return session.createCriteria(Produto.class)
.add(Restrictions.ilike("nome", nome, MatchMode.ANYWHERE))
.list();
}
}
Traduzindo essa chamada: crie uma Criteria de produtos, com a restrio de que o nome contenha a string
passada em qualquer lugar, ignorando maisculas e minsculas.
Agora precisamos criar o jsp que mostra os resultados da busca, em /WEB-INF/jsp/produto/busca.jsp.
Podemos aproveitar o jsp de listagem para mostrar a tabela:
<h3>Resultados da busca pelo nome <b>"${nome }"</b></h3>
<%@ include file="lista.jsp" %>
Precisamos tambm passar o nome que foi buscado, ento podemos mudar o mtodo busca do
ProdutosController:
public List<Produto> busca(String nome) {
result.include("nome", nome);
return dao.busca(nome);
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Por ltimo, vamos criar um formulrio para poder acessar essa busca. Abra o arquivo header.jspf e modifique o div menu:
<div id="menu">
<ul>
<li><a href="<c:url value="/produtos/novo"/>">Novo Produto</a></li>
<li><a href="<c:url value="/produtos"/>">Lista Produtos</a></li>
<li><form action="<c:url value="/produto/busca"/>">
<input name="nome"/>
</form>
</li>
</ul>
</div>
Para que no fique um input perdido no menu, sem que as pessoas saibam o que ele faz, podemos usar
um plugin do JQuery bem simples chamado Puts (http://github.com/cairesvs/Puts). Esse plugin coloca um
texto em cima do input, e quando a pessoa clica nele, o texto some. Assim podemos colocar o texto Busca de
produtos por nome no nosso input. Para usar esse plugin, precisamos baixar o javascript e import-lo na nossa
pgina, e ento usar o mtodo puts() para colocar nosso texto.
<script type="text/javascript" src="<c:url value="/javascripts/jquery.puts.js"/>"></script>
...
<li><form action="<c:url value="/produto/busca"/>">
<input id="busca" name="nome"/>
</form>
<script type="text/javascript">
$("#busca").puts("Busca produtos por nome");
</script>
</li>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Podemos ainda melhorar nossa busca, mostrando dicas de produtos j existentes enquanto o usurio est
digitando. Queremos um resultado parecido com este:
Ou seja, voc passa para o mtodo from() o que voc quer serializar em JSON. Pode ser um objeto
qualquer, mas no nosso caso vai ser uma lista. Ao chamarmos a URI desse mtodo no browser, passando um
nome qualquer, por exemplo http://localhost:8080/goodbuy/produtos/busca.json?nome=a retornado um JSON
parecido com:
{"list": [
{
"id": 1,
"nome": "Sabonete",
"descricao": "Um sabonete com perfume de lavanda",
"preco": "3.53"
},
{
"id": 3,
"nome": "Sapato",
"descricao": "um sapato de couro",
"preco": "132.00"
}
]}
Mas como vamos buscar os produtos apenas por nome, no precisamos de todas essas informaes, s
precisamos de uma lista de nomes, talvez com o preo. Para isso podemos excluir as outras propriedades:
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora estamos prontos para usar o plugin de autocomplete. Para isso, abra o arquivo header.jspf, e
adicione o javascript e o css do plugin. Use o mesmo lugar do plugin Puts:
<link href="<c:url value="/javascripts/jquery.autocomplete.css"/>"
rel="stylesheet" type="text/css" media="screen" />
<script type="text/javascript"
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
src="<c:url value="/javascripts/jquery.autocomplete.min.js"/>"></script>
...
<script type="text/javascript">
$("#busca").puts("Busca produtos por nome");
$("#busca").autocomplete(/goodbuy/produtos/busca.json);
</script>
Isso deveria ser o suficiente, mas o plugin AutoComplete espera que voc mande os dados separados por
pipe(|) ou um dado por linha. Como queremos usar JSON, precisamos configurar isso no plugin:
$("#busca").autocomplete(/goodbuy/produtos/busca.json, {
dataType: "json", // pra falar que vamos tratar um json
parse: function(produtos) { // para tratar o json
// a funo map vai iterar por toda a lista,
// e transformar os dados usando a funo passada
return $.map(produtos, function(produto) {
return {
data: produto, // todos os dados do produto
value: produto.nome, // o valor lgico do produto
result: produto.nome // o que vai aparecer ao selecionar
};
});
},
formatItem: function(produto) { // o que vai aparecer na lista de autocomplete
return produto.nome + "(" + produto.preco + ")";
}
});
Alm disso, o plugin passa como parmetro da requisio o que voc digitou no input, numa varivel chamada q ento voc precisa modicar a lgica de busca para o parmetro se chamar q:
@Get @Path("/produtos/busca.json")
public void buscaJson(String q) {
result.use(json()).withoutRoot()
.from(dao.busca(q))
.exclude("id", "descricao")
.serialize();
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
11.6 - Exerccios
1) Modifique os formulrios de adio e edio de produtos para incluir a validao do lado do cliente. Os javascripts do plugin j esto importados no projeto base. Se preferir use a outra forma de validao, passando
as opes para o mtodo validate().
<form id="produtosForm" action="<c:url value="/produtos"/>" method="POST">
<fieldset>
<legend>Adicionar produtos</legend>
<label for="nome">Nome:</label>
<input id="nome" class="required" minlength="3"
type="text" name="produto.nome" value="${produto.nome }"/>
<label for="descricao">Descrio:</label>
<textarea id="descricao" class="required" maxlength="40"
name="produto.descricao">${produto.descricao }</textarea>
<label for="preco">Preo:</label>
<input id="preco" min="0"
type="text" name="produto.preco" value="${produto.preco }"/>
<button type="submit">Enviar</button>
</fieldset>
</form>
<script type="text/javascript">
$(#produtosForm).validate();
</script>
3) (Opcional) As mensagens de erro esto em ingls. Tente procurar na documentao do plugin como fazer
para que as mensagens fiquem em portugus.
4) Crie um mtodo em ProdutosController para a listagem de produtos.
@Resource
Captulo 11 - AJAX e efeitos visuais - Exerccios - Pgina 95
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
10) Modifique o formulrio de busca para usar o plugin Puts e deixar uma mensagem dentro do input.
<div id="menu">
<ul>
<li><a href="<c:url value="/produtos/novo"/>">Novo Produto</a></li>
<li><a href="<c:url value="/produtos"/>">Lista Produtos</a></li>
<li><form action="<c:url value="/produto/busca"/>">
<input id="busca" name="nome"/>
</form>
<script type="text/javascript">
$("#busca").puts("Busca produtos por nome");
</script>
</li>
</ul>
</div>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
.from(dao.busca(q))
.exclude("id", "descricao")
.serialize();
}
C APTULO
12
Alm disso precisamos criar uma classe que representa um item do carrinho: um produto com a quantidade
selecionada.
public class Item {
private Produto produto;
private Integer quantidade;
// getters e setters
}
Pergunta importante: precisamos guardar esse carrinho no banco? O usurio no comprou ainda os produtos, ento no faz muito sentido guardar no banco de dados. Esse carrinho precisa ficar disponvel enquanto
o usurio estiver navegando no sistema, ento podemos apenas colocar o carrinho na sesso. Como vimos
antes, para que um componente seja nico durante a sesso do usurio basta anot-lo com @SessionScoped,
ento basta modificar a classe do carrinho para incluir as anotaes:
@Component
@SessionScoped
public class Carrinho {
private List<Item> itens = new ArrayList<Item>();
99
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora podemos receber esse carrinho no construtor de algum controller, com a certeza de que ele ser
nico durante a sesso do usurio.
O novo formulrio vai adicionar um item ao carrinho, passando a quantidade e o id do produto. Precisamos
agora criar um controlador que trate das operaes no carrinho de compras, que seja capaz de adicionar um
item ao carrinho. Esse controlador vai se chamar CarrinhoController:
Captulo 12 - Criando o Carrinho de Compras - Controlando o carrinho de compras - Pgina 100
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
package br.com.caelum.goodbuy.controller;
import br.com.caelum.vraptor.Resource;
@Resource
public class CarrinhoController {
}
Para adicionar o item ao carrinho, temos que receber um Carrinho no construtor, assim o VRaptor vai passar
para o CarrinhoController o carrinho da sesso do usurio:
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
public CarrinhoController(Carrinho carrinho) {
this.carrinho = carrinho;
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
carrinho.adiciona(item);
}
}
No existe ainda o mtodo adiciona no Carrinho, ento use o Ctrl+1 para criar o mtodo. Ao adicionar um
item no carrinho precisamos atualizar o total:
public class Carrinho {
private List<Item> itens = new ArrayList<Item>();
private Double total = 0.0;
public void adiciona(Item item) {
itens.add(item);
total += item.getProduto().getPreco() * item.getQuantidade();
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
//...
}
Repare que estamos usando o preo do produto para atualizar o total do carrinho, mas no formulrio s
estamos passando o id do produto. Precisamos carregar as outras informaes do Produto no banco de dados,
usando o ProdutoDao:
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
private final ProdutoDao dao;
public CarrinhoController(Carrinho carrinho, ProdutoDao dao) {
this.carrinho = carrinho;
this.dao = dao;
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
}
}
Para implementar o mtodo recarrega no ProdutoDao, vamos usar um mtodo da Session que busca as
informaes no banco e coloca no prprio objeto passado. Esse mtodo se chama refresh.
@Component
public class ProdutoDao {
//...
public void recarrega(Produto produto) {
session.refresh(produto);
}
}
Por ltimo, precisamos ir para alguma pgina aps adicionar algum item no carrinho. Por ora, vamos voltar
pra pgina de listagem de produtos. Repare que como o redirecionamento para uma lgica de outro controller,
precisamos usar a classe deste controller:
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
private final ProdutoDao dao;
private final Result result;
public CarrinhoController(Carrinho carrinho, ProdutoDao dao, Result result) {
this.carrinho = carrinho;
this.dao = dao;
this.result = result;
Captulo 12 - Criando o Carrinho de Compras - Controlando o carrinho de compras - Pgina 102
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(ProdutosController.class).lista();
}
}
Estamos usando a expresso ${carrinho.totalDeItens} mas classe Carrinho no possui um getter para
esse total de itens. Ento vamos adicionar:
public class Carrinho {
//...
public Integer getTotalDeItens() {
return itens.size();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Agora ao adicionar produtos ao carrinho, conseguimos ver a quantidade de produtos adicionados e o valor
total do carrinho nessa div que acabamos de criar:
Ainda no conseguimos visualizar os itens do carrinho, ento vamos criar uma lgica que liste os itens, no
CarrinhoController.
@Get @Path("/carrinho")
public void visualiza() {
}
Esse mtodo vai atender mesma URI que o mtodo adiciona, mas com outro verbo HTTP: o GET. Agora
podemos criar a pgina que lista os itens. Crie essa pgina em /WEB-INF/jsp/carrinho/visualiza.jsp.
<h3>Itens do seu carrinho de compras</h3>
<table>
<thead>
<tr>
<th>Nome</th>
<th>Descrio</th>
<th>Preo</th>
<th>Qtde</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<c:forEach items="${carrinho.itens}" var="item">
<tr>
<td>${item.produto.nome }</td>
<td>${item.produto.descricao }</td>
<td><fmt:formatNumber type="currency"
value="${item.produto.preco }"/></td>
<td>${item.quantidade }</td>
<td><fmt:formatNumber type="currency"
value="${item.quantidade * item.produto.preco }"/></td>
</tr>
</c:forEach>
</tbody>
<tfoot>
<tr>
Captulo 12 - Criando o Carrinho de Compras - Visualizando os itens do carrinho - Pgina 104
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<td colspan="2"></td>
<th colspan="2">Total:</th>
<th><fmt:formatNumber type="currency" value="${carrinho.total }"/></th>
</tr>
</tfoot>
</table>
Agora que temos uma pgina de visualizao, podemos redirecionar para ela quando adicionamos algum
produto ao carrinho.
@Resource
public class CarrinhoController {
//...
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(this).visualiza();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</c:forEach>
</tbody>
...
</table>
12.5 - Exerccios
1) Crie os modelos Carrinho e Item:
package br.com.caelum.goodbuy.modelo;
@Component
@SessionScoped
public class Carrinho {
private List<Item> itens = new ArrayList<Item>();
private Double total = 0.0;
//getters e setters
}
package br.com.caelum.goodbuy.modelo;
public class Item {
private Produto produto;
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</div>
@Resource
public class CarrinhoController {
private final Carrinho carrinho;
private final ProdutoDao dao;
private final Result result;
public CarrinhoController(Carrinho carrinho, ProdutoDao dao, Result result) {
this.carrinho = carrinho;
this.dao = dao;
this.result = result;
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(ProdutosController.class).lista();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
6) Crie a lgica e o jsp que visualiza os itens do carrinho. Mude tambm o redirecionamento do mtodo
adiciona para a nova lgica.
@Resource
public class CarrinhoController {
//...
@Get @Path("/carrinho")
public void visualiza() {
}
@Post @Path("/carrinho")
public void adiciona(Item item) {
dao.recarrega(item.getProduto());
carrinho.adiciona(item);
result.redirectTo(this).visualiza();
}
}
/WEB-INF/jsp/carrinho/visualiza.jsp
<h3>Itens do seu carrinho de compras</h3>
<table>
<thead>
<tr>
<th>Nome</th>
<th>Descrio</th>
<th>Preo</th>
<th>Qtde</th>
<th>Total</th>
</tr>
</thead>
<tbody>
<c:forEach items="${carrinho.itens}" var="item" varStatus="s">
<tr>
<td>${item.produto.nome }</td>
<td>${item.produto.descricao }</td>
<td><fmt:formatNumber type="currency" value="${item.produto.preco }"/></td>
<td>${item.quantidade }</td>
<td><fmt:formatNumber type="currency"
value="${item.quantidade * item.produto.preco }"/></td>
</tr>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</c:forEach>
</tbody>
<tfoot>
<tr>
<td colspan="2"></td>
<th colspan="2">Total:</th>
<th><fmt:formatNumber type="currency" value="${carrinho.total }"/></th>
</tr>
</tfoot>
</table>
8) Crie a lgica de remover produtos do carrinho e adicione o boto de remoo na visualizao do carrinho.
public class CarrinhoController {
//...
@Delete @Path("/carrinho/{indiceItem}")
public void remove(int indiceItem) {
carrinho.remove(indiceItem);
result.redirectTo(this).visualiza();
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
</c:forEach>
</tbody>
...
</table>
C APTULO
13
Autenticao
13.1 - Criando Usurios
No nosso sistema, atualmente, qualquer um pode adicionar, editar ou remover produtos. Ser que isso o
desejvel? No seria melhor apenas habilitar essas funcionalidades para os administradores do sistema?
Ento vamos criar um sistema de login para a nossa aplicao, comeando pelo modelo de usurios:
package br.com.caelum.goodbuy.modelo;
public class Usuario {
private String login;
private String senha;
private String nome;
//getters e setters
}
uma boa idia guardar os usurios no banco de dados, ento vamos adicionar as anotaes do hibernate.
No h a necessidade de criar um campo id para usurios, pois o login j ser um identificador nico.
package br.com.caelum.goodbuy.modelo;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Usuario {
@Id
private String login;
private String senha;
private String nome;
//getters e setters
}
Como estamos adicionando uma entidade nova, precisamos coloc-la no hibernate.cfg.xml. Ainda vamos
adicionar a propriedade hibernate.hbm2ddl.auto com o valor update, assim o hibernate far as mudanas nas
112
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Com o modelo pronto j possvel criar as lgicas de cadastro e de login de usurios. Vamos comear pelo
cadastro, criando o controlador de usurios com uma lgica para mostrar o formulrio:
package br.com.caelum.goodbuy.controller;
import br.com.caelum.vraptor.Resource;
@Resource
public class UsuariosController {
public void novo() {
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
<script type="text/javascript">
$(#usuariosForm).validate();
</script>
Precisamos tambm criar um link para esse cadastro. Para isso criaremos uma div no cabealho que
mostrar as informaes do usurio. Abra o arquivo header.jspf e modifique a div header:
<div id="header">
<div id="usuario">
Voc no est logado. <a href="<c:url value="/usuarios/novo"/>">Cadastre-se</a>
</div>
...
</div>
Para completar o cadastro, vamos criar a lgica que adiciona o usurio de fato, validando se o login escolhido
ainda no existe no sistema:
@Resource
public class UsuariosController {
private final UsuarioDao dao;
private final Result result;
private final Validator validator;
public UsuariosController(UsuarioDao dao, Result result, Validator validator) {
this.dao = dao;
this.result = result;
this.validator = validator;
}
@Post @Path("/usuarios")
public void adiciona(Usuario usuario) {
if (dao.existeUsuario(usuario)) {
validator.add(new ValidationMessage("Login j existe", "usuario.login"));
}
validator.onErrorUsePageOf(UsuariosController.class).novo();
dao.adiciona(usuario);
result.redirectTo(ProdutosController.class).lista();
}
//...
}
O UsuarioDao ainda no existe. Use o Ctrl+1 para criar o dao e os seus mtodos:
@Component
public class UsuarioDao {
private final Session session;
public UsuarioDao(Session session) {
this.session = session;
}
public boolean existeUsuario(Usuario usuario) {
Captulo 13 - Autenticao - Criando Usurios - Pgina 114
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Quando o usurio faz o login, precisamos guardar a informao de que ele j est logado. A melhor forma
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
guardar a informao de login na sesso, que mantm os dados enquanto o usurio estiver navegando pela
aplicao. Para isso, vamos criar uma classe que guarda o usurio logado. Essa classe ser acessada nos
jsps para acessar as informaes do usurio, ento adicionamos alguns getters para expor as informaes
relevantes.
@Component
@SessionScoped
public class UsuarioWeb {
private Usuario logado;
public void login(Usuario usuario) {
this.logado = usuario;
}
public String getNome() {
return logado.getNome();
}
public boolean isLogado() {
return logado != null;
}
}
Ento na nossa lgica de login, podemos colocar o usurio logado dentro da classe acima, depois de
verificar que o usurio digitou o login e senha certos.
@Resource
public class UsuariosController {
private final UsuarioWeb usuarioWeb;
//...
public UsuariosController(UsuarioDao dao, Result result, Validator validator,
UsuarioWeb usuarioWeb) {
//...
this.usuarioWeb = usuarioWeb;
}
@Post @Path("/login")
public void login(Usuario usuario) {
Usuario carregado = dao.carrega(usuario);
if (carregado == null) {
validator.add(new ValidationMessage("Login e/ou senha invlidos", "usuario.login"));
}
validator.onErrorUsePageOf(UsuariosController.class).loginForm();
usuarioWeb.login(carregado);
result.redirectTo(ProdutosController.class).lista();
}
}
Para carregar o usurio, crie o mtodo no UsuarioDao que busca um usurio por login e senha:
Captulo 13 - Autenticao - Efetuando o login - Pgina 116
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Component
public class UsuarioDao {
//...
public Usuario carrega(Usuario usuario) {
return (Usuario) session.createCriteria(Usuario.class)
.add(Restrictions.eq("login", usuario.getLogin()))
.add(Restrictions.eq("senha", usuario.getSenha()))
.uniqueResult();
}
}
E para mostrar que o usurio est logado mesmo, vamos modificar o cabealho:
<div id="header">
<div id="usuario">
<c:if test="${usuarioWeb.logado}">
Ol, ${usuarioWeb.nome }! <a href="<c:url value="/logout"/>">Logout</a>
</c:if>
<c:if test="${empty usuarioWeb or not usuarioWeb.logado}">
Voc no est logado. <a href="<c:url value="/login"/>">Login</a>
<a href="<c:url value="/usuarios/novo"/>">Cadastre-se</a>
</c:if>
</div>
...
</div>
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Como falamos no comeo do captulo, no legal que todo mundo consiga adicionar, remover e editar
produtos. Vamos, ento, fazer com que s usurios logados consigam acessar essas funcionalidades. Um
primeiro passo retirar os links para elas quando o usurio no est logado:
/header.jspf:
<div id="menu">
<ul>
<c:if test="${usuarioWeb.logado }">
<li><a href="<c:url value="/produtos/novo"/>">Novo Produto</a></li>
</c:if>
...
/WEB-INF/jsp/produto/lista.jsp
<table>
...
<tbody>
<c:forEach items="${produtoList}" var="produto">
<tr>
...
<c:if test="${usuarioWeb.logado }">
<td><a href="<c:url value="/produtos/${produto.id}"/>">Editar</a></td>
<td>
<form action="<c:url value="/produtos/${produto.id}"/>" method="POST">
<button class="link" name="_method" value="DELETE">Remover</button>
</form>
</td>
</c:if>
</tr>
</c:forEach>
</tbody>
</table>
Agora os usurios que no esto logados no conseguem mais ver os links das aes que ele no pode
executar. Mas ser que isso o suficiente? O que impede o usurio de digitar na barra de endereos do browser:
http://localhost:8080/goodbuy/produtos/novo? Nada! Ele s precisa conhecer qual a URI. Precisamos, de
algum jeito, impedir que os usurios acessem certas URIs se eles no estiverem logados, monitorar todas as
aes do usurio para que quando ele acessar uma URI proibida, redirecionar para uma pgina de erro, ou
melhor, para o login.
13.4 - Interceptor
Um Interceptor no VRaptor como se fosse um Servlet Filter: ele pode interceptar requisies, executando
algo antes e/ou depois da sua lgica. Mais ainda, ele pode impedir que sua lgica seja executada, redirecionando a requisio para outro lugar. Isso exatamente o que a gente queria: verificar se o usurio est logado
antes de ir pro controller, e se ele no estiver, redirecionar para o login.
Para implementar um Interceptor do VRaptor precisamos de duas coisas: anotar a classe com @Intercepts
e implementar a interface Interceptor:
@Intercepts
Captulo 13 - Autenticao - Interceptor - Pgina 118
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Como qualquer classe registrada no VRaptor, voc pode receber qualquer componente da sua aplicao (e
do VRaptor) pelo construtor do seu interceptor. No nosso caso precisamos verificar se o usurio est logado, e
essa informao est no UsuarioWeb.
@Intercepts
public class AutorizacaoInterceptor implements Interceptor {
private final UsuarioWeb usuario;
public AutorizacaoInterceptor(UsuarioWeb usuario) {
this.usuario = usuario;
}
}
Mas s precisamos executar a lgica de autenticao caso o usurio no esteja logado, ento vamos implementar o mtodo accepts:
public boolean accepts(ResourceMethod method) {
return !this.usuario.isLogado();
}
Para o mtodo intercepts, sabemos j que o usurio no est logado, ento vamos redirecionar para a lgica
de login. Para isso precisamos do Result.
@Intercepts
public class AutorizacaoInterceptor implements Interceptor {
private final UsuarioWeb usuario;
private final Result result;
public AutorizacaoInterceptor(UsuarioWeb usuario, Result result) {
this.usuario = usuario;
Captulo 13 - Autenticao - Interceptor - Pgina 119
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
this.result = result;
}
public boolean accepts(ResourceMethod method) {
return false;
}
public void intercept(InterceptorStack stack, ResourceMethod method,
Object resourceInstance) throws InterceptionException {
result.redirectTo(UsuariosController.class).loginForm();
}
}
Mas temos um pequeno problema: se o usurio no estiver logado ele no vai conseguir acessar nada no
sistema, nem o login! Na verdade, s queremos proibir que o usurio adicione e modifique produtos, ento
nosso interceptor s pode executar para essas operaes.
Poderamos at colocar no Interceptor uma lista dos mtodos que sero interceptados, mas assim toda vez
que adicionarmos uma operao nova que precise de autenticao precisaramos mudar o interceptor.
Um jeito mais legal de fazer isso marcar os mtodos que precisam de autenticao. Para isso podemos
criar uma anotao para ser colocada nos mtodos:
@Retention(RetentionPolicy.RUNTIME) //a anotao vai ficar disponvel em tempo de execucao
@Target(ElementType.METHOD) // anotao para mtodos
public @interface Restrito {
}
Assim, podemos anotar os mtodos restritos (adiciona, atualiza, remove, formulario e edita do ProdutosController):
@Resource
public class ProdutosController {
@Restrito
public void formulario() {}
@Restrito
public Produto edita(Long id) {...}
@Restrito
public void altera(Produto produto) {...}
@Restrito
public void adiciona(final Produto produto) {...}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
@Restrito
public void remove(Long id) {...}
...
}
Pronto. Nosso sistema de autenticao est pronto. Poderamos trocar a anotao @Restrito por uma, por
exemplo, @Liberado caso tenha bem mais operaes liberadas do que restritas, depende do seu sistema.
C APTULO
14
14.1 - Exerccios
1) Modifique a pgina de edio de Produtos (/WEB-INF/jsp/produtos/edita.jsp), para incluir um formulrio
de Upload de imagem. Esse formulrio vai submeter para uma lgica nova que vamos criar a seguir.
<!--...-->
<form action="<c:url value="/produtos/${produto.id }/imagem"/>" method="POST"
enctype="multipart/form-data">
<fieldset>
<legend>Upload de Imagem</legend>
<input type="file" name="imagem" />
<button type="submit">Enviar</button>
</fieldset>
</form>
2) Crie o Controller abaixo, que vai tratar dos uploads e downloads de imagens.
package br.com.caelum.goodbuy.controller;
import br.com.caelum.vraptor.interceptor.multipart.UploadedFile;
@Resource
public class ImagensController {
@Post @Path("/produtos/{produto.id}/imagem")
public void upload(Produto produto, UploadedFile imagem) {
}
}
3) S estamos interessados em fazer uploads de imagens. Ento vamos validar se o upload foi uma imagem
mesmo. O ContentType de imagens comea com image:
@Post @Path("/produtos/{produto.id}/imagem")
public void upload(Produto produto, final UploadedFile imagem) {
122
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
validator.checking(new Validations() {{
if (that(imagem, is(notNullValue()), "imagem", "imagem.nula")) {
that(imagem.getContentType(), startsWith("image"), "imagem", "nao.eh.imagem");
}
}});
validator.onErrorRedirectTo(ProdutosController.class).edita(produto.getId());
}
5) Crie o componente do VRaptor que ser responsvel por guardar as imagens no servidor. Esse componente
vai criar uma pasta fixa onde todas as imagens sero guardadas. Para isso, precisamos conseguir o
caminho de uma pasta do servidor, e a classe que consegue nos dar essa informao o ServletContext.
package br.com.caelum.goodbuy.imagens;
import java.io.File;
import javax.servlet.ServletContext;
import br.com.caelum.vraptor.ioc.Component;
@Component
public class Imagens {
private File pastaImagens;
public Imagens(ServletContext context) {
String caminhoImagens = context.getRealPath("/WEB-INF/imagens");
pastaImagens = new File(caminhoImagens);
pastaImagens.mkdir();
}
}
6) Crie um mtodo que salve a imagem do upload na pasta padro. Colocaremos uma extenso fixa para
facilitar. O IOUtils vem do projeto commons-io e copia um InputStream para algum OutputStream
package br.com.caelum.goodbuy.imagens;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
@Component
public class Imagens {
//...
public void salva(UploadedFile imagem, Produto produto) {
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
7) Use a classe Imagens para salvar a imagem que veio no upload. E a classe Result para voltar para o
formulario de edio.
package br.com.caelum.goodbuy.controller;
@Resource
public class ImagensController {
private final Validator validator;
private final Imagens imagens;
private final Result result;
public ImagensController(Validator validator, Imagens imagens, Result result) {
this.validator = validator;
this.imagens = imagens;
this.result = result;
}
@Post @Path("/produtos/{produto.id}/imagem")
public void upload(Produto produto, final UploadedFile imagem) {
validator.checking(new Validations() {{
if (that(imagem, is(notNullValue()), "imagem", "imagem.nula")) {
that(imagem.getContentType(), startsWith("image"), "imagem", "nao.eh.imagem");
}
}});
validator.onErrorRedirectTo(ProdutosController.class).edita(produto.getId());
imagens.salva(imagem, produto);
result.redirectTo(ProdutosController.class).edita(produto.getId());
}
}
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
10) Mude o formulrio de edio para incluir a imagem do produto. Repare que estamos usando nosso controller
para mostrar a imagem. Coloque essa imagem na listagem de produtos tambm.
<img src="<c:url value="/produtos/${produto.id}/imagem"/>" width="100" height="100"/>
C APTULO
15
126
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
3) Esse componente de transaes genrico: funciona tanto com a JTA direto, com Hibernate/JPA, com JDBC,
etc. Ento para especificar qual dos gerenciadores de transao vai ser utilizado, voc precisa adicionar um
bean na configurao. No nosso caso, vamos usar transaes do Hibernate:
<beans xmlns...>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
4) O transactionManager precisa de uma SessionFactory configurada. J temos uma no VRaptor, mas ela no
poder ser usada. Remova a anotao @Component do CriadorDeSessionFactory, e adicione o bean que cria
uma SessionFactory no Spring:
<beans xmlns....>
<!--...-->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation">
<value>classpath:/hibernate.cfg.xml</value>
</property>
</bean>
</beans>
5) Ao usar a SessionFactory do Spring estamos presos ao seu controle de Sessions. Isso significa que no
podemos fazer o sessionFactory.openSession para obter Sessions. O Spring controla isso para voc, ento
voc obrigado a usar componentes do Spring para conseguir uma Session, para poder usar a mesma que
est participando da transao. Mas ao invs de mudar todo o nosso sistema para usar as sessions do
Spring, vamos modificar o nosso CriadorDeSession para usar-las.
Por causa do jeito que o Spring trabalha, temos que pedir uma Session para ele no momento que vamos
us-la, e no na criao do DAO, por exemplo. Ento vamos usar um Proxy Dinmico para poder passar
Captulo 15 - Apndice - Integrando VRaptor e Spring - Exerccios: Transaction Manager - Pgina 127
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
uma Session no construtor do DAO, mas mesmo assim s pedir a Session pro Spring na hora de chamar
algum mtodo.
Modifique o CriadorDeSession.
import net.vidageek.mirror.dsl.Mirror;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import br.com.caelum.vraptor.proxy.MethodInvocation;
import br.com.caelum.vraptor.proxy.Proxifier;
import br.com.caelum.vraptor.proxy.SuperMethod;
@Component
public class CriadorDeSession implements ComponentFactory<Session> {
private final SessionFactory factory;
private final Proxifier proxifier;
private Session session;
public CriadorDeSession(SessionFactory factory, Proxifier proxifier) {
this.factory = factory;
this.proxifier = proxifier;
}
@PostConstruct
public void abre() {
this.session = proxifier.proxify(Session.class, new MethodInvocation<Session>() {
public Object intercept(Session proxy, Method method, Object[] args,
SuperMethod superMethod) {
Session sessionDoSpring = SessionFactoryUtils.doGetSession(factory, true);
return new Mirror().on(sessionDoSpring).invoke().method(method).withArgs(args);
}
});
}
public Session getInstance() {
return this.session;
}
@PreDestroy
public void fecha() {
this.session.close();
}
}
6) Para usar o controle de transaes do Spring, remova a abertura e o fechamento de transaes dos
mtodos do DAO, e anote o mtodo com @Transactional. Repare que nem todos os mtodos precisam de
transaes, s os que modificam o banco.
import org.springframework.transaction.annotation.Transactional;
@Component
public class ProdutoDao {
@Transactional
Captulo 15 - Apndice - Integrando VRaptor e Spring - Exerccios: Transaction Manager - Pgina 128
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
Captulo 15 - Apndice - Integrando VRaptor e Spring - Exerccios: Transaction Manager - Pgina 129
C APTULO
16
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
import javax.servlet.http.HttpServletRequest;
import br.com.caelum.vraptor.view.AcceptHeaderToFormat;
import br.com.caelum.vraptor.view.DefaultPathResolver;
@Component
public class VelocityPathResolver extends DefaultPathResolver {
Captulo 16 - Apndice: Mudando a View Padro: Velocity - Exerccios: Mudando o resultado de todas as lgicas para Velocity Pgina 131
Material do Treinamento Desenv. gil para Web 2.0 com VRaptor, Hibernate e AJAX
2) Crie a pasta /WEB-INF/velocity, e dentro dela a pasta teste. Mova o arquivo olamundo.vm para a pasta
teste e o renomeie para teste.vm.
3) Remova o redirecionamento para a pgina olamundo.vm de dentro do TesteController:
public class TesteController {
private Result result;
public TesteController(Result result) {
this.result = result;
}
@Path("/teste")
public void teste() {
result.include("mensagem", "Estou usando o Velocity");
}
}
4) Acesse a url do mtodo teste (http://localhost:8080/goodbuy/teste), e veja que continua funcionando: ele
vai redirecionar automaticamente para a pgina /WEB-INF/velocity/teste/teste.vm.
5) Veja que agora nenhuma das outras pginas do sistema esto acessveis. Agora qualquer chamada vai dar
404, pois ele estar usando a nova conveno. O comportamento do VRaptor agora o da sua classe, e
no mais a padro dele!
Captulo 16 - Apndice: Mudando a View Padro: Velocity - Exerccios: Mudando o resultado de todas as lgicas para Velocity Pgina 132
ndice Remissivo
AnnotationConfiguration, 11
Criteria, 40
Driver, 5
Escopo, 67
Hibernate, 4
Hibernate Annotations, 5
Hibernate Core, 5
Injeo de Dependncias, 43
JPA, 4
Log4J, 5
Logging, 5
MySQL, 4
ORM, 4
POJO, 32
Result, 52
SL4J, 5
Validao, 72
133