Made in Brazil coluna VRaptor 3: guerrilha web Como esse framework web ataca diretamente os pequenos problemas do dia-a-dia. Lucas Cavalcanti ([email protected]): é formando em Ciência da Computação pelo IME-USP, é desenvolvedor e consultor pela Caelum há 3 anos, além de contribuir com projetos como o VRaptor3, Calopsita e SeleniumDSL. O VRaptor é um framework que há 5 anos vem ganhando bastante espaço no mercado brasileiro. Nascido no IMEUSP e hoje com o apoio do Centro de Competência de Software livre da USP, a versão 3.0 traz novidades e simplificações voltadas a Rest, Injeção de Dependências, AJAX, integração com outros frameworks e testabilidade. Adriano Almeida ([email protected]): é formado em Sistemas de Informação pela Faculdade de Informática e Administração Paulista, e atua como instrutor, consultor e desenvolvedor pela Caelum. Participa também de projetos opensource, como o VRaptor2, VRaptor 3 e MirrorDSL. S ão duas as formas principais de se trabalhar para a web: com frameworks baseados em componentes visuais (como JSF e Tapestry) e frameworks baseados em ações (como Struts, Webwork e Rails). O VRaptor 3 é um framework MVC para a Web baseado em ações, com forte inspiração no Rails, Webwork e Waffle. Nascido no departamento de computação do IME-USP, essa terceira versão do framework conta com o apoio do Centro de Competência em Software Livre da USP e já é usado por diversas empresas, como você pode conferir nos depoimentos do site. Neste artigo, conheceremos diversos recursos do framework, mas é interessante salientar que com apenas a injeção de dependên- 6 www.mundoj.com.br cias, parâmetros e validação já é possível fazer 90% do trabalho do dia-a-dia, e com um código limpo, desacoplado e fácil de testar. Começando O VRaptor 3 necessita de alguns jars como dependências. Para facilitar o processo, no site www.vraptor.com.br está disponível para download um projeto vazio (blank-project), que consiste em um zip com a estrutura básica de um projeto web no Eclipse, contendo todas suas dependências. Também vem com a configuração mínima do web.xml, facilitando o início do uso do framework. Você pode descompactá-lo e importá-lo como projeto do Eclipse. .BEFJO#SB[JMt73BQUPSHVFSSJMIBXFC Resources Para trabalharmos com o VRaptor 3, precisamos criar as classes que contêm a lógica de negócio da nossa aplicação, e que saibam modificar nossas entidades e depois redirecionar o resultado para as páginas adequadas. Essas classes farão papel de Controllers da nossa aplicação. Para isso, basta criarmos uma classe simples, que esteja anotada com @ Resource. Isso identifica que esta classe será um recurso que pode ser acessado via web. Essa classe deve ter os métodos que serão invocados no momento em que determinada URI for acessada pelo navegador. Listagem 3. Formulário para inclusão de aluno, apontando para /alunos/ adiciona. <form action=”/alunos/adiciona”> <input type=”text” name=”novoAluno.nome”/> <input type=”submit” value=”enviar”/> </form> Para receber esses dados em nossa classe, basta criarmos um método adiciona em nosso controlador, que receba um parâmetro chamado novoAluno, e então o VRaptor popula o objeto fazendo as devidas conversões de tipo. Veja a Listagem 4. Para este artigo, vamos criar uma aplicação de exemplo que controla os alunos de uma escola. Listagem 4. Controller de alunos com método para adicionar o aluno novo! Considerando uma simples entidade Aluno com id e nome, vamos então criar uma lógica que lista todos os alunos do nosso banco de dados e renderizá-los em uma view (jsp). @Resource public class AlunosController { public void adiciona(Aluno novoAluno) { // futuramente usaremos o DAO System.out.println(“gravando aluno” + novoAluno.getNome()); } Listagem 1. Controller de alunos com método para listar todos os alunos. @Resource public class AlunosController { public List<Aluno> lista() { // vamos trocar por um dao futuramente: List<Aluno> alunos = new ArrayList<Aluno>(); alunos.add(new Aluno(1, “Lucas”)); alunos.add(new Aluno(2, “Adriano”)); return alunos; } public List<Aluno> lista() { // ... } } Relembrando, após a adição desse usuário, o VRaptor por padrão faz o dispatch para /WEB-INF/jsp/alunos/adiciona.jsp. } Note que a convenção do VRaptor 3 é bastante intuitiva: para chegar ao método lista() do AlunosController, basta chamar a URI /alunos/ lista. Após executar o método, o redirecionamento padrão será para a página em /WEB-INF/jsp/alunos/lista.jsp – ou seja, <nome_do_ controller>/<nome_do_metodo> para a URI e /WEB-INF/jsp/<nome_ do_controller>/<nome_do_metodo>.jsp para a view. O que devemos ter no lista.jsp? Precisamos iterar sobre a lista retornada pelo método. Aqui o VRaptor possui novamente uma convenção: se você retornou uma lista de alunos, a variável disponível no JSP será alunoList. Ou seja, o <nome_da_classe>List. Se fosse retornado apenas uma referência a Aluno, a variável seria apenas aluno. O JSP então ficaria como na Listagem 2. Listagem 2. Listando os alunos no jsp à variável alunoList, que contém o retorno do método lista(). <ul> <c:forEach items=”${alunoList}” var=”aluno”> <li>${aluno.nome}</li> </c:forEach> </ul> Recebendo parâmetros e populando entidades Precisamos também cadastrar alunos. Para isso, temos um JSP como o da Listagem 3, como o usual. Por último um truque para não ter de acessar diretamente o form.jsp, aproveitando a criação de URIs simples, podemos criar um método apenas com esse intuito, sem corpo: public void form() { } Com isso, podemos acessar a URI /alunos/form e o jsp com o formulário em branco será apresentado! Componentes e injeção Para tornar nosso exemplo mais real, queremos utilizar um DAO para inserir e listar nossos alunos. De onde viria esse DAO? Como o VRaptor usa injeção de dependências, podemos recebê-lo no construtor. E, para que o VRaptor saiba qual AlunoDao colocar no construtor, basta anotar a implementação com @Component, como mostra a Listagem 5. O VRaptor utiliza por padrão o Spring como container de IoC, então você pode usar todos os recursos do Spring, como suas anotações e integrações com outros frameworks, configurados no seu application-context.xml. Há diversos objetos que já estão registrados para injeção. Por exemplo, se você precisar de algo muito específico de HttpSession, HttpServletRequest, HttpServletResponse, basta declarar essa necessidade em seu construtor! 7 .BEFJO#SB[JMt73BQUPSHVFSSJMIBXFC Listagem 5. AlunoDao e injeção pelo construtor. Listagem 6. Usando o Result para redirecionar para outra lógica. @Component public class AlunoDao { // métodos } @Resource public class AlunosController { private AlunoDao dao; private Result result; @Resource public class AlunosController { public AlunosController(AlunoDao dao, Result result) { this.dao = dao; this.result = result; } private AlunoDao dao; public AlunosController(AlunoDao dao) { this.dao = dao; } public void lista() { // código para listar os alunos } public void form() { } public void adiciona(Aluno aluno) { dao.save(aluno); } public List<Aluno> lista() { return dao.listAll(); } } Com os poucos exemplos que vimos até aqui, já é possível construir grande parte da sua aplicação web, que gira em torno de receber parâmetros, trabalhar com dependências, e renderizar o resultado nas páginas desejadas. Caso você já tenha trabalhado com VRaptor2, é importante ter em mente que o @Component do VRaptor3 não tem a mesma semântica que o do VRaptor2. No VRaptor3, ele simplesmente indica que um objeto da classe anotada poderá ser injetado como dependência para outro componente ou recurso. public void adiciona(Aluno aluno) { dao.save(aluno); result.use(Results.logic()).redirectTo(AlunosController.class).lista(); } } Disponibilizando mais dados para a view Vimos como disponibilizar para o JSP o retorno do método. Mas como fazer se precisamos passar mais de um objeto para a view? Para isso, usamos novamente o Result, que possui o método include para incluir atributos na requisição, podendo assim usá-los na renderização da página. Um exemplo de código está na Listagem 7 Listagem 7. Disponibilizando a lista dos alunos para a view através da variável ${listaDeAlunos}. public void lista() { List<Aluno> alunos = dao.listAll(); // podemos incluir vários objetos como atributo da requisição result.include(“listaDeAlunos”, alunos); Mudando o redirecionando após a execução da lógica Adicionando validação nas lógicas Após salvar um aluno, geralmente queremos redirecionar o usuário para a listagem de alunos, ou seja, precisamos executar a lógica de listagem que está no método lista() do AlunosController. É a interface Result que disponibiliza dados do nosso Controller para um JSP e é responsável por redirecionar o usuário para lógicas ou views diferentes. Para obter uma instância de Result, basta recebê-la no construtor do AlunosController, e o VRaptor fará a mágica com a injeção de dependências, como mostra a Listagem 6. A validação das informações submetidas pelo usuário é uma das questões mais delicadas em uma aplicação web. As validações podem ser feitas no lado do cliente, através de javascript, ou no lado do servidor, utilizando recursos da própria linguagem e do framework em uso. A validação no VRaptor3 é feita através da interface Validator, que fornece duas formas de fazer as validações: a clássica e a fluente. Note que usamos o result que foi injetado pelo VRaptor e pedimos para usar uma lógica, redirecionando para o método lista do AlunosController. Na forma clássica, você faz as validações dos dados verificando se os valores seguem as suas regras ou não, e caso não sigam, você adiciona as mensagens de erro. Ou seja, se o nome do aluno é obrigatório, basta checar isso como feito na Listagem 8. Poderíamos, também, em vez de redirecionarmos para uma lógica, enviar a requisição para uma página jsp diferente do convencionado, usando o método forward: result.use(Results.page()).forward("/WEB-INF/jsp/alunos/adicionou.jsp"); 8 } www.mundoj.com.br Você precisa falar para o VRaptor onde ele deve ir caso ocorra algum erro de validação. Isso é feito da mesma forma que com o Result, mas usando o método do validator: onErrorUse. Ao executar esse método, se algum erro de validação ocorrer, a página indicada vai ser renderizada com as mensagens de erro disponíveis na variável errors, e você pode visuailizálas como na Listagem 9. .BEFJO#SB[JMt73BQUPSHVFSSJMIBXFC Listagem 8. Validando a obrigatoriedade do nome na forma clássica. public class AlunosController { private AlunoDao dao; private Result result; private Validator validator; public AlunosController(AlunoDao dao, Result result, Validator validator) { //atribui dao e result this.validator = validator; } public void adiciona(Aluno aluno) { if (aluno.getNome().isEmpty()) { No nosso caso, como trabalhamos com o recurso Aluno, gostaríamos de ter as seguintes URIs: GET - /alunos – Listagem dos alunos GET - /alunos/1 – Visualizar o aluno com id 1 POST - /alunos – Adicionar novo aluno PUT - /alunos/2 – Atualizar os dados do aluno com id 2 DELETE - /alunos/1 – Remover o aluno com id 1 Note que as URIs se repetem, mudando apenas o método HTTP utilizado. O VRaptor3 fornece uma forma de se trabalhar utilizando os seus recursos com URIs no estilo REST. Para isso, basta utilizar as anotações @Path em conjunto com as anotações @Get, @Post, @Delete ou @Put nos métodos do seu controller, como na seguinte forma. Portanto, para termos um controller que suporte as operações básicas em alunos (CRUD), teríamos um controller de acordo com a Listagem 10. validator.add(new ValidationMessage(“erro”, “nomeObrigatorio”); } Listagem 10. Utilizando os métodos HTTP nos seus recursos. validator.onErrorUse(Results.page()).of(AlunosController.class).form(); dao.save(aluno); @Resource public class AlunosController { result.use(Results.logic()).redirectTo(AlunosController.class).lista(); @Path(“/alunos”) @Post public void adiciona(Aluno aluno) { //código para adicionar um aluno } } } @Path(“/alunos”) @Get public List<Aluno> lista() { //código para listar os alunos } Listagem 9. Mostrando os erros numa jsp. <ul> <c:forEach items=”${errors}” var=”erro”> <li>${erro.category} - ${erro.message}</li> </c:forEach> </ul> @Path(“/alunos/{aluno.id}”) @Delete public void remove(Aluno aluno) { //código para remover um aluno } Na forma fluente, a sintaxe é um pouco diferente da forma clássica, no entanto, ela deixa o código de validação mais legível, como abaixo: @Path(“/alunos/{aluno.id}”) @Put public void altera(Aluno aluno) { //código para alterar um aluno } validator.checking(new Validations() {{ if (that(aluno.getNome() != null, “erro”, “nomeObrigatorio”)) { @Path(“/alunos/{aluno.id}”) @Get public Aluno visualiza(Aluno aluno) { //código para visualizar um aluno } //só entra se a validação passou that(!aluno.getNome().isEmpty(), “erro”, “nomeObrigatorio”); } } }}); validator.onErrorUse(Results.page()).of(AlunosController.class).form(); A vantagem da forma fluente em relação à forma clássica é a forma de ler o código da validação. Você pode ler o código acima como: Valide que o nome do aluno não é uma string vazia. Você ainda pode usar a API do projeto Hamcrest para criar validações mais complexas e legíveis. Trabalhando com REST Repare que você pode passar parâmetros nas URIs do @Path, e os valores passados serão populados e passados como argumento. Ou seja, se eu chamar a URI /alunos/5 via GET, o aluno passado para o método visualiza vai ter o id igual a 5. Lembre-se que alguns browsers ainda não suportam os métodos PUT e DELETE, então você deve usar javascript para mudar o método da requisição, ou passar o parâmetro _method com o valor do método desejado, como na Listagem 11. Listagem 11. Mudando o método da requisição. Uma das funcionalidades mais interessantes do VRaptor3 é o fato de suportar os diversos métodos HTTP, além dos tradicionais GET e POST. O uso desses outros métodos é uma prática adotada em aplicações que seguem o estilo REST, em que as URIs representam recursos e as operações executadas sobre esses recursos são diferenciadas através do método HTTP da requisição. <form action=”/alunos/1”><!-- form de alteração --> <input type=”hidden” name=”_method” value=”PUT”/> ... </form> 9 .BEFJO#SB[JMt73BQUPSHVFSSJMIBXFC Outra opção para definir as rotas seria centralizar as configurações das URIs das lógicas numa classe que implementa a interface RoutesConfiguration, sendo possível especificar rotas como na Listagem 12. A ideia é dizer: a rota para a URI /alunos com o método GET é a classe AlunosController método lista(): routeFor("/alunos").with(HttpMethod.GET). is(AlunosController.class).lista(); Listagem 14. Resultado gerado em JSON. { “nome”: “Adriano Almeida”, “endereco”: “Rua dos Pássaros, 999” , “email”: “[email protected]” }, { Listagem 12. Centralizando a configuração das URIs. “nome”:”Lucas Cavalcanti”, “endereco”: “Rua dos Peixes, 777” , “email”: “[email protected]” @Component public class CustomRoutes implements RoutesConfiguration { public void config(Router router) { new Rules(router) { public void routes() { routeFor(“/alunos”).with(HttpMethod.GET). is(AlunosController.class).lista(); routeFor(“/alunos/{aluno.id}”).with(HttpMethod.DELETE). is(AlunosController.class).delete(null); } } } } Mudando o formato da view e ajax Nem sempre queremos que o formato de saída da nossa lógica seja html. Para usar views de outros formatos podemos usar o header Accepts da requisição, ou o parâmetro _format. Por exemplo, se chamarmos: / alunos?_format=json, o VRaptor vai devolver a view /WEB-INF/jsp/alunos/lista.json.jsp, e dentro desse jsp você pode gerar o seu JSON com os atributos da requisição. Em geral vai ser usada a view: WEB-INF/jsp/ {controlador}/{logica}.{formato}.jsp Logo, para gerar o JSON para uma lista de alunos, podemos fazer um código parecido com o da Listagem 13, que tem como saída a Listagem 14. Para facilitar, você pode também poderia utilizar as bibliotecas que serializam como JSON, como o XStream e o FlexJSON. Listagem 13. View para gerar json: lista.json.jsp. <c:forEach var=”aluno” items=”${alunoList}”> { “nome”: “${aluno.nome}”, “endereco”: “${aluno.endereco}”, “email”: “${aluno.email}” }, </c:forEach> {} }, {} ]} Já para realizar o Upload do arquivo, o VRaptor disponibiliza a interface UploadedFile, que provê acesso a informações do arquivo que está sendo enviado. Inclusive, é possível acessar o conteúdo do arquivo propriamente dito. Um exemplo de como se trabalhar com Download e Upload de arquivos com VRaptor3 está na Listagem 15. Listagem 15. Exemplo de Download e Upload de arquivo. @Resource public class AlunosController { public File download() { File file = //cria o arquivo return file; } public void upload(UploadedFile file) { InputStream stream = file.getFile(); //salva onde você quiser: IOUtils.copy(stream, new PrintWriter(“um arquivo”)); } } Testando as lógicas Usar injeção de dependências, além de desacoplar seu código através de um design mais simples, facilita a criação de testes unitários. No entanto, alguns controllers recebem objetos Result e Validator, com suas interfaces fluentes que dificultam a passagem de implementações falsas (Mocks) dessas interfaces. Por isso, o VRaptor disponibiliza duas classes para ajudar a testar as lógicas: a MockResult e a MockValidator. Então, para criar o seu controller com mock, basta fazer: ]} AlunosController controller = new AlunosController(alunoDao, new MockResult(), new MockValidator()); Como fazer download e upload de arquivos Para trabalhar com Download de arquivos no VRaptor 3, a única coisa necessária é retornar um java.io.File no método da sua lógica. Dessa forma, quando o usuário acessar a lógica determinada, esse arquivo será aberto pelo navegador ou disponibilizado para download – dependendo das configurações do navegador. 10 www.mundoj.com.br Esses mocks não tratam as chamadas feitas a eles, então fica fácil criar testes que ignoram qual foi a view escolhida, os atributos adicionados e a validação feita. Mesmo assim, você pode (e deve) inspecionar os atributos que foram incluídos no result e saber se houve erros de validação durante a execução da sua lógica, através de outros métodos disponibilizados pelo MockResult e MockValidator. Veja nas Listagens 16 e 17. .BEFJO#SB[JMt73BQUPSHVFSSJMIBXFC Listagem 16. Inspecionando atributos incluídos. Listagem 18. ComponentFactory para criar Session do Hibernate a serem injetadas. public class AlunosControllerTest { @Test public void testaSeAListaDeAlunosFoiIncluida() { MockResult result = new MockResult(); AlunosController controller = new AlunosController(alunoDao, result, new MockValidator()); controller.lista(); Assert.assertNotNull(result.included(“listaDeAlunos”)); } @Component @RequestScoped public class SessionCreator implements ComponentFactory<Session> { private SessionFactory factory; private Session session; } public SessionCreator(SessionFactory factory) { this.factory = factory; } Listagem 17. Verificando erros de validação. public Session getInstance() { return this.session; } @PostConstruct public void create() { this.session = factory.openSession(); } public class AlunosControllerTest { @Test(expected=ValidationError.class) public void testaSeAlunoInvalidoDaErroDeValidacao() { AlunosController controller = new AlunosController(alunoDao, new MockResult(), new MockValidator()); Aluno aluno = new Aluno(); // aluno sem nome é inválido controller.adiciona(aluno); // lança exception } @PreDestroy public void destroy() { this.session.close(); } } } Trabalhando com Hibernate e JPA Trabalhando com bibliotecas como o Hibernate e a JPA, gostaríamos de poder receber nas nossas classes Session's e EntityManager's no construtor, para não ter de ficar abrindo e fechando esses recursos e transações dentro de todos os controladores (são recursos caros, merecem ser tratados em algum lugar específico). Para fazer isso, usamos a interface ComponentFactory, que cria um componente responsável por instanciar classes que têm um ciclo de vida mais complicado. Nos ComponentFactories, na verdade em qualquer componente, você pode ter um método anotado com @PostConstruct que será invocado assim que o escopo do componente foi iniciado, e um método anotado com @PreDestroy, que será invocado logo antes do escopo ser destruído. Por default, componentes são de escopo de request, e podemos alterar isso através das anotações @ApplicationScoped, @SessionScoped e @ RequestScoped. Por exemplo, se o componente é @RequestScoped, o @ PostConstruct será invocado quando começar a requisição e o @PreDestroy quando a requisição terminar. Isso é bem útil para gerenciar recursos caros, como sessões, conexões ao banco e sockets. Um exemplo de como fazer um ComponentFactory de Session está na Listagem 18. E quem vai popular o construtor do SessionCreator com uma SessionFactory? Da mesma forma podemos criar ComponentFactories para SessionFactory! E então anotaríamos esse componente com @ApplicationScoped. Quando fosse necessário instanciar um SessionCreator, o SessionFactory seria fornecido através do método getInstance de um SessionFactoryCreator, que implementasse ComponentFactory<Sessio nFactory>. Com isso, teríamos um código extremamente desacoplado e fácil de testar. Ambas factories, dada sua popularidade, já vêm com o VRaptor. Por padrão não são registradas, para que você mesmo possa criar a sua, caso queira utilizá-las, basta estendê-las e anotá-las com @Component e os respectivos escopos. Consulte a documentação para mais detalhes. Interceptadores Podemos interceptar as requisições, no mesmo estilo que uma Servlet Filter, e aproveitar a injeção de dependência e outros recursos já oferecidos pelo VRaptor. É um excelente lugar para gerenciarmos a transação da sessão do hibernate, por exemplo, criando a transação antes da execução do Controller e commitando, caso tudo tenha corrido normalmente dentro da sua lógica. Com o VRaptor, nós conseguimos este comportamento implementando a interface Interceptor. Note que praticamente todos os nossos controllers precisam de uma session para executar, portanto é interessante interceptarmos todas as requisições que chegam para a nossa aplicação. Para isso, ao implementarmos a interface Interceptor, temos que escrever o método accepts, que indica se determinado método deverá ou não ser interceptado. Um exemplo dessa situação está na Listagem 19. Até mesmo um interceptador pode aproveitar da injeção de dependências, e ele receberá a Session do ComponentFactory que criamos anteriormente. Mais facilidades para testes e desacoplamento de dependências! Trabalhando com objetos na sessão Na nossa aplicação, os alunos também poderão efetuar Login, para que dessa forma possam depois executar funcionalidades que dependem da identificação do usuário. 11 .BEFJO#SB[JMt73BQUPSHVFSSJMIBXFC Listagem 19. Interceptor para gerenciar transações. Listagem 21. Controller para fazer o login do usuário. @Intercepts @RequestScoped public class TransactionInterceptor implements br.com.caelum.vraptor. Interceptor { @Resource public class LoginController { private AlunoLogado alunoLogado; public LoginController(AlunoLogado alunoLogado) { this.alunoLogado = alunoLogado; } private final Session session; public TransactionInterceptor(Session session) { this.session = session; } public void intercept(InterceptorStack stack, ResourceMethod method, Object instance) throws InterceptorException { Transaction transaction = null; try { transaction = session.beginTransaction(); stack.next(method, instance); // passa para proximo interceptador transaction.commit(); } finally { if( transaction.isActive() ) { transaction.rollback(); } } } public boolean accepts(ResourceMethod method) { return true; //Indica que todas os controllers serão interceptados } @Path(“/login”) public void login(Aluno aluno) { //Faz aqui as regras para a validação do login //Defino o aluno e a hora atual no componente que está na sessão alunoLogado.efetuaLogin(aluno, Calendar.getInstance()); } } para todos os jsp que forem acessados dentro da sessão. Para termos esse comportamento, bastaria receber no nosso componente UsuarioLogado a HttpSession no construtor, e no momento de efetuarmos o login, invocarmos o método setAttribute da HttpSession: public void efetuaLogin(Aluno aluno, Calendar horario) { this.aluno = aluno; this.horarioDoLogin = horario; } Para deixar um objeto na sessão, poderíamos receber HttpSession por injeção de dependências, mas isso deixa nosso código mais acoplado à API de Servlets, que é difícil de testar. Podemos criar um @Component, cujo seu escopo seja de sessão (@SessionScoped), que guardará as informações referentes a esse Aluno, como na Listagem 20. Listagem 20. Componente para guardar as informações do aluno na sessão. @Component @SessionScoped public class UsuarioLogado { private Aluno aluno; private Calendar horarioDoLogin; public void efetuaLogin(Aluno aluno, Calendar horario) { this.aluno = aluno; this.horarioDoLogin = horario; } // getters } Dessa forma, um controller encarregado de fazer o login da nossa aplicação, bastaria receber em seu construtor UsuarioLogado, e no momento do login, definir quem é o usuário logado e a hora do login, invocando o método efetuaLogin() como exemplificado na Listagem 21. Ainda é possível também exibir nas páginas da aplicação informações sobre o usuário logado, como o nome dele e a hora em que ele fez o login. Para isso, poderíamos usar o método include da interface Result, no entanto, teríamos que fazer isso em todos os nossos controllers, pois o jsp não possui acesso aos nossos componentes @SessionScoped a não ser que os disponibilizemos no Result. //Disponibilizando o aluno e o horario na HttpSession this.session.setAttribute(“alunoLogado”, aluno); this.session.setAttribute(“horarioDoLogin”, aluno); } Dessa forma, podemos mostrar o nome do usuário logado no jsp através da Expression Language, com ${alunoLogado.nome}. Considerações finais Depois de 5 anos desde sua primeira versão, o VRaptor 3.0 traz significativas mudanças focadas na facilidade de uso e testabilidade, como vimos no artigo. Há ainda outros recursos que você pode explorar: você pode criar seu próprio conversor para popular os parâmetros de métodos, estender os componentes e interceptadores internos do framework, e até mesmo sobreescrever todo o sistema de rotas, tratamento de erros (como customizar os 404) etc. Você pode também usar o framework dentro do Google App Engine, tirando o proveito do cloud computing, através de algumas pequenas configurações que podem ser vistas na documentação do projeto. O fórum de frameworks brasileiros no GUJ é atualmente a principal forma de suporte ao VRaptor, onde sua dúvida será respondida prontamente, e já há uma quantidade muito grande de mensagens a respeito: http://guj.com.br/forums/show/23.java Referências t 4JUFPmDJBMFNQPSUVHVÐT Uma solução que pode ser mais interessante é colocar esse aluno que está logado dentro da HttpSession, e aí disponibilizarmos esse aluno www.vraptor.com.br t 'ØSVNPmDJBM http://guj.com.br/forums/show/23.java 12 www.mundoj.com.br