Mostrando postagens com marcador grails. Mostrar todas as postagens
Mostrando postagens com marcador grails. Mostrar todas as postagens

Grails e Cloud: desenvolvimento, deploy, execução e monitoramento. Ciclo completo com Grails na Nuvem.

No início havia muitas dúvidas se o Grails iria ser útil para aplicações grandes, ou de grande porte. Mas peraí, o que é uma aplicação grande? eBay é gigante. Se considerarmos uma medida, aí acho que podemos definir grande. Page-views por mês, por exemplo, pode ser uma medida, já que é fácil de saber, de monitorar, etc.

Um eBay tem algo em torno de 15 bilhões de page-views por mês. A globo.com tem uns 2 bilhões de page-views/mês. Um Peladeiro.com.br tem 1,2 milhão de page-views/mês. O Peladeiro é pequeno, Globo.com é grande, eBay é gigante. Pelo menos na minha opinião, sem muito exercício científico.

Além da medida de grande número de acessos, há também: grande volume de dados, necessidade de alta disponibilidade e redundância, escalabilidade, hot swap, cluster failover, etc, etc....

Aí voltamos a pergunta original: o Grails é bom para isso? Dá pra confiar que uma aplicação grails serve para uma demanda de uma aplicação de grande porte?


Alguns casos reais confirmam que a resposta é sim. Por exemplo, o Linkedin.com usa Grails para algumas partes. Este site tem as seguintes características: 22 milhões de membros, 1,2 milhão de novos membros por mês. Acho que o site mesmo, quando vc acessa linkedin.com, não é em grails, apesar de ser em java. Mas há partes feitas em Grails.

Onde eu quero chegar afinal? Meu ponto é que acho que atualmente o Grails chegou aonde queríamos que ele chegasse: está maduro o suficiente, estável, performance boa o suficiente, há ferramentas para desenvolvimento, e agora há serviços prestados por fornecedores que dão suporte ao deploy, execução e monitoramento de uma aplicação grails na Nuvem (Cloud).

Um grande player neste mundo de Cloud é a Amazon, com seu AWS . Mas como desenvolver uma aplicação Grails para rodar neste ambiente virtual? A nuvem está lá, mas você não vê. Tem solução poder computacional escalável, solução de storage, de banco de dados relacional na nuvem, e muito mais....
Mas como usar isso tudo? Como fazer uma aplicação Grails rodar nesta infra poderosa, mas complexa?

Aí que eu acho que a SpringSource está dando um show! Primeiro os caras inventam o Spring, que é solução para DESENVOLVIMENTO. Depois começam a expandir para servidores, que é solução para EXECUÇÃO. Depois fornecem solução para ferramentas ajudando mais ainda no quesito DESENVOLVIMENTO. Aí lançam produtos para gerenciar e monitorar (MONITORAMENTO). Por último, lançam um serviço que te ajuda executar aplicações java na infra de Cloud da Amazon: Cloud Foundry.

E por último, os caras compraram a G2One, que era a empresa responsável por Groovy e Grails. O resultado é: Grails tá pronto para rodar em tudo isso citado aí em cima.

Portanto, no momento presente, você pode desenvolver uma aplicação Grails usando tudo que este excelente framework oferece, pode usar os plugins de eclipse para Groovy e Grails que a SpringSource está desenvolvendo, pode fazer deploy, execução e monitoramento da applicação na nuvem da Amazon usando o CloudFoundry (ou o Cloud Foundry Plugin pra grails). Isso é excelente!!! Sua aplicação grails fica escalável, podendo usar instâncias com balanceamento de carga (load-balance) em diferentes zonas de disponibilidade, pode usar replicação master-slave para seu Mysql, pode usar SSL no apache, monitoramento e restart automático de servidores que falham, auto-escala de instâncias do EC2 da Amazon (pagando apenas pelo o que usa), pode usar backup automático de dados com snapshots.......... tudo isso para uma aplicação grails rodando na nuvem, e você gerenciando via Web.

Na teoria é lindo. Temos que ver na prática agora.
t+

Enviando emails formatados com Html e CSS - será que vai certinho?

Muito bom esta tabela que mostra o que cada cliente de email consegue mostrar corretamente em termos de CSS nos emails Html que vc envia.

http://www.campaignmonitor.com/css/

Ou seja, não adianta referenciar um arquivo CSS na tag link lá no header, pois nem todos os clientes levam isso em consideração. E nem tag style no header, e nem..... olhe o link acima.

Abcs
Felipe

Serviços do Grails, Transações e Rollback automático

