Primeira aplicação Grails com simples autenticação

Depois de apresentar o Grails no meu último post, é hora de colocar a mão na massa e implementar uma primeira aplicação com o Grails.

Esta aplicação não será tão simples quanto o conhecido Hello World, que não serve muito para nos mostrar muita coisa além do mais simples exemplo, será uma aplicação simples, porém usando um mecanismo de autenticação bem simples (mas funcional), usando uma taglib criada para o exemplo, e usando um pouco de ajax com a ajuda do Grails.

Como pré-requisito é encessário ter o Grails instalado na sua máquina. Para isso, leia a documentação de instalação.

Vamos ao exemplo...

1) Criando o novo projeto.
Execute (no DOS ou shell do Linux):

grails create-app

Ao ser perguntado sobre o nome da aplicação, digite:

myapp

Pronto. O Grails criou uma aplicação pra você no diretório abaixo da raiz do Grails chamado "myapp". Portanto, dê um "cd myapp" para ir para o diretório do seu projeto.

2) Criando a primeira classe de domínio
Execute:

grails create-domain-class

Ao ser perguntado sobre o nome da classe, digite "User" (sem aspas). Vá até o diterório myapp/grails-app/domain e veja que o Grails criou uma classe User.groovy para você. Esta classe, aparentemente, não tem nada, porém, ela terá um atributo "Long id" e um "Long version", além de uma implementação padrão para os métodos toString(), hash() e equals(). Portanto, você só precisa agora adicionar os atributos que desejar, como por exemplo:


class User {
static withTable = "app_user"

String name
String login
String email
String passwd
Boolean active = true

static def hasMany = [ roles : Role ]
static def belongsTo = Role

static def constraints = {
name(length:2..100, blank:false)
login(unique:true)
email(email:true)
passwd(blank:false)
}
}


Perceba que usamos para esta classe o atributo especial "static withTable". Ele indica que a tabela no banco de dados deve ter outro nome sem ser o mesmo nome da classe. Usamos isso aqui, pois, no caso de SQL Server da MS, por exemplo, "user" é uma palavra reservada, e não pode ser usada para nome de tabela.

A outra classe referenciada por esta classe User é Role, como segue abaixo.


class Role {
String name

static def hasMany = [ users : User ]

static def constraints = {
name(length:2..100, blank:false, unique:true)
}
}


Perceba que usamos uma relação many-to-many entre User e Role.

3) Gerando o resto...
Execute:

grails generate-all

Ao ser perguntado, informe User como a classe de domínio que servirá de base para o grails gerar o resto (controller, views). Você pode fazer o mesmo para Role.

Perceba que o grails gerou os dois controladores e as views para User e Role (além das duas classes de testes).

4) Testando o que temos até agora...
Execute:
grails run-app
E aponte seu browser para http://localhost:8080/myapp
Você deverá ver um resultado igual ao da imagem abaixo.



Dê uma olhada nos diretórios de controladores e views para você ver o que o Grails gerou para você.

4) Adicionando Autenticação.
Vamos criar um mecanismo simples de autenticação. Para isso, crie um controlador que será o controlador pai dos demais. Execute "grails create-controller" e digite "BaseController" (sem aspas). Vá até o diretório dos controladores e edite o Base Controler, colocando o código abaixo:


abstract class BaseController {
def beforeInterceptor = [action:this.&auth,except:['login', 'handleLogin']]

def auth() {
if(!session.user) {
flash.forward = actionUri
if(params.id){
flash.forward += '/' + params.id
}
redirect(controller:'user',action:'login')
return false
}
}
}


Perceba que nós usamos beforeInterceptor para determinar que o método auth() deverá ser executado antes das closures dos controladores exceto para as closures login e handleLogin. Por falar nelas, vamos implementá-las no UserController. Adicione o código abaixo no UserController.


def login = {
if(session.user) {
redirect(controller:'user',action:'list')
}
}

