Quem já experimentou frameworks como Django e Rails sabe bem que o desenvolvimento de aplicações web pode perfeitamente ser produtivo e até divertido. Isso estava fora da realidade do mundo Java antes do surgimento do Play Framework!.
O Play é um framework full stack, stateless, focado em produtividade, bastante parecido com Django e Rails. É construído sobre uma arquitetura assíncrona baseada em eventos, sendo muito simples de escalar. Possui suporte a módulos, gerenciamento de dependências e configuração baseada em arquivos properties. Não é necessário compilar e realizar deploy em um servidor de aplicações, o Play vem com um servidor embutido, e durante o desenvolvimento basta alterar o código, salvar e recarregar o browser. O próprio framework compila e recarrega a aplicação, como em linguagens interpretadas. E se houverem erros, eles serão exibidos no próprio browser, de forma intuitiva.
O Play vem com um conjunto de ferramentas em modo console para várias tarefas, como criar novos projetos, iniciar a aplicação com o servidor embutido, executar testes, configurar dependências, gerar um pacote WAR, etc, o que o torna bastante produtivo tanto para novos projetos quanto para dar manutenção em um projeto já existente. É possível desenvolver em Java ou Scala (sim, Scala!).
Vamos enumerar alguma das caracteristicas do framework:
- Limpo, Rápido e Dinâmico
- Produtivo, Poderoso e Divertido!
- Puro Java (Permite a importação e uso de qualquer biblioteca Java)
- Utiliza o sistema de template Groovy(Groovy trabalha de forma transparente com todos os objetos e bibliotecas Java existentes e compila diretamente para Java bytecode tanto no desenvolvimento de aplicações como no modo script.)
- Utiliza sistemas de tradução de Rotas
- Facilidade na resolução de erro
- Padrão MVC
- Assíncrono (I/O não bloqueante)
- Stateless
- Arquitetura RESTful ( REST (ou Representational State Transfer) é um estilo de desenvolvimento para sistemas distribuídos que privilegia a trasferência de estado sem envolver camadas de mapeamento (como o SOAP faz). Assim, os verbos GET, POST, PUT e DELETE mapeam quase que diretamente para ações READ, UPDATE, CREATE e DELETE (CRUD).
- Persistência com JPA (JPA é uma especificação de Persistência da plataforma Java, para persistir as classes java em bancos de dados relacionais. )
- Extensível (Através de Módulos / Bibliotecas Java)
- TDD – Test Development Drive (Test Driven Development (TDD) ou em português Desenvolvimento dirigido por testes )
Como iniciar uma aplicação no Play?
É facil, basta usar o comando no consolhe (Terminal no Linux e Prompt de comando no Windows, lembrando que você terá que estar dentro da pasta do framework no windows e no linux ter adicionado o caminho da pasta ao PATH para executar o comando play):
play new nomedaaplicacao
Toda a estrutura do projeto será criada, incluindo arquivos de configuração e o roteamento inicial da aplicação. Se você deseja abrir o projeto no Eclipse ou no Netbeans, o Play! cria o projeto para você. Basta fazer assim:
play eclipsify nomedaaplicacao
Ou se preferir o Netbeans:
play netbeansify nomedaaplicacao
Dessa forma, você poderá importar o projeto de sua IDE e todas as configurações estarão realizadas. Outro comando importante é a gerador de war (deploy).
play war nomeDoProjeto -o nomeDoProjeto.war
Pronto, feito isto basta começar a programar no projeto usando Java ou Scala 🙂 !
Slide da apresentação feita na SWX: play-framework-apresentaçao
Código de uma pequena aplicação de uma agenda feita na SWX:
No diretório /conf no arquivo application.conf , caso use mysql, basta localizar e modificar o padrão para seu usuário e senha do mysql e o nome do banco (No exemplo a seguir está sem senha).
# To connect to a local MySQL5 database, use: db=mysql:acson@minha_agenda // acson -> usuário do banco, minha_agenda -> banco
No diretório /app/controllers , modifique o Application.java (ou crie se não existir) para:
package controllers; import play.*; import play.mvc.*; import play.data.validation.*; import java.util.*; import models.*; public class Application extends Controller { public static void index() { List<Contato> contatos = Contato.find("order by nome asc").fetch(); render(contatos); } public static void inserir() { render(); } public static void visualizar(String email) { Contato contato = Contato.find("email", email).first(); render(contato); } public static void editar(String email) { Contato contato = Contato.find("email", email).first(); render(contato); } public static void excluir(String email) { Contato contato = Contato.find("email", email).first(); contato.delete(); index(); } public static void cadastrar_contato(@Required String nome, @Required String email, String telefone, String twitter, String skype) { Contato contato = new Contato(nome, email, telefone, twitter, skype); if (validation.hasErrors()) { render("Application/inserir.html", contato); } contato.save(); index(); } public static void editar_contato(long id) { validation.required(request.params.get("nome")); validation.required(request.params.get("email")); Contato contato = Contato.find("id", id).first(); if (validation.hasErrors()) { render("Application/editar.html", contato); } contato.nome = request.params.get("nome"); contato.telefone = request.params.get("telefone"); contato.email = request.params.get("email"); contato.twitter = request.params.get("twitter"); contato.skype = request.params.get("skype"); contato.save(); index(); } }
No diretório /app/models , crie a classe Contato (Contato.java) com o seguinte código:
package models; import play.*; import play.db.jpa.*; import javax.persistence.*; import java.util.*; @Entity public class Contato extends Model { public String nome; public String email; public String telefone; public String twitter; public String skype; public Contato(String nome, String email, String telefone, String twitter, String skype) { this.nome = nome; this.email = email; this.telefone = telefone; this.twitter = twitter; this.skype = skype; } }
No diretório /app/views , crie o arquivo application.html com o seguinte código:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>.: Agenda de Contatos - #{get 'title' /}</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" media="screen" href="@{'/public/stylesheets/principal.css'}"> #{get 'moreStyles' /} <script src="@{'/public/javascripts/jquery-1.4.2.min.js'}" type="text/javascript" charset="utf-8"></script> #{get 'moreScripts' /} </head> <body id="page1"> <div id="geral"> <div id="conteudo"> <div id="logo"></div> <div class="linha"> <div class="titulo">.: Agenda de Contatos</div> #{if request.actionMethod != "inserir" && request.actionMethod != "editar"} <div class="text"><a href="@{Application.inserir()}" class="button">+ Incluir Contato</a></div> #{/if} </div> <div class="linha"></div> #{doLayout /} </div> </div> </div> </body> </html>
No diretório /app/views/Application , crie o arquivo inserir.html com o seguinte código:
#{extends 'application.html' /} #{set title:'Inclusão' /} #{form @Application.cadastrar_contato()} #{ifErrors} <div class="linha"><p style="color:red;"><strong>ATENÇÃO: </strong>Preencha todos os campos obrigatórios.</p></div> <div class="linha"></div> #{/ifErrors} <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="nome">Nome:</label></p> </div> <div class="dir"> <p><input type="text" name="nome" id="nome" value="${params.nome}" /></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="telefone">Telefone:</label></p> </div> <div class="dir"> <p><input type="text" name="telefone" id="telefone" value="${params.telefone}" /></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="email">Email:</label></p> </div> <div class="dir"> <p><input type="text" name="email" id="email" value="${params.email}" /></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="twitter">Twitter:</label></p> </div> <div class="dir"> <p><input type="text" name="twitter" id="twitter" value="${params.twitter}" /></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="skype">Skype:</label></p> </div> <div class="dir"> <p><input type="text" name="skype" id="skype" value="${params.skype}" /></p> </div> </div> </div> <div class="btn-submit"> <p><input type="submit" value="Cadastrar o Contato" /></p> </div> #{/form}
No diretório /app/views/Application , crie o arquivo visualizar.html com o seguinte código:
#{extends 'application.html' /} #{set title:'Visualizao' /} <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="nome">Nome:</label></p> </div> <div class="dir"> <p>${contato.nome}</p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="telefone">Telefone:</label></p> </div> <div class="dir"> <p>${contato.telefone}</p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="email">Email:</label></p> </div> <div class="dir"> <p>${contato.email}</p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="twitter">Twitter:</label></p> </div> <div class="dir"> <p>${contato.twitter}</p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="skype">Skype:</label></p> </div> <div class="dir"> <p>${contato.skype}</p> </div> </div> </div> <div class="btn-submit"> <p> <a href="@{Application.index()}">Cancelar</a> <a href="@{Application.editar(contato.email)}">Editar</a> <a href="@{Application.excluir(contato.email)}" onClick="if(!confirm('Deseja excluir este contato?')){return false;}">Excluir</a> </p> </div>
No diretório /app/views/Application , crie o arquivo editar.html com o seguinte código:
#{extends 'application.html' /} #{set title:'Edição' /} #{form @Application.editar_contato(contato.id)} #{ifErrors} <div class="linha"><p style="color:red;"><strong>ATENÇÃO: </strong>Preencha todos os campos obrigatórios.</p></div> <div class="linha"></div> #{/ifErrors} <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="nome">Nome:</label></p> </div> <div class="dir"> <p><input type="text" name="nome" id="nome" value="${contato.nome}" /></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="telefone">Telefone:</label></p> </div> <div class="dir"> <p><input type="text" name="telefone" id="telefone" value="${contato.telefone}" /></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="email">Email:</label></p> </div> <div class="dir"> <p><input type="text" name="email" id="email" value="${contato.email}" /></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="twitter">Twitter:</label></p> </div> <div class="dir"> <p><input type="text" name="twitter" id="twitter" value="${contato.twitter}"/></p> </div> </div> </div> <div class="linha"> <div class="duas-colunas"> <div class="esq"> <p><label for="skype">Skype:</label></p> </div> <div class="dir"> <p><input type="text" name="skype" id="skype" value="${contato.skype}" /></p> </div> </div> </div> <div class="btn-submit"> <p><input type="submit" value="Editar o Contato" /></p> </div> #{/form}
No diretório /app/views/Application , crie o arquivo index.html com o seguinte código:
#{extends 'application.html' /} #{set title:'Listagem' /} #{if contatos.size() == 0} <div class="linha"> <p>Nenhum contato cadastrado.</p> </div> #{/if} #{else} #{list items:contatos, as:'contato'} <div class="linha"> <p><a href="@{Application.visualizar(contato.email)}">» ${contato.nome} - ${contato.email}</a></p> </div> #{/list} #{/else}
E para ficar mais agradável e padronizado, vamos mudar o arquivo de rotas que fica em /conf , o arquivo é o routes
# Routes # This file defines all application routes (Higher priority routes first) # ~~~~ # Home page GET / Application.index GET /cadastrar Application.inserir GET /visualizar/{email} Application.visualizar GET /editar/{email} Application.editar GET /excluir/{email} Application.excluir # Map static resources from the /app/public folder to the /public path GET /public/ staticDir:public # Catch all * /{controller}/{action} {controller}.{action}
Quando o usuário chamar o método de inserir que está no Application.java, no browser aparecerá dominio/cadastrar, e assim por diante.
Terminando isso o aplicativo está pronto para uso.
Abaixo algumas telas de uma pequena aplicação que fizemos, uma agenda de contados:
Site oficial do Play Framework: http://www.playframework.org/
Github do Play Framework: https://github.com/playframework/play
Algumas fontes consultadas: Imasters, Bragil.net