Comprei o livro recente que saiu do Glen Smith, o Grails in Action. E, claro, comecei a ler. Ainda não terminei. Mas já valeu a compra e a dedicação de ler só pelo fato que eu descobri e não sabia:


Nos serviços do grails ( classes *Service.groovy, dentro do diretório services), que possuem Transação Declarativa , o default é cada método ser transacional. Isso significa que uma transação será iniciada antes do método do serviço começar a ser executado, e será finalizada (commit ou rollback) ao final.

O rollback acontece automaticamente quando uma Exception é lançada. O que eu não sabia era que apenas Runtime Exceptions fazem o rollback acontecer. Checked Exceptions não. Isso mudou minha vida! :-)

Eu fazia um try catch no controller, às vezes, pegando o tipo Exception no catch. Grave erro conceitual de cara né, mas pior ainda, pois eu estava contando com o rollback que poderia não acontecer.

Então amigos: apenas classes derivadas de RuntimeException fazem o rollback acontecer.

Abcs

Manubia.com.br - Controle Financeiro com Grails e, agora, com jQuery

É pessoal....

depois de algum tempo, tempão mesmo, usando a dupla Prototype + Scriptaculous.....depois de saber usar bem esta dupla, de ler livro e tudo..... resolvi abandonar totalmente a dupla e me render ao jQuery.

Deu um trabalho danado migrar o Manubia.com.br (site de controle financeiro) para o jQuery. Tem muita coisinha diferente. Mas no final das contas, estou gostando muito.

Alguns dos motivos de ter feito a migração:

  • jQuery tem um mundo plugins que resolvem diversos problemas que a gente precisa ficar catando pela web quando usamos Prototype + Scriptaculous.
  • o jQuery tem um projeto inteiro dedicado a interface gráfica: jQuery UI
  • formatação de campos de um formulário (formatar valor numérico com pontos e vírgulas, de forma internacionalizada): Masked Input Plugin
  • Calendário tipo Date Picker para campos de datas: jQuery UI DatePicker
  • Plugin para manipulação de comboboxes (select boxes).
  • No final das contas, a gente escreve menos código JavaScript do que com Prototype. Parece que fica mais limpo o código.
  • Tem um site dedicado aos plugins
  • De acordo com alguns benchmarks (um aqui, outro aqui), o jQuery tem uma performance melhor do que o Prototype (ou seja, os usuários é que ganham).
  • Eu tenho tudo que preciso em uma biblioteca só, não preciso usar parte de uma, parte de outra, etc... (pelo menos por enquanto, vamos ver até quando).
É isso. Acho que o Manubia ganhou com este investimento que fiz... No fundo mesmo, eu precisava era do formatador numérico para campos input. Aí achei o plugin de jQuery...isso foi a gota d'água pra eu migrar. (Eu já tava p da vida por estar usando o date picker do Open Rico)

Abcs
Felipe

Filtro do Grails para impor SSL (https) a determinadas páginas

Olá pessoal,


hoje andei pesquisando a respeito de uso de certificados SSL com uma aplicação grails. Afinal, está chegando a hora de impor o uso de SSL (https) meu novo site de controle financeiro, o Manubia.com.br .

Então ao pesquisar o assunto, encontrei uma classe de um grails Filter que pode me ajudar muito em forçar determinadas URLs a usarem SSL e outras não. Por exemplo, uma página que mostra dados não confidenciais, não precisa usar SSL (https). Já uma página que mostra ou recebe um formulário confidencial DEVE usar SSL.

Aí vai o Filter. Ainda não testei, mas parece certinho.

Abcs

/**
* Created by IntelliJ IDEA.
* User: danielhonig
* Date: Jan 23, 2009
* Time: 4:27:10 PM
* To change this template use File | Settings | File Templates.
*/

import org.codehaus.groovy.grails.commons.ConfigurationHolder