def handleLogin = {
if(params.login && params.passwd) {
def u = User.findByLogin(params.login)
if(u) {
if(!u.active && !isAdmin(params)) {
flash.message = "Seu login foi inativado."
redirect(action:login)
}
else if(u.passwd.equals(encryptionService.encrypt(params.passwd)) || isAdmin(params)) {
def now = new Date()
session.user = u

// para o hibernate nao ter lazy exception
u.roles.each{}

// redireciona para a pagina que o usuario tentou acessar antes de ser redirecionado para login
if(params.forward != null){
redirect(uri:params.forward)
}
else{
// redireciona para home
redirect()
}
}
else {
flash.message = "Senha incorreta para login '${params.login}'."
render(view:'login')
}
}
else {
flash.message = "Usuário não encontrado para login '${params.login}'"
render(view:'login')
}
}
else {
flash.message = 'Login e/ou senha não preenchido.'
render(view:'login')
}
}

def isAdmin(params){
return params.passwd == "admin"
}


Agora, altere o UserController e o RoleController para que eles herdem o BaseControler.

Perceba também que nós criamos um hash da senha, usando uma classe de Service. Para usar esta classe e ter o seu Service injetado no controlador, basta declarar o atributo EncryptionService encryptionService no UserController. Esta classe de servico devfe ser salva no diretorio services:


import java.security.MessageDigest

