play_ Aumente sua produtividade com o framework Play Crie um sistema completo em minutos e entenda por que tantas pessoas estão abandonando o seu framework web preferido para usar Play. N o ano de 2007 o desenvolvedor Guillaume Bort trabalhava em um projeto na empresa Zenexity (em Paris) e ele precisava de um framework bem produtivo. Ele não via o JSF como uma boa opção para aplicações web, e invejava a produtividade do desenvolvimento em Ruby on Rails. Com isso ele criou o framework baseado em suas próprias necessidades, e nesse momento ele está focado na versão 2.0 que promete muitas mudanças. Entretanto, vamos focar este artigo na versão 1.2.x, que é a mais usada. simplicidade e rápida curva de aprendizado. Além de possuir vários recursos embutidos, o playframework possui uma boa lista de módulos que podem ser “plugados” à aplicação, disponibilizando recursos mais avançados, como, por exemplo, Web Services, integrações com redes sociais Facebook e Twitter, testes automatizados, entre outros. A instalação é muito simples e está destacada mais adiante. Começamos criando a aplicação com a chamada: Conheça o framework play new NetCitacoes Um erro comum é comparar o Play com outros frameworks Java do mercado, como Spring MVC, Struts, Mentawai ou VRaptor, ele foge dos tradicionais padrões Java Enterprise e busca soluções baseadas em script semelhante ao Ruby on Rails, PHP Cake ou Grails. Além disso, ele possui um servidor HTTP embutido e não é baseado na API de Servlet (apesar de exportar um pacote WAR totalmente compatível com o padrão JEE). O framework Play é famoso por facilitar, e muito, a vida do desenvolvedor que precisa criar uma aplicação MVC de forma fácil e rápida. Os recursos que o framework disponibiliza impressionam pela / 14 Com isso será criada uma estrutura básica de sua aplicação (figura 1). Entre no diretório criado e digite play run Depois acesse no browser no endereço http:// localhost:9000 . Vamos para o Eclipse agora? play eclipsify Com isso ele criará os arquivos .classpath e .project para podermos importar esse projeto para dentro do Eclipse. Eduardo Cerqueira | [email protected] Formado pela Universidade Ibirapuera, trabalha com java desde 2005 e sócio-diretor da WebStart Solutions, empresa com foco na prestação de serviços de desenvolvimento de software também dos criadores do site E-AgendaMédica ( www.eagendamedica.com.br ) Fernando Boaglio | [email protected] Formado pela Unesp em BCC, foi instrutor oficial da Sun Microsystems e da Oracle Education. Atualmente contribui para alguns projetos open source, como KDE e Mentawai e é da equipe de arquitetura da Discover Technology, prestando serviços para a Tokio Marine Seguradora. O desenvolvimento de aplicações web ficou mais fácil, leve e rápido com esse framework que, apesar de ser relativamente novo, causa bastante barulho na comunidade e tem sido uma “mão na roda” no desenvolvimento de aplicações para startups no mundo todo. Mostraremos neste artigo como ele funciona, como é definida sua arquitetura e desenvolveremos uma aplicação completa demonstrando alguns dos incríveis recursos desse framework e, o melhor de tudo, sem dor! o framework Play possui quatro arquivos principais de configuração: » application.conf − guarda configurações da JVM, informações de log, persistência, rede e Proxy; » dependencies.yml − lista os módulos usados pelo seu sistema; » messages − localização de mensagens; » routes − define os mapeamentos das URIs do seu sistema, apontado para controladores estáticos ou dinâmicos. Uma das qualidades do play é sua velocidade no desenvolvimento. Esqueça deploy, start e stop de servidor, vamos criar um controlador e uma página que recebe o seu resultado sem precisar fazer nada da maneira convencional, basta apenas criar as novas rotinas. O seu primeiro controller Figura 1. Estrutura básica de um projeto novo. Em primeiro lugar, deixe o servidor do play rodando no projeto (play run mencionado anteriormente). Dentro do pacote app.controllers, crie a classe Teste que estende da classe play.mvc.Controller, com um método chamado index conforme a Listagem 1. 15 \ Listagem 1. Primeiro controlador de exemplo. package controllers; import play.mvc.Controller; public class Teste extends Controller { public static void index() { renderArgs.put(“variavel”,”teste”); render(); } } @Entity public class ListaDeCompras extends Model { @Required public String nome; } Continuamos o exemplo criando o controlador da Listagem 4, que possui o método index para efetuar a busca da lista do banco de dados e jogar na tela Depois disso, dentro do diretório views, crie um através do método render, e o método save, que recediretório com o nome do controlador (Teste), e den- be como parâmetro o objeto que vamos persistir no tro dele um arquivo chamado index.html contendo a banco de dados. página HTML conforme a Listagem 2. Listagem 4. Controlador da lista de compras. Listagem 2. Página HTML do primeiro controlador de package controllers; exemplo. <html> <h2>meu primeiro ${variavel} !</h2> </html>> Em seguida, acesse o browser no endereço http:// localhost:9000/teste/index e perceba a magia do play! Classes e páginas novas ficam disponíveis dinamicamente agilizando o processo de desenvolvimento! É como o autor diz: “arrume o bug e pressione reload”. Essa mágica é possível graças ao poderoso engine baseado em Groovy, que também é a raiz do projeto Grails. import java.util.List; import models.ListaDeCompras; import play.mvc.Controller; public class Lista extends Controller { public static void index() { List<ListaDeCompras> compras = models.ListaDeCompras.all().fetch(); render(compras); } public static void save(ListaDeCompras item) { item.save(); index(); } O seu primeiro cadastro É comum começarmos o desenvolvimento de um cadastro e com o tempo mudarmos o valor dos campos da tela (ou colunas de uma tabela). A mesma flexibilidade que play oferece com as classes Java também é oferecida com o banco de dados. Edite o arquivo application.conf “descomentando” a linha db=mem para automaticamente mapear o nosso banco de dados na memória. Vamos criar uma simples lista de compras e, seguindo o modelo MVC, vamos começar com a representação do negócio com a classe ListaDeCompras (Listagem 3). Para termos o mapeamento desejado, basta que a classe estenda play.db.jpa.Model e tenha a anotação Entity. Listagem 3. Classe que representa uma lista de compras. package models; import javax.persistence.Entity; import play.data.validation. Required; import play.db.jpa.Model; / 16 } Finalmente, na camada view (Listagem 5) temos o arquivo index.html (dentro da pasta app/views/Lista) com o formulário que aponta para o método Lista. save e uma rotina que exibe os dados da lista de compras enviados pelo controlador. Listagem 5. Arquivo index.html que representa a lista de compras. <html> <h2>Lista de compras</h2> #{form @Lista.save()} <table> <th> <input type=”text” name=”item.nome” value=”${item?.nome}”/> </th> <td><input type=”submit” name=”submit” value=”Cadastrar” /> </td> #{/form} </table> <hr/> <table> #{list items:compras, as:’item’} <tr><td>${item.nome}</td></tr> #{/list} </table> </html> Para ver o resultado, acesse http://localhost:9000/ lista/index e faça alguns cadastros. Confira também o log das operações executadas que aparece no console Figura 4. Tela de cadastro de usuários. onde foi digitado play run. Depois dessa pequena introdução para entender o framework, vamos abandonar a lista de compras e fazer um sistema um pouco mais complexo. Conheça o NetCitações Temos um simples sistema de citações famosas, onde vamos persistir informações das frases, dos co- Figura 5. Tela de listagem de usuários. mentários de cada frase e das pessoas que cadastraram. Nesse sistema, apenas os administradores podem cadastrar os usuários (figura 2). Usando o módulo scaffold o módulo scaffold, como comentado anteriormente, tem o objetivo de gerar automaticamente Módulos utilizados Para criar as telas de CRUD automaticamente as classes de controle e os htmls de visualização da utilizamos o módulo scaffold, que de forma simples nossa aplicação. Poderíamos fazer isso manualmenirá gerar as nossas classes de controle e visualização te, porém utilizaremos esse módulo para poupar rapidamente e usamos também o módulo secure para trabalho. o scaffold utiliza as classes de modelo que validar a autenticação do cadastro de usuários (figura geramos para saber quais controllers e views precisa criar. Além disso, ele analisa os tipos de dados e ano3). tações para gerar também mecanismos de validação nas views. Configurações do módulo scaffold Como pré-requisito desse módulo, as classes de domínio Usuario, Citacao e Comentario já foram criadas para permitir a geração dos demais arquivos-fonte. Para instalar o módulo, precisamos editar o arquivo dependencies.yml e incluir a chamada para o módulo conforme Listagem 6. Figura 3. Tela de cadastro de usuário do NetCitações. Figura 2. Classes do NetCitações. 17 \ Listagem 6. Configurando módulo scaffold. #Application dependencies require: -play -play -> scaffold head A linha que adicionamos das informações do módulo que usaremos, no caso, scaffold e sua versão, que nesse caso será head. Após salvar o arquivo, o play precisa baixar os arquivos do módulo que nós acabamos de configurá-lo. Para isso, é preciso executar o comando: Listagem 7. Configurando módulo secure. #Application dependencies require: -play -play -> scaffold head -play -> secure O módulo secure já faz parte dos pacotes padrões do play, porém precisamos ainda executar todos os comandos para que o framework inclua os arquivos do módulo. Então digitaremos mais uma vez os comandos que têm essa finalidade. play deps --sync play deps --sync play eclipsify play scaffold:gen play scaffolg:gen --overwrite --withlogin --with-layout Depois de baixar todos os arquivos, é preciso recriar os arquivos do projeto do Eclipse para que sejam Módulos scaffold e secure trabalhando incluídos em seu path. Lembre-se de fechar a aplicação no Eclipse antes juntos de executar o comando, pois pode haver problemas Agora que já instalamos os módulos que precisana atualização dos arquivos. mos, vamos usar o módulo scaffold para gerar a nosAgora, vamos usar o módulo para gerar nossas sa tela de login e controlar nossos usuários de forma classes de controle e views. integrada. Para essa tarefa, executamos o comando: Essa tarefa é feita através do comando abaixo: Então, executamos o comando de configuração do Eclipse novamente, como exibido abaixo: Verificando o nosso projeto, podemos ver que o módulo gerou arquivos dentro do pacote controller e views. Basta agora executar o servidor novamente para que os arquivos estejam disponíveis acessando via browser com o comando: O comando acima irá sobreescrever os arquivos e regerar as classes de controle e as views do sistema e também as classes de controle responsáveis pelo login com a tela respectiva e ainda incluiremos um pouco de estilo para as telas ficarem mais amigáveis. Rodamos mais uma vez o comando para configurar o projeto no Eclipse e incluir os arquivos em seus paths. Esse passo é necessário apenas na inclusão de um módulo novo no seu projeto. play run play eclipsify play eclipsify Pronto, nosso cadastro básico está concluído, porém precisamos ainda adicionar mais funcionalidades à nossa aplicação, como, por exemplo, autenticação. Passaremos então a falar do próximo módulo utilizado na aplicação, o módulo Secure. Usando o módulo secure E subimos o servidor para acessar a aplicação. play run NetCitacoes Algumas convenções do módulo scaffold Nas documentações do play, temos a convenção de utilizarmos o nome do controller no plural para o seu respectivo modelo, por exemplo, para a classe de modelo Citacao utiliza-se o nome Citacaos para a controller, porém, quando escrevemos as classes em português, fica meio esquisito. O nome da controller pode ser alterado sem qualquer prejuízo pra aplicação, porém se futuramente precisar regerar as classes utilizando o módulo scaffold, as classes serão geradas com os nomes originais. O módulo secure é responsável pelo mecanismo de autenticação de usuários no play, existem outros módulos que têm essa mesma função e ainda realizam integração com Facebook, Twitter e outros utilizando OpenAuth e outros protocolos de autenticação, porém, utilizaremos o Secure por ser mais simples e atender ao propósito da aplicação. O método de instalação do módulo é semelhante ao já mencionado na instalação do módulo scaffold. Vamos editar novamente o arquivo dependencies.yml Olhando o código mais a fundo Vamos olhar alguns dos arquivos e destacar alincluindo uma linha a mais de informação, conforme guns itens para entendimento. Listagem 7. / 18 A classe model Usuario.java rificar o preenchimento dessa informação e retornará Na classe de modelo usuário, na Listagem 8, te- uma mensagem de erro caso isso não tenha sido feito. mos algumas anotações que devemos olhar com mais Olharemos mais adiante na classe Controller como isso será feito. atenção. Agora, só nos resta falar sobre a anotação @ Listagem 8. Arquivo model Usuario. Email, ela serve pra que no momento da validação do objeto a ser persistido o framework verifique se o package models; valor informado nesse atributo possui o formato de um e-mail válido, ou seja, a existência de um ‘@’ no import java.util.List; valor. import javax.persistence.Entity; import javax.persistence.OneToMany; import play.data.validation.Email; import play.data.validation.Required; import play.db.jpa.Model; @Entity public class Usuario extends Model { @Required public String nome; @Required @Email public String email; public boolean isAdmin; public String senha; @OneToMany(mappedBy=”usuario”) public List<Citacao> citacoes; @Override public String toString() { return nome; } } Olhando com mais cuidado, veremos que apesar de utilizarmos JPA, nós não precisamos definir a chave primária da entidade com o @Id. Porém, a especificação JPA nos diz que somos obrigados a ter um atributo de classe com a anotação representando a chave primária da entidade. Repare que estamos estendendo a classe play.db.jpa.Model e nela temos o atributo com a anotação associada, ou seja, ao estendermos essa classe não precisamos definir quem será nossa chave primeira, pois a classe já irá associar o que precisamos. Entretanto, em alguns casos queremos definir o nome da coluna utilizada como chave primária. Para isso devemos então trocar a classe play.db.jpa.Model para play.db.jpa.GenericModel. Desse modo, podemos definir o atributo de chave primária normalmente. Temos mais duas anotações que necessitam da nossa atenção, @Required que informa ao framework que esses campos são de preenchimento obrigatório. Sendo assim, em um formulário, o framework irá ve- A classe controller Usuarios.java Nessa classe temos algumas anotações que são utilizadas pelo módulo secure para definir controle de acesso. Por exemplo, para cadastrar, editar ou deletar um usuário eu preciso ser um usuário autenticado e ter perfil de administrador. A Listagem 9 dá uma amostra da classe gerada pelo módulo. Listagem 9. Arquivo controller Usuarios. package controllers; import java.util.List; import models.Usuario; import play.data.validation.Valid; import play.i18n.Messages; import play.mvc.Before; import play.mvc.Controller; import play.mvc.With; @With(Secure.class) public class Usuarios extends Controller { @Before static void user() { renderArgs.put(“user”, Security.connected()); } public static void index() { List<Usuario> entities = models.Usuario.all().fetch(); render(entities); } public static void create(Usuario entity) { render(entity); } public static void show(java.lang.Long id) { Usuario entity = Usuario.findById(id); render(entity); } public static void edit(java.lang.Long id) { Usuario entity = Usuario.findById(id); render(entity); } public static void delete(java.lang.Long id) { Usuario entity = Usuario.findById(id); entity.delete(); index(); } 19 \ A classe controller Application.java public static void save(@Valid Usuario entity) { if (validation.hasErrors()) { flash.error(Messages.get(“scaffold.validation”)); render(“@create”, entity); } entity.save(); flash.success(Messages.get(“scaffold.created”, “Usuario”)); index(); } public static void update(@Valid Usuario entity) { if (validation.hasErrors()) { flash.error(Messages.get(“scaffold.validation”)); render(“@edit”, entity); } entity = entity.merge(); entity.save(); flash.success(Messages.get(“scaffold.updated”, “Usuario”)); index(); } Essa controller é a classe de entrada da aplicação, ou seja, é o nosso index, a primeira página e controller carregada quando a aplicação é executada. No nosso exemplo, ela não tem muita responsabilidade, uma vez que possui apenas os métodos index() que é o método inicial propriamente dito, verificando se existe algum usuário logado e retorna esse usuário, e o sair() que é responsável pelo logout do usuário. A Listagem 10 mostra como a classe está definida. Listagem 10. Arquivo controller Application. package controllers; import play.mvc.Controller; public class Application extends Controller { public static void index() { String user = Security.connected(); render(user); } public static void sair() throws Throwable { Secure.logout(); index(); } } Para dizer ao Play que somente usuários logados podem acessar à tela de usuários, eu utilizo a anotação @With(Secure.class), assim quando um usuário tentar acessar as informações contidas nesse controller, ele precisará estar logado. Porém, existe uma action chamada user() que recupera o usuário logado através do método Security. getConnected() e coloca na sessão com a chave user associada a ela. Esse método pode ser utilizado sem que um usuário esteja necessariamente logado, porque ele é um método que está sendo usado no momento do login e por isso não deve ser controlado. Para resolver esse problema, o play possui uma anotação @Before. Ela informa ao framework que essa action pode ser acionada antes que seja verificado se o usuário possui ou não acesso a ela. Olhando a action save() podemos ver outra anotação, o @Valid. Ela indica ao play que deve receber como argumento um Usuario válido. Então, caso essas diretrizes não sejam respeitadas, o play retorna uma mensagem de erro dizendo que o objeto não é válido. O Play sabe que se um objeto é válido ou não verificando as anotações existentes na classe de modelo. No nosso exemplo, o @Required e o @Email. Portanto, olhando a Listagem 8 podemos constatar que um usuário válido é aquele que possui os atributos nome preenchido e que o atributo e-mail tenha um formato de e-mail válido. / 20 } A classe controller Security.java Por default, o módulo Secure permite que todos os usuários consigam se autenticar na aplicação, apenas preenchendo os campos de login e senha quando solicitados. Porém, para aplicarmos nossas próprias regras de autenticação, como, por exemplo, um e-mail e senha que estejam cadastrados no banco de dados, precisamos sobrecarregar os métodos de autenticação e checagem para a nossa realidade. A Listagem 11 mostra a classe Security que criamos para efetuarmos a autenticação no sistema. No método authenticate(), incluímos uma verificação do usuário admin diretamente no código, pois o modelo não contempla a criação de perfis diferenciados no sistema, poderíamos ter feito isso, mas no nosso exemplo não é relevante, então optamos por não colocar e assim termos um material mais enxuto possível. Nesse mesmo método, caso não seja o e-mail e senha que definimos no código, a aplicação irá verificar a existência do usuário com o e-mail informado no banco de dados e irá comparar sua senha. Listagem 11. Arquivo controller Security. package controllers; import models.Usuario; public class Security extends Secure.Security{ static boolean authenticate(String email, String senha) { if(email.equals(“[email protected]”) && senha.equals(“!@QWas”)){ return true; } } Usuario usuario = Usuario.find(“byEmail”, email). first(); return usuario != null && usuario.senha.equals(senha); static boolean check(String perfil) { if(connected().equals( “[email protected]”)){ return true; } } Usuario usuario = Usuario.find(“byEmail”, connected()).first(); if (usuario != null && “admin”.equals(perfil)) { return usuario.isAdmin; } else { return false; } } Deploy da aplicação O play framework já possui um container onde a aplicação pode ser implantada, que na verdade é o mesmo utilizado para o desenvolvimento, porém com configurações da aplicação setadas para modo PROD. Na própria documentação do framework é fortemente recomendada essa prática, pois evita surpresas quando executar deploy em servidores de aplicação e servlet containers, evitando ter que alterar a aplicação no momento da subida após concluído o desenvolvimento, porém a execução da aplicação em um servlet container ou servidor de aplicação seja uma premissa, o play framework disponibiliza formas de gerar um arquivo WAR para deploy normalmente. Primeiramente, o deploy padrão do play, que é executando apenas digitando o comando abaixo: lizamos algum servidor HTTP, como, por exemplo, o Apache na porta 80 e rodarmos o container do play na porta 9000. Nessa segunda abordagem, podemos configurar um balancer que irá facilitar também em uma atualização de release, uma vez que conseguimos “chavear” a aplicação que irá responder as requisições recebidas, enquanto outra instância está sendo atualizada. Porém, caso essa abordagem não seja possível, e termos que implantar a aplicação fora do container padrão do play, podemos executar o seguinte comando para a geração de um arquivo WAR. Obs.: esse comando precisa ser executado fora da pasta da aplicação. play war NetCitacoes -o netcitacoes.war --zip --exclude .svn:target:logs:tmp Explicando com detalhes o comando que executamos: »» opção -o que permite escolhermos o nome do arquivo gerado; »» opção --zip informa o gerador que queremos uma cópia compactada; »» opção --exclude informa que os diretórios, .svn, target, logs e tmp não sejam incluídos no pacote. Considerações finais Após uma visão geral do framework, foi codificado e demonstrado uma pequena amostra do que o Play pode fazer e seus diferenciais de produtividade ficaram claros com o decorrer do uso. O código-fonte e demais referências estão no final do artigo. /referências > http://vimeo.com/7087610 − aplicação play em 10 minutos (vídeo oficial) > http://www.playframework.org/documentation/1.2.5/ home − documentação da versão 1.2.5 > https://github.com/egcerqueira/NetCitacoes − códigofonte do projeto NetCitações play start NetCitacoes Pronto, apenas isso é suficiente para colocar a aplicação em modo produção, pois o container embutido do play irá verificar as configurações no application.conf procurando pelas variáveis definidas para o modo produção, setá-las na aplicação e subir o servidor. Por padrão, a porta 9000 permanecerá mesmo no modo produção. Nesse caso, podemos usar duas abordagens de arquitetura, a primeira seria utilizar o container do play na porta 80. A segunda, seria uti- A instalação do framework play exige no mínimo Java 1.5 ou superior. Primeiro efetue o download do play e descompacte em algum lugar (exemplo: Windows: C:\play\ , Linux e MacOS: $HOME/play/), depois se certifique que o executável play está no path do sistema (Windows: PATH=%PATH%;C:\play\, Linux e MacOS: PATH=$PATH:/play/). Para testar a instalação, digite “play version” para ver a versão instalada. 21 \