class SSLFilters {
// Set these to your HTTP/HTTPS ports
def defaultPortHTTP = 80
def defaultPortHTTPS = 443

static def SSLRequiredMap = ConfigurationHolder.config.SSLRequiredMap

def shouldUseSSL(controllerName, actionName) {
// User actions use SSL
if (SSLRequiredMap[controllerName]!=null) {

//If * then all actions are secured
if (SSLRequiredMap[controllerName][0] == '*')
return true

//else look for specific action
if (SSLRequiredMap[controllerName].contains(actionName))
return true
}
false
}

def filters = {
sslFilter(controller: "*", action: "*"){
before = {
def controller = controllerName
def action = actionName
log.debug("SSLFilter: ${params?.controller}.${params?.action}")
def url = null

// SSL off by default
def useSSL = shouldUseSSL(controllerName,actionName)

log.debug("${controller}.${action}, useSSL:$useSSL")

// Get redirected url
url = getRedirectURL(request, params, useSSL);

// If redirect url returned, redirect to it
if (url) {
redirect(url: url)
return false;
}

return true;
}
}
}

def getRedirectURL = {request, params, ssl ->
log.debug("getRedirectURL: $ssl")

// Are we there already?
if (request.isSecure() == ssl)
return null;

def protocol
def port

// Otherwise we need to flip
if (ssl) {
protocol = 'https'

// If using the standard ports, don't include them on the URL
port = defaultPortHTTPS
if (port == "443")
port = ""
else
port = ":${port}"
}
else {
protocol = 'http'
// If using the standard ports, don't include them on the URL
port = defaultPortHTTP
if (port == "80")
port = ""
else
port = ":${port}"
}

log.debug("Flip protocol to $protocol")

def url = request.forwardURI
def server = request.serverName
def args = paramsAsUrl(params)
url = "$protocol://$server$port$url$args"

log.debug("getRedirectURL - redirect to $url")

return url;
}

def paramsAsUrl = {params ->
int ii = 0
String args = ""
params.each
{k, v ->
if (['controller', 'action', 'id'].find {k == it})
return;

if (ii)
args += "&"
else
args += "?"

args += "$k=$v"
ii++
}

return args

}
}

Controle Financeiro e Planejamento Financeiro com Grails e Prototype

Mês passado eu e um grande amigo, e meu sócio neste projeto, lançamos um site para controle financeiro pessoal: www.manubia.com.br


O site foi todo desenvolvido com Grails e a dupla Prototype/Scriptaculous.

Tenho me questionado se não deveria mudar para jQuery. Eu vi um benchmark e acabo ficando na dúvida. Alguém prefere o jQuery ao invés do Prototype?

Em fim..... o sistema do Manubia foi ótimo de ser feito com o Grails. É incrível a produtividade do Grails, todos os plugins (estou usando authentication plugin, mail plugin e o mail-confirmation plugin). E bastante efeito visual e ajax com o Prototype e o Scriptaculous. Tá ficando legal.

Sobre o sistema em si, é um sistema de controle financeiro, onde você pode inserir suas transações, ou importar arquivo do seu banco, e em seguida pode categorizar todos os seus gastos. Feito isso, o sistema apresenta gráficos muito interessantes comparando seus gastos ao longo dos meses, resumo de suas contas , entre outros. É muito interessante pois assim você tem visibilidade dos seus gastos, e pode, portanto, fazer um planejamento financeiro para a sua família.

Eu importei meus dados dos últimos 4 meses, e foi ótimo pois consegui, junto com minha esposa, descobrir nossos ralos financeiros. Assim poderemos fechar esses ralos e poupar um pouco mais...

Abcs
Felipe

Logging com Grails 1.1

Hoje fiquei tentando entender a configuração do Logging no grails 1.1...pois mudou um pouco.

Tem um bom exemplo aqui, que eu reproduzo abaixo.


log4j = {

appenders {
console name:'console', threshold:Level.ERROR,
layout:pattern(conversionPattern: '%p %d{ISO8601} %c{4} %m%n')
rollingFile name:"rollingFileTrace", threshold:Level।TRACE, maxFileSize:1048576,
file:logFile+'Trace।log', layout:pattern(conversionPattern: '%p %d{ISO8601} %c{5} %m%n')
rollingFile name:"rollingFileDebug", threshold:Level।DEBUG, maxFileSize:1048576,
file:logFile+'Debug.log', layout:pattern(conversionPattern: '%p %d{ISO8601} %c{5} %m%n')
rollingFile name:"rollingFileError", threshold:Level.ERROR, maxFileSize:1048576,
file:logFile+'Error.log', layout:pattern(conversionPattern: '%p %d{ISO8601} %c{5} %m%n')
}

error console, rollingFileDebug, rollingFileError, rollingFileTrace:
'org.codehaus.groovy.grails.web.servlet', // controllers
'org.codehaus.groovy.grails.web.pages', // GSP
'org.codehaus.groovy.grails.web.sitemesh', // layouts
'org.codehaus.groovy.grails."web.mapping.filter', // URL mapping
'org.codehaus.groovy.grails."web.mapping', // URL mapping
'org.codehaus.groovy.grails.commons', // core / classloading
'org.codehaus.groovy.grails.plugins', // plugins
'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration
'org.springframework'

debug rollingFileDebug, rollingFileTrace:'org.hibernate',
'com.britetab',
'BootStrap',
'org.apache.ddlutils'

trace rollingFileTrace:'org.hibernate.SQL',
'org.hibernate.type',
'flex.messaging.services'

warn console,rollingFileDebug,rollingFileTrace:'org.mortbay.log',
'org.hibernate.tool.hbm2ddl'

root {
trace 'rollingFileTrace'
debug 'rollingFileDebug','rollingFileTrace'
error 'console','rollingFileError','rollingFileDebug','rollingFileTrace'
additivity = true
}
}