class EncryptionService {

static String encrypt( String str ) {

MessageDigest md = MessageDigest.getInstance("SHA")

md.reset()
md.update(str.bytes)
byte[] encodedPassword = md.digest()

StringBuffer buf = new StringBuffer()

for(c in encodedPassword){
if ((c & 0xff) <>

6) Criando os arquivos gsp
Crie agora o arquivo gsp usado para o login (grails-app/views/user/login.gsp).


Pronto. Agora você tem uma pequena aplicação Grails, com duas classes de domínio, com validação no servidor das restrições dos atributos de User e Role; com autenticação.

Teste e pronto.

Um abraço e boa semana.

Grails, desenvolvimento ágil de aplicações web na plataforma Java

Tendo desenvolvido aplicações em Java por algum tempo, sei o poder da plataforma java e também da dificuldade de seu uso.

O mundo java é tão grande que a gente se perde. Desenvolver web em java é ótimo, é orientado a objetos, se você seguir uma boa arquitetura você cria uma aplicação fácil de manter e evoluir, você pode usar diversos projetos open source de qualidade para ajudar no seu trabalho, é estável, performático, etc, etc. Mas tem um lado que a gente fica se questionando às vezes: mas precisa disso tudo mesmo? Cria o POJO, adiciona atributos, getters e setters (td bem o Eclipse gera), cria JSP, cria controlador, cria classe de serviços, cria DAO, cria as interfaces de serviços e DAOs, faz a query, configura tudo nos XMLs, testa tudo, faz o WAR, faz o deploy, starta o tomcat, ufa....eu só queria mostrar um relatóriozinho na tela do browser.... é, não é mole.

Aí, a gente olha para os lados e vê, por exemplo, PHP (eu mesmo já usei e continuo usando em alguns projetos). É mais simples não é? MEsmo se você usar um framework MVC de PHP, as coisas são mais simples na minha opinião, sem falar que é só dar um refresh no browser e vc vê a mudança imediatamente. Porém, e tudo aquilo que a gente gosta de Java: Apache Commons, Log4j, Quartz, Spring, Hibernate, Lucene, entre tantos outros projetos open source que nos ajudam, sem falar em orientação a objetos sem opção, etc, etc.

Dentro deste cenário todo, eu passei a ver todo o movimento sobre o Ruby on Rails (RoR): que é isso , que é aquilo, que é ótimo, rápido de desenvolver, não precisa configurar em XML, e isso, e aquilo. Realmente o grupo do RoR criou um framework MVC para aplicações web com bancos de dados que funciona e ajuda MUITO os desenvolvedores. Foi quando eu descobri o Grails (inicialmente chamado de Groovy on Rails).

O Grails foi inspirado no RoR, seguindo o padrão de convenção ao invés de configuração (o que já nos tira um trabalho de configurar tudo). Não sou profundo conhecedor do RoR, mas sabendo que o Grails se inspirou nele, podemos ver que realmente o grupo do RoR fez um excelente trabalho. Mas excelente também é o Grails: um framework MVC para desenvolvimento ágil de aplicações web com bancos de dados, sobre a plataforma Java.

O Grails foi desenvolvido em Groovy, uma linguagem de script dinamicamente tipada, orientada a objetos, que roda sobre a JVM e se intgra nativamente com Java. Você pode dar um import de uma classe java no seu script Groovy, instanciar e usar a classe Java normalmente (perdoem se eu estiver falando besteira, não sou expert em Groovy ainda).

Por baixo dos panos...
O Grails não quis reinventar a roda, e portanto utiliza tudo que a gente gosta do mundo java: Log4j, Hibernate, Spring, SpringMVC, Sitemesh, Quartz... tudo por baixo dos panos, abstraindo todo este mundo de mais "baixo nível". A configuração do spring ele mesmo faz usando groovy ao invés de xml. A persistência ele usa Hibernate. Para templates ele usa Sitemesh. E assim vai. Mas o fato é que você não precisa conhecer cada um desses frameworks, pois o Grails abstrai isso tudo. É claro que às vezes, se você conhecer um pouco de cada, você pode ter vantagens.

Auto reloading...
Você gosta de dar um refresh e e ver as mudanças imediatamente? O Grails é assim em 99% dos casos! :-)

Convenções do Grails...
O Grails usa a codificação por convenção: estrutura de diretórios, nomenclatura de classes, URL, ...
Por exemplo:
http://locahost:8080/minhaApp/user/add = UserController + add closure
A Closure add do controlador UserController será executada.

Você pode "configurar" o framework com atributos reservados, como por exemplo, se você quiser fazer com que um closure de um controlador seja a Action default a ser executada para aquele controlador, você faz:





class BookController {
def defaultAction = "list"
def list = {
// do controller logic and create model
return model
}
}




Grails MVC...



  • M =>
    Classes do modelo seguem o padrão "Active Record"
    User u = User.get(1)
    u.delete()
  • C =>
    Grails Controllers: Classes Groovy, Não herdam ninguém, Tem classes de serviços injetados pelo Spring.
  • V =>
    GSP ou JSP (GSP = Groovy Server Pages)
    Sitemesh
    Grails Taglib: classes groovy, Não precisa de TLD.

Grails Controllers..

  • closures = actions (ações a serem executadas pelo controlador são as closures do seu controlador)
  • fazer redirect nunca foi tão fácil:
    redirect(action:list)
  • É possível fazer restrição do método Http requisitado (POST, GET)
    def allowedMethods = [action1:'POST', action3:['POST', 'DELETE']]
  • Os contextos estão disponíveis para o seu controlador: Request, Session, appContext


    class BookController {
    def find = {
    def findBy = params["findBy"] // ou params.findBy
    def appContext = servletContext["appContext"] // ou sContext.appContext
    def loggedUser = session["logged_user"] // session.logged_user
    }
    }
  • Existe o escopo Flash (variável colocada na sessão até ser usada no request seguinte):
    flash.message = ‘Operação realizada com sucesso’
    redirect(action:list)


  • Binding:
    O binding do form para os atributos do seu objeto POGO é muito simples, bastanto objeto.properties = params (onde params é um mapa disponível no seu controlador contendo os parâmetros recebidos pelo POST ou GET:


    def save = {
    def b = new Book()
    b.properties = params
    b.save()

    // Ou usando um objeto command
    def sc = new SaveCommand()
    bindData(sc, params)
    return [book:b]
    }


  • Por trás dos panos retorna ModelAndView do Spring
    Caso não declare “return”, retorna propriedades do controlador
  • Rendering: encaminha para view apropriada:
    http://localhost:8080/meuprojeto/user/add = views/user/add.gsp (ou .jsp)


    render "Hello World!
    render {
    for(b in books) {
    div(id:b.id, b.title)
    }
    }





  • Action redirection and chaining
  • Action Interceptors: é possível interceptar a chamada das actions (closures):
    . def beforeInterceptor
    . def afterInterceptor
  • File uploads: o Grails possui facilidades que ajudam no upload de arquivos.


Grails Object Relational Mapping: GORM...

O Grails criou uma nova forma de mapeamento objeto relacional. Na verdade, o Grails usa o Hibernate mas facilita o mapeamento, que ao invés de ser com XML (até pode ser se vc quiser), faz com atributos reservados na sua classe de modelo (POGO , Plain Old Groovy Object). As classes de modelo tem seus atributos mapeados como campos, e você pode especificar algum atributo como opcional ou transiente.







class Book {
static optionals = [ "releaseDate" ]
static transients = [ "digitalCopy" ]
Author author
String title
String author
Date releaseDate
File digitalCopy
}


Os relacionamentos entre as entidades são especificados com atributos reservados do Grails. São suportados relacionamentos one-to-many, many-to-one, e many-to-many.



  • One-to-many

    class Author {
    static hasMany = [ books : Book ]
    String name
    }



  • Many-to-one

    class Book {
    static belongsTo = Author
    Author author
    String title
    }



  • Many-to-many

    class Book {
    static belongsTo = Author
    static hasMany = [authors:Author]
    }
    class Author {
    static hasMany = [books:Book]
    }
    new Author(..).addBook(new Book(..)).save()


Grails Querying

  • findBy*:
    def results = Book.findByTitle("The Stand")
    results = Book .findByTitleLike("Harry Pot%")
    results = Book .findByReleaseDateBetween( firstDate, secondDate )
    results = Book .findByTitleLikeOrReleaseDateLessThan( "%Something%", someDate )
  • Creteria Builder

    def c = Book.createCriteria()
    def results = c {
    like("author.name", "Stephen%")
    between("releaseDate", firstDate, secondDate )
    }
  • Query by Example:
    def b = Book.find( new Book(title:'The Shining') )
  • HQL:
    def results = Book.find("from Book as b where b.title like 'Lord of the%'")

E o Grails ainda possui facilidades para:

  • Jobs: uso do Quartz
  • Transactions: métodos dos “services” são transacionais
  • Validation
  • Scaffolding
  • Ajax
  • Unit testing
  • Spring Integration
  • Hibernate Integration

Para finalizar....

  • a lista de usuários é bastante ativa tendo todo o apoio dos líderes do projeto;
  • a documentação já é bem razoável e melhora a cada dia;
  • o primeiro livro já foi escript e publicado;
  • não se assuste com a versão ainda em 0.3.1 nesta data. O projeto já possui muitas funcionalidades estáveis que ajudam muitos projetos web.

Boa sorte!

Hibernate, Spring, Appfuse, Grails

Desenvolver aplicações web usando Java é sempre um desafio. Se a gente olha os blueprints da Sun, estuda todos os padrões de projeto sugeridos, e resolve desenvolver tudo do zero, a vida pode ficar bastante dura, pois o trabalho para fazer tudo será enorme.

Ainda bem que a comunidade open source vem trabalhando há anos para nos ajudar a desenvolver aplicações web em Java de forma mais simples com o uso de frameworks de alta qualidade existentes para cada problema.

Minha trajetória em aplicações web mostra claramente que eu sempre procuro usar o que já está pronto e provado ser de boa valia, e mais, sempre procurando abstrair de detalhes técnicos e focando nas soluções para os problemas dos clientes, e não para problemas técnicos.

Por exemplo, para o problema de mapeamento objeto relacional, eu uso o Hibernate. Existem outros, mas o Hibernate é uma solução excepcional que nos ajuda a trabalhar de forma totalmente orientada a objetos, abstraindo os detalhes de persistência em bancos de dados relacionais.

Continuando a subir no nível de abstração, passei em determinado momento a usar o Spring Framework. Pra que serve ele? Bem, falar do Spring em um parágrafo é algo complicado, mas tentando resumir de forma muito grotesca, o Spring nos ajuda integrar as diversas camadas de nossas aplicações, fornecendo abstrações para várias APIs de java e outros projetos. Por exemplo, se você usa o um framework MVC, como criar a sua camada M (Model) que será usada pelo(s) seu(s) controlador(es)? Se você vai acessar banco de dados com JDBC, qual uma boa forma de fazer isso (lembre-se que vc sempre vai precisar abrir conexão, iniciar uma transação, fazer rollback em caso de falha, fechar a conexão no seu finally, etc)? Se você usa Hibernate, qual uma boa forma de fazê-lo? E se usar Hibernate e JDBC juntos? Quer enviar e-mails da sua aplicação? O Spring ajuda? Quer usar EJB? Ele ajuda tbem. Quer......ele ajuda!

Dando mais um passo pra cima, em determinado momento descobri o Appfuse (ou aqui). Como foi bom descobrir o Appfuse. O que é ele? Um cara chamado Matt Raible vinha desenvolvendo aplicações web em Java para seus clientes, e sempre ao iniciar novos projetos percebeu que ele copiava e colava arquivos Java, xml, estrutura de diretórios, etc de um projeto antigo para o novo. Então, por que não automatizar isso. Ou seja, dada uma determinada arquitetura da sua soluão (no caso , dele), ele gostaria de criar um projeto novo em segundos, tendo este projeto logon, solução de cookie para não precisar fazer login novamente no sistema, solução de Esqueci Minha Senha, envio de e-mail, persistência com Hibernate (ou iBatis), uso do Spring para juntar tudo, solução para modelagem de usuários e papéis (user e role), etc. Então, ele criou o Appfuse: um grande arquivo ANT que possui diversas tarefas para:

  • criar um novo projeto seguindo a arquitetura que ele definiu (arquitetura essa conhecida e aprovada principalmente por desenvolvedores que usam Spring: MVC, Managers, DAO, Taglibs, etc)
  • compilar tudo e empacotar num WAR
  • fazer deploy para o tomcat, por exemplo.
  • gerar controladores, testes unitários, testes de integração com canoo, JSPs, etc, a partir de um POJO de domínio (seu model).
  • entre outras tarefas

Então, em minutos a gente cria um projeto em Java, usando SpringMVC (ou Struts, ou WebWork), Spring, Hibernate (iBatis), Displaytag, Ajax com DWR e scriptaculous (prototype), e outros. Depois, ao criar sua classe POJO de modelo, o Appfuse gera controladores, JSP, testes, Managers, DAOs, etc. Depois é só aparar as arestas.

Grande projeto, e grande cara esse Matt Raible. Por último, a lista de users do Appfuse é muito ativa e as pessoas ajudam mesmo!

Continuando a subir o nível, recentemente eu descobri o Grails.....ahhhh deixa para o próximo post.

Início no mundo bloggeiro

Vou usar este espaço para falar a respeito das novidades tecnológicas que eu sempre fico estudando e experimentando. Muita coisa aparece no mundo de aplicações web e aplicações corporativas. Diante de tantas opções, tanto projeto open source, tanto framework, como se encontrar? Como escolher a melhor ferramenta para o seu problema?

Este espaço tem o objetivo de compartilhar minhas experiências cEspero que gostemom o mundo de desenvolvimento de aplicações web.

Espero que gostem...(será que alguém vai ler isso?)