Detalhe do Grails com Eclipse

Pessoal

eu já passei maus bocados uma vez por causa da questão que irei escrever aqui. Hoje, meu companheiro de desenvolvimento João Paulo passou pelo mesmo problema...Depois de algum tempo ele resolveu me perguntar, e lendo o erro que ele me passou, não deu outra, era o problema que já passei.

Quando você ler um erro como este:

No signature of method: Requestmap.save() is applicable for argument types: () values: {}

(é claro que no seu caso a sua classe de domain não será Requestmap, será Pessoa, Categoria, Produto, sei lá o que)

Uma das possíveis causas disso é uma configuração no eclipse, que faz com que o Eclipse compile as classes groovy, gerando os arquivos .class . Neste caso, o grails, ao rodar a aplicação com "grails run-app"
, ele também compila, aí tudo se perde.

Não deixe de configurar o Eclipse para não compilar os arquivos de groovy. Para isso:
- Clique com botão direito no seu projeto, no eclipse
- Selecione "Groovy project Properties"
- Marque a opção "Disable Groovy Compiler Generating Class Files"

Abcs
Felipe

Grails 1.0.1 e many-to-many com GORM DSL

Oi pessoal,

primeiro queria divulgar o forum do GrailsBrasil ( www.grailsbrasil.com ). Um ótimo espaço para os brazucas, que cada vez mais descobrem e se encantam com o Grails.

Em seguida, queria falar, com certo atraso, da tão esperada Release 1.0 do Grails, que na verdade já está na 1.0.1. Isso é uma grande notícia.

Por último, a razão deste post....

O Grails 1.0.1 possui o GORM DSL, que oferece grande facilidade para controlar detalhes do mapeamento objeto relacional dos objetos de domínio. Ao migrar uma aplicação do grails 0.6 para o 1.0.1, quis alterar alguns detalhes do mapeamento, já que agoraeu conseguiria isso sem precisar apelar para mapeamento Hibernate com XML.

O problema que eu queria resolver era:
Digamos que eu tenha uma classe Usuario e outra Privilegio, com um relacionamento many-to-many entre elas. Logo teremos:


class Usuario {
String nome
static hasMany = [privilegios:Privilegio]
}

class Privilegio {
String nome
static hasMany = [usuarios:Usuario]
static belongsTo = Usuario
}

Com as classes acima, o grails gera as seguintes tabelas:
usuario
id
nome

privilegio
id
nome

usuario_priv
usuarios_id (que é uma FK para privilegio.id)
privilegios_id (que é uma FK para usuario.id)
É isso mesmo: o campo usuarios_id é uma chave estrangeira para o id da tabela privilegio; e o campo privilegios_id é uma chave estrangeira para o id da tabela usuario. A explicação pra isso está aqui (vejam o comentário do Graeme).

Eu não sei quanto a vocês, mas pra mim isso soa muito estranho. Mas é assim mesmo que o grails gera por padrão. A forma que eu gosto de trabalhar é com campos no singular e com o nome do campo da chave estangeira condizente com o nome da tabela de referência:
usuario_priv.usuario_id = usuario.id
usuario_priv.privilegio_id = privilegio.id

Para isso, então, eu antigamente teria que fazer o mapemanto com XML do hibernate (já fiz vários para outras aplicações com grails). Mas agora na versão atual posso fazer o que quero com o GORM DSL. E é isso que quero mostrar, pois há um detalhe que precisei fazer alguns testes até entender o comportamento do Grails com essa DSL. Vamos lá...

class Usuario {
String nome
static hasMany = [privilegios:Privilegio]

static mapping = {
privilegios column:'usuario_id', joinTable:'usuario_priv'
}
}


class Privilegio {
String priv
static hasMany = [usuarios:Usuario]
static belongsTo = Usuario

static mapping = {
usuarios column:'privilegio_id', joinTable:'usuario_priv'
}
}


O que eu acho curioso e anti-intuitivo no código acima, é que numa primeira tentativa eu coloquei:
class Usuario {
static mapping = {
privilegios column:'privilegio_id', joinTable:'usuario_priv'
}
}
Mas isso acabou sendo errado. Na verdade temos que colocar como nome da coluna (para ter o resultado que eu esperava), o nome da classe em que me encontro, e não o nome do atributo de relacionamento. Em fim: o correto é o código acima completo das classe Usuario e Privilegio, e não o código acima com fundo avermelhado.

Fiz questão de colocar isso aqui, pois a documentação não me parece muito clara quanto a isso. (talvez eu é que tenha entendido errado a documentação, sei lá....em fim....tá aí pra quem precisar deste detalhe).


Abcs
Felipe

Grails, Gravl, SAP e REST intro

Oi pessoal,

hoje eu vou fazer um pequeno post com algumas coisas que achei interessantes recentemente.

Primeiro, nosso amigo Glen Smith, novamente, faz um desafio a si mesmo em benfício da comunidade Grails. Essas comunidades open source são demais mesmo....

O Glen está implementando um pequeno sistema de blog, chamado de Gravl, em no máximo 1000 linhas de código (LOC), para mostrar algumas funcionalidade do Grails e seus plugins. Ele está usando algumas das últimas novidades das recentes versões do Grails, como Web Flow, GORM DSL, URL Mappings, RSS Plugin, Searchable Plugin, Ajax Upload com barra de progresso, entre outras coisas. O código fonte está disponível como um projeto open source do google code, e seu objetivo é ser uma aplicação de exemplo do Grails. Vale conferir. ( veja outros posts dele sobre este tema ).

Segundo, a SAP anunciou que estará usando o Grails 1.0 para o seu produto Composition on Grails, que é uma aplicação que provê facilidade para o rápido desenvolvimento de "aplicações compostas"(composite applications) no SAP NetWeaver 7.1 CE. Esta notícia é muito interessante por colocar o Grails como a opção escolhida por uma corporação de peso como a SAP para um de seus produtos. Excelente notícia para a comunidade Grails. Veja o post do Grame Rocher sobre isso.

E em terceiro, (sem ter uma relação direta com grails, mas que me fez pensar muito nas facilidade que o grails tem a respeito de REST) li outro dia um artigo introdutório sobre REST no InfoQ, que foi muito bom apesar de eu já ter lido outros sobre este tema. Mostra de maneira simples uma possível implementação dos conceitos de REST, e reforça esta alternativa light ao mundo de web services. E só pra não deixar passar, veja as facilidades para REST que o Grails tem (veja item 13.1).

Até a próxima...

Grails muito perto da release 1.0

Olá,

depois de tanto tempo lonje do meu blog, infelizmente, volto a campo para comentar algumas coisas.

Primeiro, estou tão longe assim devido (eu sei que não é desculpa, mas...) ao trabalho e muito também a minha filha. Manuela completará 1 ano daqui a duas semanas. E este primeiro ano de pai de filha única e menina, realmente eu fiquei muito babão e dedicando todo meu tempo livre a ela.

Em fim....

Depois de um período de trabalhos de consultoria, um pouco longe da mão na massa, tenho voltado a codificar, e naturalmente usando o Grails. Tendo usado o grails desde a versão 0.3, e isso já faz quase um ano, e passando pelas migrações para as versões 0.4.1, 0.4.2, 0.5 e agora 0.6, percebo o quanto valeu a pena ter escolhido o Grails para novas aplicações quando ainda era 0.3 Na época existia um questionamento muito grande se eu deveria ou não usar o Grails 0.3, quando ainda era muito no início do framework, quando a cada nova release era necessário alguma reengenharia devido a mudanças de API (eu senti muito migrande da 0.4.2 para a 0.6). Hoje, agradeço a mim mesmo por ter apostado no Grails naquela época.

Tenho uma aplicação que começou a ser desenvolvida lá traz com 0.3, e hoje roda em produção com grails 0.6 (ainda não migrei para 1.0-RC1). Estou muito satisfeito com a decisão de usar o grails desde o início, pois o granho de produtividade foi tanto, que hoje quando precisamos dar manutenção, evoluir e criar novas funcionalidades, penso em como seria custoso se não estivessemos usando o Grails. Acho que seria caro demais para meu cliente, ou então usaríamos uma solução em PHP (neste caso, deste cliente).

O Grails está na versão 1.0 RC1, faltando poucas semanas para a release final 1.0, finalmente. Muitas pessoas, e principalmente empresas, só irão assumir a aposta num novo framework caso ele tenha um release 1.0 estável. E agora o Grails já está quase lá.

A quantidade de facilidades que o grails 1.0 nos dá é enorme:
- Facilidades para Web Services (REST ou SOAP)
- Diversos plugins. Destaques para:


Não dá nem para citar tudo aqui. Só lendo mesmo, e principalmente, experimentando.

Falando em "lendo", numa discussão há algumas semanas, o pessoal da lista colocou a opinião de que era fundamental para uma boa adoção do grails (corporativa principalmente) a existência de uma documentação MUITO melhor, pois o site era razoável apenas. Em resposta a isso, o pessoal da G2One (empresa criada para dar suporte ao Groovy e Grails) escreveu o primeiro draft do Reference Guide. Que para uma primeira versão, está muito bom. Vale a leitura completa para conhecer diversos detalhes do framework.

O que me motivou para escrever este post de hoje foi uma mensagem enviada para a lista pelo Matt Raible. Ele é um consultor/arquiteto/desenvolvedor Java, com bastante experiência, e criador do AppFuse. Já usei muito o AppFuse, e se não fosse o Grails, estaria usando até hoje.
O Matt Raible mandou uma mensagem perguntando algumas coisas sobre performance do Grails, sobre o uso do FreeMarker com o Grails, sobre a possibilidade de criar um Struts2 plugin para o grails, e outras coisas. Ele perguntava essas coisas pois queria ter mais argumentos para conseguir "vender" o grails para algumas emoresas em que ele está prestando consultoria, empresas essas que usam essas tecnologias e cujos diretores gostariam de manter a tecnologia em uso para os próximos projetos. Os argumentos e dúvidas do Matt são de que o Grails permitiria a continuidade do uso dessas tecnologias, e ainda oferece um desenvolvido mais produtivo, etc, etc...

Depois da release 1.0, com o nível de documentação que se está alcançando, com a comunidade ativa que o grails tem hoje...... Aí, quando eu leio o Matt defendendo o uso do Grails em empresas que ele dá consultoria, sinceramente é o último argumento que eu precisava. Não tem mais volta. Pra mim, aplicações web na plataforma java, só em grails.

Ah, em relação a minha aplicação que citei acima. É um site que tem mais de 200.000 usuários cadastrados, q tem 31.000 visitantes por dia, gerando aproximadamente 400.000 page views por dia. Há controle de login, processamento de imagens, envio de emails, módulo administrativo, geração de relatórios, etc, etc.
Vejam por si só:
http://www.noiteuniversitaria.com.br

Grande abraço e até a próxima.

Grails e ImageMagick, poderio em manipulação de imagens

Recentemente tive que lidar, mais uma vez, com manipulação de imagens numa aplicação web. O requsito era o clássico: usuário faz upload de uma imagem que a gente não sabe as dimensões, e a aplicação precisa criar um thumbnail (aquela versão bem reduzida da imagem para apresentar numa página junto com diversos outros elementos de página), e também um versão da imagem em tamanho grande pré-determinado.

Mas desta vez havia requisitos novos:
1) Era preciso adicionar uma marca d'água à foto grande, e;
2) Deveria ser possível fazer um upload de um arquivo zip com dezenas de fotos dentro, e a aplicação precisava processar todas as imagens do zip, criando o tumbnail, a foto grande e a marca d'água.

Uma das opções que surgiram no meio do caminho apareceu quando surgiu este assunto na lista do Grails e alguém fez com que eu conhecesse o ImageMagick. Esta biblioteca é uma biblioteca nativa de processamento de imagens...como se fosse um photoshop, porém sem interface gráfica, apenas por linha de comando. E o mais interessante é que ela realmente possui diversas funcionalidades de um photoshop, como filtros, transformações, conversões, ou seja, realmente edição de imagem. Vale a pena conhecer. Veja alguns exemplos aqui. Para exemplos de uso da API, veja aqui.

Ok....então passamos a conhecer o ImageMagic (IM). Ah, antes que eu esqueça, existem várias intefaces de acesso ao IM para várias linguagens. Há inclusive o JMagick, que é a API java para o IM. Porém, pesquisando a respeito no google a gente vê que em diversas situações o JMagick crasha (crash). Então, se a gente não pode ainda, ou não deve, usar o JMagick, a saída é usar o IM nativamente ( que deve ser o que o JMagick faz, mas não sabemos o que fizeram lá para ele entrar em crash né).

Portanto, finalmente, este post tem então o objetivo de mostar uma action de um controller grails que eu fiz num micro projetinho grails só pra testar o uso do IM usando groovy e executando o IM nativamente, seguindo as dicas do nosso amigo James Page da lista do grails.

O primeiro passo é instalar o ImageMagick na sua plataforma. Em windows há o instalador através de um arquivo .exe. No linux há o RPM. E é claro, você pode instalar a partir do fonte do ImageMagick também.

Depois de instalar, você pode testar facilmente pela linha de comando. Se funcionou, vamos para o mundo web com nosso grails.

Eu criei um projeto novo só pra testar. Para isso:

grails create-app


Chamei este projeto de "img". Após criar, vá para o diretório do projeto e

grails create-domain-class


Chamei esta classe de domínio de TaskFile (só porque em outro projeto eu tenho uma classe Task que possui diversos TaskFile).

class TaskFile { 
String name
Date uploaded = new Date()
String description
}



Em seguida gerei o controlador TaskFileController (com comando "grails generate-controller").

import org.springframework.web.multipart.MultipartFile;

class TaskFileController {
def index = { redirect(action:list,params:params) }
// .... todas as actions que o grails gera. Alterei a action save
// alem de alterar, criei metodos privados
def save = {
def taskFile = new TaskFile()
taskFile.properties = params

if(taskFile.save()) {
def f = request.getFile('taskFile')
if(!f.empty) {
def fileName = 'file_' + taskFile.id + getExtension(f)
f.transferTo( new File('imgs/' + fileName) )
processImg(fileName)
}
redirect(action:show,id:taskFile.id)
}
else {
render(view:'create',model:[taskFile:taskFile])
}
}

private void processImg(fileName){
final String original = 'imgs/' + fileName
def t = Thread.start {
def cmd = createCmd(original, '-thumbnail', createDimentions(100,0) , 'imgs/tumb_' + fileName)
def process = cmd.execute()
process.waitFor()

def cmdMain = createCmd(original, '-thumbnail', createDimentions(400,0) , 'imgs/main_' + fileName)
def processMain = cmdMain.execute()
processMain.waitFor()

def waterMarkCmd = ["cmd /c composite -compose atop watermark.png", 'imgs/main_' + fileName, 'imgs/wm_' + fileName]
waterMarkCmd.join(" ").execute()
}
}

private String getExtension(MultipartFile file){
String contentType = file.getContentType();
String fileExtension = null;
if (contentType.equalsIgnoreCase("image/pjpeg") || contentType.equalsIgnoreCase("image/jpeg")) {
fileExtension = ".jpg";
}
else if (contentType.equalsIgnoreCase("image/gif")) {
fileExtension = ".gif";
}
else if (contentType.equalsIgnoreCase("image/x-png")) {
fileExtension = ".png";
}

return fileExtension;
}




String createCmd(inpath, action, options ,outpath){
//@todo Need os specific code here... Remove c for Linux.....
def cmd = ['cmd','/c', 'convert', inpath, action , options, outpath]
return cmd.join(" ");
}

String createDimentions(width, height){
StringBuffer sb = new StringBuffer();
if(width > 0)
sb.append(width);

sb.append('x');
if(height > 0)
sb.append(height)

return sb.toString()
}
}





Percebam que no código do controlador, o interessante foi o uso de uma Thread separada para o processamento das imagens. Já que processar imagens pode levar algum tempo em determinados casos, não é necessário deixar o usuário final esperando isso. Assim, criei uma nova thread que faz este trabalho enquanto a thread do request já manda o response direto pro usuário. Neste exemplo, eu faço 3 coisas: salvo o arquivo original, crio o thumbnail, crio a imagem grande que chamei de main, e por último crio a imagem grande com a marca d'água (watermark). Tudo isso é feito no método processImg(fileName). Percebam que este método faz uso do método execute() do groovy, que executa um comando no sistema operacional e retorna um java.lang.Process.

Depois disso foi só gerar as views (grails generate-views). Alterei a create.gsp para que tivesse o upload do meu arquivo:

           <g:form action="save" method="post" enctype="multipart/form-data">
<div class="dialog">
<table>
<tbody>



<tr class='prop'><td valign='top' class='name'><label for='description'>Description:</label></td><td valign='top' class='value ${hasErrors(bean:taskFile,field:'description','errors')}'><input type='text' name='description' value="${taskFile?.description?.encodeAsHTML()}" /></td></tr>

<tr class='prop'><td valign='top' class='name'><label for='name'>Name:</label></td><td valign='top' class='value ${hasErrors(bean:taskFile,field:'name','errors')}'><input type='text' name='name' value="${taskFile?.name?.encodeAsHTML()}" /></td></tr>

<tr class='prop'><td valign='top' class='name'><label for='taskFile'>taskFile:</label></td><td valign='top' class='value ${hasErrors(bean:taskFile,field:'uploaded','errors')}'><input type="file" name="taskFile" /></td></tr>

</tbody>
</table>
</div>
<div class="buttons">
<span class="formButton">
<input type="submit" value="Create"></input>
</span>
</div>
</g:form>


E só isso. Até a próxima. Abcs.

Grails plugins mostrando sua força

O projeto Grails lançou recentemente sua versão 0.5.
A evolução tem sido bastante rápida, com o objetivo de chegar na versão 1.0 até o fim deste ano. Já dise uma vez e vou dizer de novo: estar na versão 0.5 ( ou qualquer uma que ainda não seja 1.0) não significa que não possa ser usado para sistemas em produção, significa apenas que a API pode ser alterada ao longo do caminho, forçando as aplicações a se atualizarem junto com a evolução do Grails.

Uma das novidades recentes é a API de Plugins do Grails....ok ok, eu sei que já falei disso.

Neste post que quero apenas comprovar como o Grails Plugins pode ser bastante poderoso. O Graeme Rocher enviou recentemente um post no seu blog demonstrando a beleza dos plugins ao criar um plugin para integrar o Wicket ao Grails.

Vale a pena olhar para quem tem interesse neste crescente projeto que é o Wicket.

Abcs
Felipe

Grails: Controller para download de arquivo em diretório

Eu tenho colocado ultimamente posts que são pequenos e possuem código fonte de algumas pequenas coisas. Hoje vou colocar aqui uma action de um controller que faz download de um arquivo qualquer de um diretório no sistema de arquivos.
Eu acho interessante ter estes pequenos posts de exemplos pois servem de referência para dúvidas futuras, inclusive minhas. Alguém acha ruim este tipo de post? Preferem os posts mais elaborados (porém demandam mais tempo né)?
Em fim, segue o código.

def downloadBook = {
def file = new File("c:/temp/onliso.pdf")
response.setHeader("Content-Type", "application/octet-stream;")
response.setHeader("Content-Disposition", "attachment;filename=\" onlisp.pdf\"")
response.setHeader("Content-Length", "${file.size()}")
response.outputStream << file.text.bytes
}


É simples ou não é desenvolver vom grails e groovy. Olha o tamanho deste código. QUal seria a alternativa em Java? Quantas linhas a mais? Não quero criticar não, eu adoro Java, mas ter uma linguagem de script como Groovy na plataforma Java é muito bom, MUITO BOM.

Até a próxima.
[]s
Felipe

Bind de coleção de objetos com Grails

Muitas vezes os frameworks ágeis de programação web como Grails e Ruby on Rails ajudam muito nas operações CRUD simples. Você consegue rapidamente fazer criar, obter, alterar e apagar objetos de domínio com forms simples. Mas nas aplicações reais, fora dos "Hello World" usados nos exemplos, a gente se depara algumas vezes com a necessidade de fazer operações de CRUD em forms mais elaborados. Às vezes há casos que queremos reunir mais de um objeto de domínio num mesmo form, e há outros casos em que queremos reunir o mesmo objeto de domínio mais de uma vez: um exemplo seria salvar vários objetos Endereço de uma só vez.

Há uma maneira simples, usando o Grails, de se fazer o bind de uma coleção de objetos do mesmo tipo. Vamos a ela:

Neste exemplo, digamos que a gente queira salvar vários objetos Person de uma só vez, recebendo um post de um form único.

Crie um objeto helper em Groovy (um objeto comum que irá lhe ajudar nesta tarefa) como se segue:

class MyForm {
def personList = [new Person(), new Person()]
}



No arquivo GSP que terá o form, faça assim:
<input name="personList[0].firstname" value="Fred" />
<input name="personList[0].surname" value="Jones" />
<input name="personList[1].firstname" value="Bob" />
<input name="personList[1].surname" value="Smith" />


No controlador que irá receber o POST, você só precisará fazer o bind desta forma:
def form = new MyForm()
bind(form, params)



Pronto, simples assim. Desta forma você continua aproveitando o BIND automático do Grails (no fundo é Bind do Spring) e simplificando o código do seu controller.

[]s
Felipe