tag:blogger.com,1999:blog-44473584007926459902024-02-07T02:24:30.577-03:00FelipenascAplicações web com Java, Grails e Ajax, baseadas no mundo open sourceUnknownnoreply@blogger.comBlogger58125tag:blogger.com,1999:blog-4447358400792645990.post-38954171447571911402013-12-19T21:27:00.002-02:002013-12-19T21:27:29.761-02:00Acessando o web service de Nota Fiscal de Servicos Eletronica (NFS-e Nota Carioca) em JavaDepois de bater bastante cabeça para conseguir dar um primeiro passo na integração com o sistema de Nota Fiscal de Serviços Eletrônica (NFS-e) do município do Rio de Janeiro (Nota Carioca), vou colocar aqui o passo a passo que eu segui para contribuir com outras pessoas que possam estar passando pelo mesmo martírio.<br />
<br />
Não encontramos muita informação detalhada por aí. A conclusão que cheguei é que quem conseguiu fazer a integração não quer divulgar como fez, pois provavelmente a implementação faz parte de algum produto comercial sendo vendido, e portanto fornecer estas informações gratuitamente vai contra a empresa.<br />
<br />
Como este não é o meu caso, pois eu não vendo este serviço, estou apenas integrando para poder emitir NFS-e automaticamente com o Peladeiro.com, então resolvi contribuir. Aí vai.....<br />
<br />
<br />
<b>1- Gerar as classes Java que representam os XML consumidos pelo Web Service da prefeitura:</b><br />
<br />
Meu primeiro passo foi gerar as classes Java que representam os arquivos XML definidos pela prefeitura. Para isso, baixei o XML Schema (XSD) da versao que a prefeitura usa. Baixei do site da ABRASF em <a href="http://www.abrasf.org.br/">http://www.abrasf.org.br/</a><br />
<br />
Neste site, procurar na página de "<a href="http://www.abrasf.org.br/paginas_multiplas_detalhes.php?cod_pagina=1&titulo=TEMAS%20T%C9CNICOS&data=nao">Nota Fiscal de Servicos Eletronica</a>" o XSD correspondente. No meu caso foi o tipos_nfse_v01.xsd<br />
<br />
Gerar as classes Java a partir do XSD: (<a href="http://docs.oracle.com/javase/6/docs/technotes/tools/share/xjc.html">http://docs.oracle.com/javase/6/docs/technotes/tools/share/xjc.html</a>)<br />
<br />
Para isso, executei o comando:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw-70TcYWB0KAGIjryR2SIAowaFlgQty7CAoeZl1lveT1bZjv40OKGnKlwnHSvYPTIiwStMrWmIeHCByniYrfBQ7EhcKQ5g56R7chfnxhN4sLFwSgSaPdcBbGIBWTYDLEbLtEozK4Z7KfI/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> xjc -p com.meusistema.nfse.binding -d C:\Users\Eu\projetos\meunfse\src\java\com\meusistema\nfse\binding C:\Users\Eu\projetos\meunfse\web-app\WEB-INF\tipos_nfse_v01.xsd
</code></pre>
<br />
Isso gerou um monte de classes Java, cada uma representando os tipos definidos no XSD. Essas classes serão usadas para preencher com os dados a serem passados para o web service (e retornados tbem). Após usar as classes para preencher com os dados, por exemplo setCpf("34234234");, usei o JAXB para gerar a string XML a ser passada para o web service.... mas vamos chegar lá...<br />
<br />
<b>2- Gerar as classes Java de Cliente do Web Service através do WSDL da prefeitura.</b><br />
<br />
O passo seguinte foi gerar as classes Java que representam o Client do web service. A prefeitura fornece o WSDL. No meu caso ficava aqui: <a href="https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl">https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl</a><br />
<br />
Só que para conseguir acessar este WSDL, foi preciso eu ter o certificado digital. Sem o certificado, nada feito. Então, comprei um certificado da Certisign, do tipo NFS-e A1. É um processo chato, pois você compra no site, mas depois tem que marcar um dia para ir até lá para entregar a documentação da empresa, dos sócios, do representante, etc.... No final, você sai com um número, acesso um site, e com este número faz o download do certificado meucert.pfx (é um certificado com extensão PFX).<br />
<br />
Instalei o certificado na minha máquina (windows 7), e através do browser eu consegui acessar o <a href="https://notacarioca.rio.gov.br/WSNacional/nfse.asmx?wsdl">link acima</a> do WSDL. Salvei o WSDL na minha máquina, e gerei as classes Java a partir dele. Para isso, usei o projeto CXF (versão 2.7.8), e com isso usei <a href="http://cxf.apache.org/docs/wsdl-to-java.html">wsdl2java</a>, No meu DOS executei:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw-70TcYWB0KAGIjryR2SIAowaFlgQty7CAoeZl1lveT1bZjv40OKGnKlwnHSvYPTIiwStMrWmIeHCByniYrfBQ7EhcKQ5g56R7chfnxhN4sLFwSgSaPdcBbGIBWTYDLEbLtEozK4Z7KfI/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> wsdl2java -p com.meusistema.nfse -frontend jaxws21 -client "C:\Users\Eu\projetos\meunfse\web-app\WEB-INF\notacarioca-nfse.wsdl"
</code></pre>
<br />
<b>3- Como conseguir acessar o Web Service através do seu sistema, sua aplicação Java cliente, usando o certificado digital PFX.</b><br />
<br />
Bom, neste ponto eu já conseguia acessar o web service (na verdade o WSDL) pelo browser, mas a minha aplicação sempre recebia um erro 403 - Forbidden: Access is denied.<br />
<br />
a) Depois de muito bater cabeça, vi que o meu certificado PFX não possuía toda a cadeia de certificados, ele possuía apenas o certificado da minha empresa. Mas era preciso ter toda a cadeia:<br />
A cadeia na verdade é <b>minha empresa :: autoridade certificadora :: autoridade raiz -> etc</b><br />
<br />
Então, segui um passo a passo que me salvou. Obrigado ao alexegidio em <a href="http://www.guj.com.br/java/148620-nfe-erro-4037---forbidden-ao-acessar-webservices-asmx-resolvido">http://www.guj.com.br/java/148620-nfe-erro-4037---forbidden-ao-acessar-webservices-asmx-resolvido</a><br />
<br />
O passo a passo é:<br />
<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw-70TcYWB0KAGIjryR2SIAowaFlgQty7CAoeZl1lveT1bZjv40OKGnKlwnHSvYPTIiwStMrWmIeHCByniYrfBQ7EhcKQ5g56R7chfnxhN4sLFwSgSaPdcBbGIBWTYDLEbLtEozK4Z7KfI/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"><span style="font-family: arial;"> </span> 1- Importe o certificado .PFX para o IE >>> FERRAMENTAS/OPÇÕES DA INTERNET/ ABA CONTEÚDO / BOTÃO CERTIFICADOS.
2- Selecione a aba "Pessoal".
3- Clique em importar.
4- O Sistema vai abrir um assistente. Selecione o Certificado e clique em avançar.
5- Na próxima tela MARQUE O CHECBOX "Marcar esta chave como exportável. Isso possibilitará o backup e transporte das chaves posteriormente". Como o texto diz se você não marcar esta opção o certificado não poderá ser exportado
6- Após ser importado seu certificado vai para a aba Pessoal. Clique sobre ele e clique em "exportar"
7- Clique em avançar
8- Na próxima tela marque a opção: "Sim, exportar a chave privada". Clique em avançar.
9- Na janela seguinte marque o checkbox "Incluir todos os certificados no caminho de certificação, se possível". <<ESTA OPÇÃO É CRUCIAL NÃO ESQUEÇA DELA !!!
10- Informe a senha e a seguir o local onde o arquivo será salvo.
PRONTO Feito isso está pronto você já pode utilizar o certificado no seu projeto.
</code></pre>
<br />
<br />
b) Teve um outro pulo do gato que foi juntar este meu certificado, agora completo com toda a cadeia de certificados, com os demais certificados aceitos para acesso HTTPS. O java da sua máquina vem com um arquivo cacerts que dentro possui as autoridades certificadoras confiáveis. É assim que sua aplicação java pode acessar um web service em HTTPS. Mas agora, além de aceitar os certificados do HTTPS, nossa aplicação tem que passar um certificado privado, como forma de autenticação. Então, eu fiz o import do meu certificado PFX pra dentro de um keystore existente do java instalado na minha máquina. Para isso, copiei o <b>cacerts </b>original da Sun em <b>C:\Program Files\Java\jdk1.6.0_30\jre\lib\security\cacerts</b> para outro diretório para não quebrar o arquivo original. E então executei o seguinte comando neste arquivo cópia:<br />
<br />
<pre style="background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjw-70TcYWB0KAGIjryR2SIAowaFlgQty7CAoeZl1lveT1bZjv40OKGnKlwnHSvYPTIiwStMrWmIeHCByniYrfBQ7EhcKQ5g56R7chfnxhN4sLFwSgSaPdcBbGIBWTYDLEbLtEozK4Z7KfI/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"><code style="color: black; word-wrap: normal;"> keytool -importkeystore -srckeystore meu-certificado.pfx -destkeystore cacerts -srcstoretype PKCS12 -deststoretype JKS
</code></pre>
<br />
A senha do cacerts original da Sun é "changeit".<br />
<br />
Com isso, agora eu tenho um keystore que é o arquivo <b>cacerts</b>, que possui também o meu próprio certificado, além dos certificados aceitos para HTTPS.<br />
<br />
Pronto. Agora é só executar....<br />
<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>package com.peladeiro.nfse;
import java.io.File;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
import com.peladeiro.factory.DadosNfseFactory;
import com.peladeiro.nfse.binding.EnviarLoteRpsEnvio;
public class ClienteTeste {
private static final QName SERVICE_NAME = new QName("http://notacarioca.rio.gov.br/", "Nfse");
public static void main(String[] args) throws JAXBException {
URL wsdlURL = Nfse.WSDL_LOCATION;
if (args.length > 0 && args[0] != null && !"".equals(args[0])) {
File wsdlFile = new File(args[0]);
try {
if (wsdlFile.exists()) {
wsdlURL = wsdlFile.toURI().toURL();
} else {
wsdlURL = new URL(args[0]);
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
ClienteTeste c = new ClienteTeste();
c.autentica();
c.chamaWebService(wsdlURL);
}
private void autentica() {
String caminhoDoCertificadoDoCliente = "C:\\Users\\Eu\\meu-certificado-final.pfx";
String senhaDoCertificadoDoCliente = "minhaSenhaPrivada";
String caminhoDoKeyStore = "C:\\Users\\Eu\\cacerts";
String senhaDoKeyStore = "changeit";
System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");
System.setProperty("javax.net.ssl.keyStore", caminhoDoCertificadoDoCliente);
System.setProperty("javax.net.ssl.keyStorePassword", senhaDoCertificadoDoCliente);
System.setProperty("javax.net.ssl.trustStoreType", "JKS");
System.setProperty("javax.net.ssl.trustStore", caminhoDoKeyStore);
System.setProperty("javax.net.ssl.trustStorePassword", senhaDoKeyStore);
}
public void chamaWebService(URL wsdlURL ) throws JAXBException{
Nfse ss = new Nfse(wsdlURL, SERVICE_NAME);
NfseSoap port = ss.getNfseSoap();
// Esta Factory instancia os objetos com os dados a serem passados para o web service
/*
Exemplo:
TcIdentificacaoRps idrps = new TcIdentificacaoRps();
idrps.setNumero(new BigInteger("4321"));
idrps.setSerie("1");
idrps.setTipo(TIPO_RPS);
* */
EnviarLoteRpsEnvio envio = DadosNfseFactory.criaEnviarLoteRpsEnvio();
JAXBContext jc = JAXBContext.newInstance(EnviarLoteRpsEnvio.class);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
StringWriter sw = new StringWriter();
m.marshal(envio, sw);
RecepcionarLoteRpsRequest recepcionarLoteRpsRequest = new RecepcionarLoteRpsRequest();
recepcionarLoteRpsRequest.setInputXML(sw.toString());
RecepcionarLoteRpsResponse recepcionarLoteRpsResponse = port.recepcionarLoteRps(recepcionarLoteRpsRequest);
System.out.println("recepcionarLoteRps.result=" + recepcionarLoteRpsResponse.outputXML);
}
}
</code></pre>
<br />
E é isso!<br />
Abcs<br />
<br />
<br />
<br />Unknownnoreply@blogger.com17tag:blogger.com,1999:blog-4447358400792645990.post-66957227002494519112013-05-24T15:00:00.002-03:002013-05-24T15:00:49.879-03:00Artigo interessante do pessoal do Financial Times mostrando detalhes da Web App para dispositivos móveis:<br />
<a href="http://coding.smashingmagazine.com/2013/05/23/building-the-new-financial-times-web-app/">http://coding.smashingmagazine.com/2013/05/23/building-the-new-financial-times-web-app/</a><br />
<br />
Algumas bibliotecas que eu não conhecia, ou que eles criaram:<br />
<a href="https://github.com/ftlabs/ftellipsis">https://github.com/ftlabs/ftellipsis</a><br />
<a href="https://github.com/ftlabs/fruitmachine">https://github.com/ftlabs/fruitmachine</a><br />
<a href="https://github.com/ftlabs/ftscroller">https://github.com/ftlabs/ftscroller</a><br />
<a href="https://github.com/davidaurelio/TouchScroll">https://github.com/davidaurelio/TouchScroll</a><br />
<br />
Além disso, usaram LocalStorage para guardar JavaScript e CSS para a aplicação poder funcionar Offline, e guardarm as imagens em IndexedDB ou WebSQL.<br />
<br />
Cada um atacando os desafios mobile de formas diferentes....<br />
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-74115770594652765072012-07-24T21:05:00.001-03:002012-07-24T21:05:26.009-03:00primeira iPhone App - Novo Nono Dígito para Celulares de São PauloEm parceria com meu amigo JP, fizemos uma aplicação para iPhone que resolve o problema de atualizar todos os telefones de celulares de São Paulo da nossa agenda do iPhone.<br />
<br />
O problema é que em 29 de julho agora, os números de telefone celular de São Paulo ganharão um nono dígito 9. Então, se o telefone do paulista era xxxx-xxxx, agora será 9xxxx-xxxx.<br />
<br />
E o aplicativo 9D9 tá aí pra resolver isso:<br />
<a href="http://www.9d9.com.br/">www.9d9.com.br</a><br />
<br />
Colocamos no ar essa semana, mas a Apple ainda não aprovou.... aguardemos mais uns dias....Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-30836806534724395892012-05-15T20:00:00.002-03:002012-05-15T20:00:32.202-03:00Vendo corretamente os caracteres especiais com UTF8 no PuttyQuando uso o Putty para me conectar com servidores linux remotos, e lá dentro uso caracteres especiais (como acentos da língua portuguesa). muitas vezes tenho problemas para visualizar estes caracteres.<br />
Por exemplo, quando faço um select no BD Postgres, e no resultado da query tem caracteres especiais codificados em UTF8, fica tudo feio....<br />
Para resolver isso, abra o Putty. Na janela inicial, lá no menu Window > Translation, defina o item Remote Character Set para "UTF-8". Só isso.<br />
AbcsUnknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-87032230191680285942012-02-21T17:04:00.000-02:002012-02-21T17:04:17.867-02:00Google Calendar feito com GrailsEu não escrevo há algum tempo, eu sei....sinto muito...... mas o tempo tem estado muito curto. Mas hoje vi um post que não pude deixar de repassar por aqui, pois vale a pena....<br />
<br />
O post é de <a href="http://www.craigburke.com/">Craig Burke</a> mostrando uma aplicação Grails, desde a modelagem de domínio até plugins usados, que simula o Google Calendar. Bem interessante:<br />
<br />
Parte 1: <a href="http://www.craigburke.com/blog/2012/02/09/creating-google-calendar-in-grails-part-1-the-model/">http://www.craigburke.com/blog/2012/02/09/creating-google-calendar-in-grails-part-1-the-model/</a><br />
Parte 2: <a href="http://www.craigburke.com/blog/2012/02/16/creating-google-calendar-in-grails-part-2-displaying-the-calendar/">http://www.craigburke.com/blog/2012/02/16/creating-google-calendar-in-grails-part-2-displaying-the-calendar/</a><br />
<br />
Abcs a todos.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-41693309077999486302012-01-14T03:06:00.000-02:002012-01-14T03:06:38.437-02:00Montando um super PC de alta performance<span style="color: blue;">Um amigo me mandou um email me dando dicas para montagem de um PC de alta performance. Ele escreveu o que fez para montar a máquina dele, que disse ter ficado um verdadeiro avião. Ficou tão bom o email que aqui vai ele. Valeu Paulo Goes......:</span><br />
<br />
"Felipe,<br />
<br />
<br />
O coração do sistema é um processador i7 2600K. É o processador mais potente para uso pessoal (os outros i7 já são workstation ou servidor). Este processador incorpora um conjunto de instruções gráficas, de modo que dispensa o uso de processador gráfico, mesmo na placa mãe. O i7 2600 (sem o K) é um pouco mais barato, possui a mesmíssima capacidade de processamento, só não é “overclockable” e tem uma versão anterior do driver gráfico Intel (é uma excelente opção para reduzir um pouco o custo). Os i5 (acho que o i5 equivalente é o 2500/2500K) têm potência equivalente (e a mesma relação K/sem K dos i7), mas não suportam hiperthreading. Com hiperthreading as 4 CPUs do processador dual core aparecem como oito para o sistema operacional, proporcionando um ganho de paralelismo (se e somente se o seu mix de aplicações demandar tantos processos concorrentes). Assim os i5 são também excelentes opções para baratear o custo.<br />
<br />
O componente seguinte é a placa mãe. Atualmente estou bem exigente neste quesito, e só estou comprando ASUS ou Intel. As Intel estão mais baratas, e bem interessantes. No entanto, eu tinha alguns requisitos adicionais:<br />
<br />
- queria 16G de memória – isto hoje só é economicamente viável com placas que tenham 4 bancos de memória. A maioria das placas mais baratas só estão vindo com 2 bancos, mas as memórias de 8G estão ainda proibitivamente caras;<br />
- tenho alguns dispositivos IDE, e as placas mais modernas e baratas só têm SATA<br />
- meu adorado monitor ainda é VGA, e as placas mais modernas e baratas têm apenas DVI/HDMI, e eu não queria usar conversores<br />
- SATA 6GB/s – as placas mais modernas têm interface SATA de 6GB/s. Na prática, não faz a menor diferença, porque os HDs não conseguem entregar nem os 3GB/s do SATA “II”. No entanto (ver abaixo), uma das principais “turbinas” do meu “avião” é um disco SSD que atinge com facilidade este throughput, de modo que SATA 6GB/s era mandatório.<br />
- gosto de placas ATX – nada de micro-ATX ou mini-ATX (embora, na prática, sejam quase equivalentes)<br />
- mouse, teclado, cam, impressora, scanner, iPhone, disco externo, pen-drives, tudo lá em casa é USB. Desejo uma placa mãe com o maior número possível de USBs externas.<br />
<br />
No mais, escolher uma placa é uma combinação de marca, com os slots/dispositivos que você necessita, mais aqueles que você acha que vai precisar no futuro. Neste sentido, achei que umas portas USB 3 seriam altamente desejáveis, e que um firewire não seria demais. Aí, é entrar no Boadica, filtrar por fornecedor, sortar por valor, e ir olhando as configurações até achar a primeira que te satisfaça. Escolhendo uma, entrar no Tomshardware, para ver se há algum comentário negativo sobre esta combinação processador/placa. Acabei escolhendo uma Asus, mas nem lembro o modelo agora.<br />
<br />
O terceiro componente importante é um SSD de 120G da OCZ (Vertex 3). Este é um componente ainda complicadinho. Memórias SSD têm um burnout depois de alguns milhões de escritas (ou seja, têm uma vida útil limitada). As controladoras mais modernas (Sandforce) maximizam a vida útil distribuindo igualmente as atividades de escrita por setores diferentes do disco, evitando assim, por exemplo, que o começo do disco estrague antes do final. Uma boa controladora também é essencial para conseguir os tais 6GB/s de throughput. Os OCZ são as Ferrari dos SSD. Tem uma outra linha (que não é Vertex) que é um pouco mais barata, mais ainda muito boa. Me aconselharam a evitar os Kingston (bem mais baratos) e me disseram que os Corsair talvez sejam bons. Li alguns artigos dizendo que os Samsung estão causando sensação por serem mais baratos e tecnologicamente competentes, mas não quis arriscar. A idéia é instalar o Windows neste SSD, conseguindo assim uma baita velocidade nos boots e na carga de qualquer programa/OS component. Configurar corretamente o Windows para maximizar o benefício e a vida útil do SSD é uma arte obscura, na qual ainda estou me iniciando (deixa o paging ou não? temporary IE files? temp files? como tirar os dumps do Windows do SSD? etc.).<br />
<br />
O gabinete é outro aspecto que eu tomo cuidado. Gabinetes devem ser silenciosos, práticos para montar (por exemplo, não necessitam de parafusos para fixar os HDs) e bem ventilados. Já a alguns anos que venho usando os gabinetes CoolerMaster. Sempre usei a linha básica (Elite), mas este eu comprei da linha ultra-silenciosa (Sileo), que vem também com duas ventoinhas (os Elite vêm com uma só – a gente sempre pode acrescentar ventoinhas, mas as originais da CoolerMarster são hiper-silenciosas).<br />
<br />
A fonte é um componente fundamental para a saúde do equipamento. A qualidade das fontes caiu, e fontes anteriormente de boa qualidade (como as Seventeen) hoje são consideradas suspeitas. Comprei uma Coolermaster 600W reais, meio overkill para o meu equipamento (uma de 450W já bastava) porque não tenho nenhum periférico/placa no meu ambiente que não seja um “array” de HDs (que consomem bem pouca energia), mas, pensando no futuro...<br />
<br />
A memória, comprei Corsair 4x4GB. Eu sempre usei Kingston (que custa metade do preço) sem problemas, mas meu filho me convenceu a comprar as Corsair... provavelmente bobagem.<br />
<br />
O componente final é um HD de 1TB (ainda o menor custo por byte armazenado), para se juntar aos meus atuais 2,5TB.<br />
<br />
Um aspecto interessante é que eu não jogo games e não faço processamento gráfico, de modo que não tenho placa de vídeo há anos (uso onboard ou, agora, inprocessor).<br />
<br />
Finalmente, como comprei: Primeiro, eu uso o Boadica para estabelecer referência de preços. Os preços do Boadica são muito bons, e normalmente não consigo estes preços nem na Santa Efigênia aqui em sampa. Como estava sem tempo, minha estratégia desta vez foi adquirir na Internet. Para começar, identifiquei no Buscapé os fornecedores web mais confiáveis para componentes (nenhum é muito confiável, nem inspira muita confiança, de modo que é preciso ser um pouco tolerante e aceitar algum risco aqui). Pré-selecionei uma meia dúzia, fiz uma análise de preços, e acabei ficando com 3 (Atera, Superbalão, e Kabum). Comprar em loja, e ainda mais parcelado (12x) se afasta um pouco dos preços do Boadica, mas é uma forma de diminuir o risco e o impacto imediato no bolso). Você não precisa fazer todas as compras na mesma loja, mas também não deseja comprar um componente em cada loja (maior risco de problemas e despesas de entrega), assim, tem que fazer algumas simulações para ver qual fornecedor consegue o menor preço médio dos componentes combinados. Comprei o gabinete, a fonte e as memórias na Kabum, e o processador, a placa mãe, e o HD no Superbalão. O SSD arrisquei um pouco mais e comprei no mercado livre.<br />
<br />
A compra no mercado livre foi a mais bem sucedida, embora o mercado pago tenha feito uma bobagem e faturado duas vezes o meu cartão. Escrevi para o vendedor, e ele em algumas horas estornou a venda (o mercado pago não ajuda você, você tem que se virar), e em menos de 24h após a compra o SSD estava lá em casa. Com a Kabum tive vários problemas por conta da operadora American Express (nunca tinha usado e nunca mais usarei). Cancelei a compra, refiz pelo Visa, e 48h depois recebi a mercadoria, sem problemas. No geral, fiquei muito satisfeito com a Kabum, por causa do comportamento deles nestes dificuldades com a AMEX. O pior foi o Superbalão. A entrega levou uma semana (após meu cartão ter autorizado), e o HD chegou sem lacre e arranhado (parece que já havia sido instalado em algum PC antes). Deu algum trabalho para trocar (eles queriam que eu pagasse o frete, e eu tive que chiar muito), e ainda estou aguardando receber a troca.<br />
<br />
That’s it. Isso tudo requereu muita pesquisa, conversa, visitas a fóruns e o escambau. A parte mais chata são sempre estas relações comercias selvagens do Brasil, combinadas com a estupidez da Amex. Por uns 10K reais faço tudo de novo prá você..."<br />
<br />
<span style="color: blue;">Isso é que é dica..... Sensacional....</span>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4447358400792645990.post-92190357601702249152011-08-21T22:26:00.001-03:002011-08-21T22:27:47.070-03:00Grails de alta performance - parte 2Este post faz parte de uma série que estou escrevendo sobre técnicas para fazer uma aplicação grails de alta performance. Estou usando uma aplicação de exemplo, para demonstrar estas técnicas. Esta aplicação se chama <a href="https://github.com/felipenasc/GPerform">GPerform</a>, e está <a href="https://github.com/felipenasc/GPerform">disponível no GitHub</a>, e é uma espécie de Twitter + Instagram (em menor escala, naturalmente), onde usuários enviam posts de texto curto + foto.<br />
<br />
Na <a href="http://felipenasc.blogspot.com/2011/07/gperform-uma-aplicacao-de-exemplo-com.html">parte 1</a> desta série, eu mostrei a primeira prática para se desenvolver uma aplicação grails de alta performance: Não usar Collections para as relações hasMany.<br />
<br />
Neste post vou mostrar os plugins que usei.<br />
<br />
<span class="Apple-style-span" style="font-size: large;">1) Spring-events plugin:</span><br />
<br />
Este plugin dá a possibilidade de publicar eventos via Spring, de forma assíncrona.<br />
<br />
Para instalar o plugin, basta executar "grails install-plugin spring-events".<br />
<br />
Eu usei este plugin para lançar um evento indicando que alguém enviou um novo post no sistema, e que, portanto, o sistema precisa processar a foto (criando uma foto com tamanho (kb) menor, e outras fotos thumbnail).<br />
<br />
Mas, para não deixar o usuário esperando enquanto o sistema processa as fotos, e assim dar uma impressão de muita velocidade de resposta para o usuário, eu quis fazer este processamento de forma assíncrona, dando uma resposta para o usuário quase que imediata após o envio do seu post.<br />
<br />
A action que recebe o envio do post do usuário, com texto + foto, apenas grava a foto num diretório, salva a entidade Foto no banco de dados, e lança este evento assíncrono no Spring, retornando a resposta imediatamente para o usuário (sem processar a foto neste momento). Veja:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code> def compartilhar = {
String msgErro = ""
if(!session.usuario){
redirect(controller:'home')
return
}
// Pega a foto do formulario e salva no diretorio para ser processada
MultipartFile fotoOriginal = request.getFile('fotoOriginal')
String extensao = identificaExtensao(fotoOriginal)
String nomeArquivo = RandomStringUtils.random(10).encodeAsSHA256() + extensao
try {
File destino = new File(ConfigurationHolder.config.gp.fotos.originais.folder + File.separator + nomeArquivo)
fotoOriginal.transferTo(destino)
// Salva uma isntancia do objeto de metadados Foto
Foto foto = new Foto(params)
foto.autor = Usuario.get(session.usuario.id)
foto.nomeArquivo = nomeArquivo
if(foto.save(flush:true)){
// metodo adicionado pelo spring-events Plugin
<b>publishEvent(new FotoSubidaEvent(new Expando(id:foto.id)))</b>
flash.message = "Foto enviada com sucesso. Aguarde processamento."
redirect(action:index)
}
else{
if(destino.exists()) destino.delete()
msgErro = "Erro ao salvar os dados da foto."
render(view:'index', model:[msgErro:msgErro, foto:foto])
}
} catch (IOException ioe) {
msgErro = "Erro ao fazer upload do arquivo."
} catch (IllegalStateException ie) {
msgErro = "Erro ao fazer upload do arquivo."
}
}
</code></pre><br />
Veja que o método importante aqui é o "<span class="Apple-style-span" style="font-family: monospace; font-size: 12px; line-height: 14px; white-space: pre;"><b>publishEvent()</b></span>", que publica o evento <b>FotoSubidaEvent </b>(eu sei que o nome ficou feio, mas ...). Pronto, o evento foi publicado.<br />
<br />
Agora, precisamos criar uma classe que vai ser o Listener deste evento. A maneira mais fácil que encontrei foi criar uma classe de serviço que implementa "<span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;">ApplicationListener<fotosubidaevent></fotosubidaevent></span>", ou seja, ela será um Listener para os eventos do tipo FotoSubidaEvent. Ao implemetar esta interface, sua classe de Service terá que ter um método "<span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;"><span class="s1">void</span></span><span class="Apple-style-span" style="font-family: Monaco; font-size: 11px;"> onApplicationEvent(FotoSubidaEvent event)</span>". Este método será chamado pelo Spring automaticamente quando um evento do tipo FotoSubidaEvent for lançado, de forma assíncrona.<br />
<title></title> <style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco}
span.s1 {color: #b5288b}
</style> <title></title> <style type="text/css">
p.p1 {margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Monaco}
span.s1 {color: #b5288b}
</style> <br />
<span class="Apple-style-span" style="font-size: large;">2) Springcache plugin:</span><br />
<div><br />
Este <a href="http://gpc.github.com/grails-springcache/docs/guide/index.html">plugin</a> faz uso do projeto Spring Cache, e permite que se faça:</div><div>- Cache de métodos dos beans do Spring; por exemplo, para fazer cache do retorno de métodos das classes de serviços. </div><div>- Fragmentos das páginas, ou páginas inteiras, geradas pelos controllers; por exemplo, para fazer o cache de uma determinada action de um controller; </div><div><br />
</div><div>No caso da aplicação de exemplo, <a href="https://github.com/felipenasc/GPerform">GPerform</a>, usei o Springcache para fazer cache da action "<span class="Apple-style-span" style="color: #1000d1; font-family: Monaco; font-size: 11px;">HomeController.</span><span class="Apple-style-span" style="color: #1000d1; font-family: Monaco; font-size: 11px;">recentes()</span>" , ou seja, eu coloquei em cache os posts de fotos mais recentes. Assim, não é preciso acessar o banco de dados o tempo todo para pegar os posts mais recentes, pois eles estão na memória do cache.<br />
<br />
Para usar este plugin, basta instalar usando o comando "grails install-plugin springcache". Em seguida, coloquei uma anotação na action do meu controller (na action que quero que a resposta fique em cache):</div><br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><span class="Apple-style-span" style="font-family: monospace;"> </span><code><b>@Cacheable("recentesCache")</b>
def recentes = {
log.info "Executando action 'recentes' e por isso nao ta usando o cache agora."
def recentes = buscaFotosMaisRecentes(20)
[fotosMaisRecentes:recentes]
}
</code></pre><br />
<div>Veja que eu coloquei um nome neste cache ("<span class="Apple-style-span" style="font-family: monospace; font-size: 12px; line-height: 14px; white-space: pre;">recentesCache</span>"), pois eu posso criar vários caches distintos, e controlar cada um da forma que eu quiser. Esta anotação <span class="Apple-style-span" style="font-family: monospace; font-size: 12px; line-height: 14px; white-space: pre;"><b>@Cacheable("recentesCache")</b></span> é que indica para o plugin que o retorno desta action precisa ser guardado em memória no cache. Só isso.<br />
<br />
Agora o problema passa a ser como renovar o cache quando um novo post for enviado por um usuário do site. Para isso, basta limpar o cache (matar o cache) quando um novo post for inserido. No meu caso, o que fiz foi o seguinte: quando um usuário envia um novo post (que é um texto + foto), o controller recebe esta requisição, faz algumas verificações, salva a foto num diretório para ser processada posteriormente (de forma assíncrona -- falo sobre isso mais tarde), e grava os dados da entidade Foto no banco de dados (que são os metadados sobre a foto e o texto do post do usuário). Quando o sistema for processar a foto de forma assíncrona, aí sim a foto está pronta para ser mostrada no site, e é neste momento que eu preciso limpar o cache. O método que processa a foto e limpa o cache é um método de uma classe de Serviço (FotoService). Veja abaixo:<br />
<br />
</div><br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><span class="Apple-style-span" style="font-family: monospace;"> </span><code><b>@CacheFlush("recentesCache")</b>
void onApplicationEvent(FotoSubidaEvent event) {
if(log.isInfoEnabled()) log.info "Executando evento FotoSubidaEvent..."
Foto foto = Foto.get(event.source.id)
if(foto){
// processa a imagem da foto
}
}
</code></pre><br />
Assim que este método for executado pela aplicação, o cache será limpado (jogado fora), pois este método tem a anotação <span class="Apple-style-span" style="font-family: monospace; font-size: 12px; line-height: 14px; white-space: pre;"><b>@CacheFlush("recentesCache")</b></span>. Veja que usei o mesmo nome "recentesCache" para indicar qual cache quero limpar.<br />
<br />
Perceba também que este é aquele método "<span class="Apple-style-span" style="font-family: monospace; font-size: 12px; line-height: 14px; white-space: pre;">onApplicationEvent()</span>", citado no item 1 acima, usado pelo Spring quando o framework detecta o lançamento do evento do tipo FotoSubidaEvent.<br />
<br />
Pronto. Da próxima vez que a action <span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;">HomeController.recentes()</span> for executada, o Springcache vai ver que o "recentesCache" não existe mais, aí vai executar a action, pegar seu resultado e colocar em cache novamente. E enquanto este cache existir, a action não é executada, e ao invés disso, o conteúdo do cache é enviado para o usuário.<br />
<br />
Um pequeno detalhe deste plugin é que ele tem uma configuração padrão que indica que o cache deve existir por apenas 120 segundos. Como era muito pouco para o meu caso, então configurei para que o cache vivesse por 3600 segundos. Veja como:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>springcache {
defaults {
eternal = false
diskPersistent = false
timeToLive= 86400
}
caches {
<b> recentesCache {
timeToLive= 3600
}
</b> }
}
</code></pre><div></div><br />
<br />
Ficamos por aqui com este artigo que já tá longo. Ainda falta coisa pra falar, como resources plugin, cache-headers plugin, cached-resources plugin e zipped-resources plugin. Até a próxima.<br />
<br />
Lembre-se que você é bem vindo para contribuir com esta aplicação de exemplo <a href="https://github.com/felipenasc/GPerform">GPerform via o GitHub</a>. Abcs a todos.Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4447358400792645990.post-71676576941015119742011-07-27T16:54:00.000-03:002011-07-27T16:54:55.428-03:00GPerform: uma aplicação de exemplo com PERFORMANCE na almaVenho pensando num tema legal para escrever um post. Aí resolvi dar uma atualizada, ler um pouco, ver umas apresentações, ..... E uma coisa que tem me interessado muito ultimamente é o assunto performance. Como uso muito Grails, então o assunto seria Grails e Performance. Então vamos lá.<br />
<br />
Depois de algumas leituras, acho que juntei diversas dicas, plugins, e técnicas que vão fazer uma aplicação grails voar em alta velocidade. Mas não queria apenas ficar na leitura, pois sabemos bem que a teoria é uma coisa, na prática.....<br />
<br />
Então botei a mão na massa e resolvi criar uma nova aplicação que chamei de GPerform (Grails Performance). Criei uma aplicação (na verdade está sendo criada ainda) que é uma espécie de Twitter + Instagram (em menor escala, naturalmente). Você manda uma foto, com comentário. Você pode seguir pessoas, e pessoas podem te seguir. Você pode avaliar fotos como boa ou ruim. Tudo é público.<br />
<br />
A aplicação é 0.1, ok? Mas está <a href="https://github.com/felipenasc/GPerform">disponível no GitHub</a>. Seja bem vindo para contribuir se quiser (estou dando meus primeiros passos com o Git, então paciência comigo....).<br />
<br />
A primeira coisa a fazer foi criar o modelo de dados, e aí já começam as dicas para uma melhor performance da sua aplicação: Não usar Collections para as relações hasMany. E mais que isso: acabar com as relações diretas entre duas classes para os casos many-to-many e one-to-many. Pois é, parece estranho. Mas tem fundamento.<br />
<br />
Para esclarecer o fundamento, vou dar um pequeno exemplo. Imagina:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>class ContaCorrente{
String numero
String agencia
static hasMany = [transacoes: Transacao]
}
</code></pre><br />
<div style="margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px;"><br />
</div><pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>class Transacao{
BigDecimal valor
static belongsTo = [conta: ContaCorrente]
}
</code></pre><br />
<br />
Imagine que você queira usar as facildades que o Grails dá para gerenciar esta relação one-to-many. Ou seja, você vai querer usar o método conta.addToTransacoes(t) que está disponível para você, claro.<br />
<br />
Mas imagine quantas transações tem numa conta corrente al longo de um ano. Se você tiver 30 transações por mês, no ano serão 360. Ou seja, no final do ano, quando o seu sistema for adicionar mais uma transação na conta do usuário, ele irá buscar as 360 transações para montar a collection "conta.transacoes" e só depois irá adicionar a nova transação nesta collection, para aí gravar esta alteração na collection no banco de dados. Se inicialmente você queria fazer apenas um insert, você acaba fazendo um select que retorna 360 itens pra memória (que você não vai usar nenhum deles), um insert da nova transação, e um update da ContaCorrente (pois o GORM atualiza o campo version deste objeto pai).<br />
<br />
É ou não é coisa demais, e é muito provável que a performance da aplicação será seriamente afetada?<br />
<br />
Para resolver isso, <a href="http://burtbeckwith.com/">Burt Beckwith</a> sugere, em uma <a href="http://www.infoq.com/presentations/GORM-Performance">apresentação na infoQ</a>, que não se use a relação one-to-many com o hasMany. Que se faça apenas o outro lado da relação, que é o many-to-one. Qual é a perda disso? Você não vai ter o método addToTransacoes(). Mas isso é fácil de resolver. E também não vai ter um atributo "transacoes" que te retorna todas as transacoes da sua conta (você provavelmente não iria usar nunca - nem deveria). (veja um <a href="https://mrpaulwoods.wordpress.com/2011/02/07/implementing-burt-beckwiths-gorm-performance-no-collections/">post interessante</a> sobre isso) Por exemplo:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>class ContaCorrente{
String numero
String agencia
def buscaTransacoesRecentes(int qtde){
Transacao.executeQuery('from Transacao t where t.conta = :c order by t.dateCreated desc', [c:this], [max:qtde])
}
def adicionaTransacao(BigDecimal valor){
new Transacao(valor:valor, conta:this).save()
}
}
</code></pre><br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>class Transacao{
BigDecimal valor
Date dateCreated
ContaCorrente conta
}
</code></pre><br />
Pronto. Simples assim. Você agora conseguiu ter apenas o seu único insert que você queria. Sem selects desnecessários, e sem update desnecessário (não estou contando com o select da ContaCorrente, mas tbem não contei com ele ali em cima). Veja que eu criei métodos na classe ContaCorrente para adicionar uma nova transação e para buscar as mais recentes.<br />
<br />
Acho que tá bom por hoje né? Terei que escrever outros posts para contar tudo. Tem ainda o uso de diversos plugins, como por exemplo: springcache, spring-events, resources, cache-headers, cached-resources, zipped-resources, e por aí vai. Já já eu volto.Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-4447358400792645990.post-29458632708884089312010-11-19T17:08:00.000-02:002010-11-19T17:08:40.408-02:00Dicas e passo a passo para iniciar com o Amazon Web Services - AWS (em inglês)Estou começando a fazer uns testes com os servidores da Amazon, o Amazon Web Services. Aí descobri umas dicas e passo a passo com boas informações:<br />
<br />
<a href="http://bristle.com/Tips/CloudComputing.htm#aws">http://bristle.com/Tips/CloudComputing.htm#aws</a><br />
<br />
Abcs a todos.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-68539180954346238372010-11-18T15:16:00.000-02:002010-11-18T15:16:00.586-02:00Grails: problema com uso de herança nos ControllersAo fazer uma revisão na performance de uma aplicação Grails, acabei me deparando com um problema muito esquisito. <br />
<br />
Nesta aplicação, há alguns controllers que possuíam alguns métodos comuns. Então, nada mais natural do que criar um controller pai, abstrato, do qual os demais controllers herdavam para poderem utilizar os métodos comuns. Não eram actions, eram métodos simples.<br />
<br />
Percebi, então, que os alguns dos métodos do controller pai eram executados mesmo sem serem chamados. É isso mesmo. Não me perguntem como, mas estava acontecendo. Será que havia algum erro da minha aplicação. Pode ser que sim, mas....<br />
<br />
Então criei uma action de teste simples no meu controller filho:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>class FilhoController extends PaiController {
def teste = {
render "ola"
}
}
</code></pre><br />
Ou seja, nada deveria ser executado além de aparecer um "ola" na tela. Mas o problema é que ao configurar o Grails para fazer o log das queries SQL (no DataSource.groovy coloquei loggingSql = true), quando eu acessava esta action http://localhost:8080/app/filho/teste , um monte de SQL aparecia no console. Eram as queries que existiam nos métodos do PaiController. E aí? Fiquei horas, literalmente, tentando entender. Achei que fosse algo em algum Filter, algo no BootStrap, no resources.groovy, sei lá aonde. Mas não imaginei que era a herança que estava causando isso.<br />
<br />
Bom, resolvi arriscar e acabei com a herança. Matei o "extends PaiController". Aí criei uma classe groovy comum contendo todos os métodos da antiga classe PaiController. Problema resolvido. Acredita nisso? Nem eu. Mas........<br />
<br />
Só mais um detalhe: nesta classe groovy, que chamei de WebHelper, eu precisava de session, de um service, do request, da taglib para poder usar o método message(), e por aí vai. Então, criei esta classe com.minhaapp.web.WebHelper na pasta src/groovy, e configurei o resources.groovy para poder injetar nestas minha classe tudo que eu precisava, da seguinte forma:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>beans = {
webHelper(com.minhaapp.web.WebHelper) { bean ->
bean.scope = 'prototype' // aqui pode ser singleton se vc preferir
bean.autowire = 'byName'
applicationTagLib = ref('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
}
}
</code></pre><br />
<br />
E a classe WebHelper então ficou assim:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>package com.minhaapp.web
import org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib
import org.springframework.web.servlet.support.RequestContextUtils
import org.springframework.web.context.request.RequestContextHolder
public class WebHelper {
def grailsApplication
def meuService
def applicationTagLib
def teste(){
println grailsApplication.class
println RequestContextHolder.currentRequestAttributes().session.class
println applicationTagLib.class
println meuService.class
}
}
</code></pre><br />
Abcs a todos.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4447358400792645990.post-37875620318854556702010-11-10T11:59:00.000-02:002010-11-10T11:59:31.756-02:00Reportagens sobre Grails na revista WidePessoal<br />
<br />
a <a href="http://www.revistawide.com.br/">revista Wide</a> deste mês tem duas reportagens boas sobre o Grails. Uma delas é comigo mesmo, onde falo a respeito do uso do Grails no caso do <a href="http://www.noiteuniversitaria.com.br/">NoiteUniversitaria.com.br</a> ( e comento um pouco sobre o caso do <a href="http://www.manubia.com.br/">Manubia</a> ).<br />
A outra é uma entrevista com o Graeme Rocher, desenvolvedor líder do projeto Grails.<br />
<br />
Interessante a revista. (não estou ganhando $$ com a revista não hein)....<br />
<br />
Abcs a todos.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-71507416794059255612010-07-06T01:27:00.002-03:002010-07-06T01:32:15.800-03:00Grails e Roo: screencast de comparação ao gerar uma mesma aplicaçãoOlá pessoal,<br />
<br />
venho dando uma olhada no <a href="http://www.springsource.org/roo">Spring Roo</a>. Esta ferramenta é bastante útil para quem desenvolve com Java. Ou seja, não apenas na plataforma Java, como o Grails, mas apenas com a linguagem Java. Se você tiver esta condição de ter que desenvolver com Java, vale a pena conhecer o Roo.<br />
<br />
Quero fazer uma comparação entre o desenvolvimento com Grails e Roo. Não é uma comparação justa, pois o Grails é um framework é em Groovy, e outro é uma ferramenta de geração que produz uma aplicação 99% Java (usando frameworks conhecidos com Spring, Hibernate, Log4j, etc). Porque então estou comparando? No fundo quero fazer um benchmark de performance entre as duas aplicações. Mas até chegar lá, vou mostrar desde o início, apresentando a geração da aplicação através dos comandos de geração das duas ferramentas.<br />
<br />
Para isso, então, escolhi uma aplicação bem simples, e o passo a passo a ser seguido tanto com Grails quanto com o Roo é:<br />
<br />
- Criar projeto<br />
- Configurar persistência com Hibernate e Hypersonic DB in Memory (só Roo precisa deste passo)<br />
- Criar entidade Project<br />
- Criar atributos com validação: name, beginDate, active<br />
- Criar 2 finders com os atributos active e beginDate (só Roo precisa deste passo)<br />
- Configurar Log (só Roo precisa deste passo)<br />
- Configurar arquivos Eclipse (só Roo precisa deste passo)<br />
- Gerar Controller e views<br />
- Executar a aplicação.<br />
<br />
<br />
Aí vão os vídeos. Perdoem-me a pouca empolgação do vídeo 1, mas é que já era 1h da manhã e estava todo mundo dormindo lá em casa..... não podia fazer muito barulho pra não acordar minha filha :-)<br />
Abraços a todos.<br />
<br />
<span style="font-size: large;">Vídeo 1: Gerando com Roo</span><br />
<br />
<object height="405" width="500"><param name="movie" value="http://www.youtube.com/v/ZeDEU1HMxhQ&hl=en_US&fs=1?rel=0&border=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/ZeDEU1HMxhQ&hl=en_US&fs=1?rel=0&border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="500" height="405"></embed></object><br />
<br />
<span style="font-size: large;">Vídeo 2: </span><span style="font-size: large;">Gerando com </span><span style="font-size: large;">Grails</span><br />
<br />
<object height="405" width="500"><param name="movie" value="http://www.youtube.com/v/NQ1A-y6wqus&hl=en_US&fs=1?rel=0&border=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/NQ1A-y6wqus&hl=en_US&fs=1?rel=0&border=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="500" height="405"></embed></object>Unknownnoreply@blogger.com5tag:blogger.com,1999:blog-4447358400792645990.post-59101396321164566722010-07-02T22:51:00.000-03:002010-07-02T22:51:56.298-03:00Tudo sobre Grails GORM e as relações One-To-Many, Many-To-One e Many-To-ManyEsta é mais uma dica rápida. Você já se pegou com dúvidas de como funcionam as relações entre entidades no Grails? Como fazer o cascade corretamente? como se livrar daquela exception que não insiste em ser lançada?<br />
Pois é. O grails possui o mapeamento Objeto Relacional baseado no Hibernate, mas cria uma série de facilidades nas entidades para podermos lidar de forma fácil com as relações entre objetos.<br />
GORM significa Grails' Object Relational Mappging. E as relações a que me refiro são One-To-Many, Many-To-One, e Many-To-Many.<br />
Há um excelente resumo escrito por Peter Ledbrook no blog da SpringSource.<br />
<br />
<a href="http://blog.springsource.com/2010/07/02/gorm-gotchas-part-2/">http://blog.springsource.com/2010/07/02/gorm-gotchas-part-2/</a><br />
<br />
Vale MUITO a pena a leitura. Este link acima é a Parte 2 de uma série sobre o GORM. Caso queira (vale a pena tbem) leia a <a href="http://blog.springsource.com/2010/06/23/gorm-gotchas-part-1/">Parte 1</a>.<br />
<br />
Abcs<br />
FelipeUnknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-60903734663244487482010-06-16T22:23:00.001-03:002010-06-16T22:25:35.999-03:00Grails TagLib em qualquer classeUma dica rápida: se você precisar ter acesso a alguma Taglib do Grails dentro de alguma classe que não tem injetada a taglib, olhe só essa. Neste exemplo abaixo, digamos que você precise de uma mensagem internacionalizada dentro da classe BootStrap:<br />
<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>def grailsTags = grailsApplication.mainContext.getBean( 'org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib' )
String msg = grailsTags.message(code:'exemplo.de.mensagem')
</code></pre><br />
<br />
AbcsUnknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-1476578603357577832010-03-24T10:18:00.000-03:002010-03-24T10:18:15.090-03:00Escalabilidade de aplicações Web: futuro vs presente = Cassandra vs Mysql+memcachedMuito interessante o <a href="http://about.digg.com/blog/looking-future-cassandra">post do Digg</a> que fala sobre as tecnologias, estratégias e problemas que o Digg (assim como outras aplicações gigantes na Web) vive ao redor do tema escalabilidade.<br />
<br />
Quando se trata de aplicações web populares como Digg, Twitter e Facebook, escalabilidade se torna um desafio e tanto, e manter a aplicação com redundância, performance e consistência gera um custo grande de manutenção e monitoramento.<br />
<br />
Até muito recentemente, ou até hoje mesmo, muitos desses sites usam a solução de Mysql + memcached para poderem suportar o nível de escalabilidade necessário. Por exemplo, até o fim de 2008, o <a href="http://www.facebook.com/note.php?note_id=39391378919">Facebook possuía mais de 800 servidores de memcached</a> para poder servir mais de 28 terabytes de cache. É mole? <b>28 Tera de cache! </b>E isso, só para poder servir a leitura de dados em cache. Na hora de escrever os dados, aí usa-se uma solução de Mysql configurado com Master-Slave, e muitas vezes com particionamento vertical.<br />
<br />
O fato atualmente é: esta solução não está mais dando conta do recado. Alguns desses sites estão saindo da solução com Mysql+memcached e estão indo para o mundo não SQL. E o que muitos deles estão adotando é o <a href="http://cassandra.apache.org/">Cassandra</a>.<br />
<br />
Cassandra é um banco de dados distribuído, altamente escalável, baseado no conceito de <a href="http://labs.google.com/papers/bigtable.html">BigTable</a>.<br />
<br />
É isso. E vem por aí uma nova era para as soluções de aplicações web de alto volume.<br />
Vale a leitura do post do Digg.<br />
AbcsUnknownnoreply@blogger.com3tag:blogger.com,1999:blog-4447358400792645990.post-48907466802793345682010-03-22T21:17:00.001-03:002010-03-22T21:35:20.516-03:00GWT + Grails = Rock n Roll: Parte 2 - Separados e falando com RequestBuiler e JSONEste post é a Parte 2 da sequência de posts sobre o tema GWT + Grails = Rock n Roll. Você pode ver a <a href="http://felipenasc.blogspot.com/2010/02/grails-gwt-rock-n-roll-parte-1.html">Parte 1 aqui</a>. Nesta primeira mostrei como criar uma aplicação baseada em GWT e Grails. Para juntar os dois, usei o plugin Grails-Gwt. <br />
<br />
Neste post de hoje, depois de alguns dias lendo sobre GWT, fazendo testes e brincando com a aplicação criada no post da Parte 1, resolvi tentar também desenvolver outra aplicação com GWT + Grails mas desta vez sem usar o plugin. Desta forma, poderia eu mesmo decidir se prefiro com ou sem o Plugin. Este post, então, mostrará como criar uma solução composta de duas aplicações isoladas, uma GWT e outra Grails, que se falam via troca de dados usando JSON.<br />
<br />
<strong>Separando Grails e GWT em duas aplicações distintas</strong><br />
<br />
Como descrevi na Parte 1, não usar o plugin e ter duas aplicações distintas (uma GWT e outra Grails) significa ter que lidar com a Same Origin Policy (SOP) em tempo de desenvolvimento. Em produção simplesmente pode-se dar um jeito de levar os arquivos Html, Css e Javascript gerados pelo GWT para o propeto grails (em último caso, basta copiar e colar de um lado para o outro). Mas em desenvolvimento é preciso lidar com SOP de uma maneira mais produtiva do que ter que ficar copiando e colando de um lado para outro. A maneira mais simples que encontrei foi usando uma <a href="http://edwardstx.net/wiki/attach/HttpProxyServlet/ProxyServlet.java">ProxyServlet</a>, originalmente criada por Jason Edward, e depois evoluída por Matt Raible. O código fonte desta classe eu peguei no projeto Grails OAuth Plugin criado pelo M Raible. É simples de usar, e resolve o problema facilmente. O que ela faz neste nosso contexto é: <br />
<ul><li>a aplicações GWT no cliente faz uma requisição para o servidor; </li>
<li>a aplicações GWT no servidor recebe esta requisição e repassa para a aplicação Grails</li>
<li>a aplicação Grails recebe esta requisição e responde JSON</li>
<li>a aplicação GWT recebe esta resposta JSON e repassa para o cliente GWT</li>
</ul><br />
Desta forma não precisamos nos preocupar com SOP.<br />
<br />
O que se ganha em fazer isso? Para que, afinal, quero ter duas aplicações separadas (uma GWT e outra Grails). Bom, isso acaba sendo uma decisão pessoal de cada um, ok? Mas para mim, comecei a achar mais interessante seguir os padrões de cada projeto. Um projeto GWT, segundo as recomendações, deve ser organizado de uma maneira, com determinada árvore de diretórios, etc. Pode não ser seguido, mas é um padrão recomendado. Então, porque não seguir e falar a mesma lingua em chats, fóruns de discussão, novos desenvolvedores na equipe, etc?<br />
<br />
Além disso, separar os dois projetos provoca uma reflexão conceitual interessante sobre arquitetura de aplicações ricas para a web (RIA - Rich Internet Applications). Ao separar, você está literalmente, fisicamente, implementando a arquitetura SOFEA (Service Oriented Front-End Architecture). SOFEA foi muito bem apresentado no artigo <a href="http://wisdomofganesh.blogspot.com/2007/10/life-above-service-tier.html">Life Above the Service Tier</a> (vale a leitura. Este pessoa hoje em dia criou um grupo sobre este assunto e tem um site em: <a href="http://www.thinserverarchitecture.com/">ThinServerArchitecture.com</a> ).<br />
<br />
Numa arquiteturea SOFEA, toda a lógica de interface com usuário está sendo rodada no cliente, no nosso caso, no browser. Aqui então, as camadas View e Contoller da aplicação terão um projeto só pra elas (o projeto GWT). Ou seja, o desacoplamento é total. Você simplesmente desacopla o máximo que pode o tal do Front-End. Isso é interessante na minha opinião, pois aí a sua aplicação servidora, que roda no servidor, ela acaba por ter a responsabilidade apenas de prover serviços para a aplicação de Front-End (User Interface - UI). Parece até que estamos voltando para o conceito Cliente x Servidor, e de certa forma, estamos sim, mas um pouco diferente, pois é um Thin Client, onde apenas as regras de interface e controle de fluxo de navegação estão no cliente. Todas as demais regras de negócio, estão e devem estar no servidor.<br />
<br />
Outras duas justificativas para esta separação em duas aplicações são:<br />
<ul><li>Pode ficar mais simples equipes diferentes trabalharem cada uma em um projeto diferente</li>
<li>Cada projeto pode ser versionado independentemente</li>
</ul>Bom, depois de todas essas justificativas, o fato é: criei uma nova aplicação que é composta por um projeto GWT responsável pelo Front-End, e por outro projeto Grails responsável pelo Back-End (provendo serviços, lembra SOA?). Veja que as coisas começam até a ficar mais coesas e desacopladas. Interessante resultado....<br />
<br />
<strong>Comunicação entre GWT e Grails com JSON</strong><br />
<br />
Para a comunicação entre o Front-End e o Back-End, escolhi usar JSON. A aplicação <a href="http://grails.org/doc/latest/ref/Controllers/render.html">grails pode facilmente gerar uma resposta JSON</a>, enquanto que a aplicação <a href="http://google-web-toolkit.googlecode.com/svn/javadoc/2.0/com/google/gwt/json/client/JSONParser.html">GWT pode facilmente consumir JSON</a>. Então, sendo o JSON um formato leve para troca de dados, e sendo ele facilmente servido e consumido pelo Grails e GWT respectivamente, acho que JSON é uma boa escolha. Mas, se você preferir trabalhar com XML, ou outro formato proprietário, a decisão é sua.<br />
<br />
Portanto, para criarmos esta nova aplicação, basta criar um novo projeto GWT, e outro Grails. <br />
<br />
<strong>Aplicação Front-End (GWT)</strong><br />
<br />
Eu estou usando o Eclipse com o plugin do GWT, portanto, para criar um novo projeto basta clicar na opção de criar novo projeto GWT do Eclipse. Criei este projeto GWT chamado rockgwtfront, com o pacote com.rockgwt.front<br />
<br />
O wizard do Eclipse já cria uma classe EntryPoint, o arquivo XML de configuração do Module, e também cria um Serviço RPC de exemplo. Mas não precisaremos deste serviço para este exemplo, portanto, pode apagar o pacote com.rockgwt.front.server e o pacote com.rockgwt.front.shared, e também pode apagar os arquivos GreetingService.java e GreetingServiceAsync.java do pacote com.rockgwt.front.client. Depois altere a classe Rockgwtfront.java para não usar mais este Serviço RPC. No final das contas, fiquei com a classe de EntryPoint abaixo:<br />
<br />
<pre style="background-color: #eeeeee; border-bottom: #999999 1px dashed; border-left: #999999 1px dashed; border-right: #999999 1px dashed; border-top: #999999 1px dashed; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; padding-top: 5px; width: 100%;"><code>package com.rockgwt.front.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
public class Rockgwtfront implements EntryPoint {
public void onModuleLoad() {
final Button sendButton = new Button("Get Hendrix songs");
final FlexTable songsTable = new FlexTable();
// We can add style names to widgets
sendButton.addStyleName("sendButton");
sendButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
String hendrixSongsServiceUrl = GWT.getHostPageBaseURL();
if(!GWT.getHostPageBaseURL().contains("rockgwt"))
hendrixSongsServiceUrl += "rockgwt/";
hendrixSongsServiceUrl += "hendrix/songs";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, hendrixSongsServiceUrl);
builder.setTimeoutMillis(10000);
builder.setCallback(new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
if (response.getStatusCode() == 200) {
JSONValue v = JSONParser.parse(response.getText());
JSONArray songs = v.isArray();
if(songs != null){
updateBacklogItemsTable(songsTable, songs);
}
else{
throw new JSONException("O retorno nao veio como um objeto de Projeto");
}
} else {
onError(request, new RequestException(response.getText()));
}
}
@Override
public void onError(Request request, Throwable exception) {
Window.alert(exception.getLocalizedMessage());
}
});
try {
builder.send();
} catch (RequestException e) {
Window.alert(e.getLocalizedMessage());
}
}
});
RootPanel.get("sendButtonContainer").add(sendButton);
RootPanel.get("songsTableContainer").add(songsTable);
}
private void updateBacklogItemsTable(FlexTable songsTable, JSONArray songs) {
songsTable.clear();
for (int i=0; i<songs.size(); i++) {
JSONObject item = songs.get(i).isObject();
songsTable.setWidget(i, 0, new Label(item.get("name").isString().stringValue()));
songsTable.setWidget(i, 1, new Label(item.get("album").isString().stringValue()));
}
}
}
</code></pre><br />
Veja que a classe acima utiliza um RequestBuilder para realizar a requisição no servidor. Este RequestBuilder é uma classe do GWT que pode ser usada para fazer requisições remotas. Com ela, as requisições são sempre assíncronas, SEMPRE. Veja que passei um objeto Callback que será chamado quando esta requisição voltar do servidor. Assim, neste callback posso fazer o que for preciso com a resposta recebida. Neste caso, devo fazer o parse do JSON para poder trabalhar com os dados retornados. No fim de tudo, pego esses dados e os apresento numa tabela, só isso. <br />
<br />
Veja que a aplicação GWT espera um retorno JSON no seguinte formato:<br />
[{"name":"Hey Joe", "album":"Are You Experienced"},<br />
{"name":"Bold as Love", "album":"Axis: Bold as Love"}]<br />
<br />
Portanto, é preciso que a aplicação Grails retorne um JSON exatamente neste formato acima.<br />
<br />
É preciso adicionar ao ~/rockgwtfront/war/WEB-INF/lib do projeto os JARs necessários para a ProxyServlet citada acima. Portanto, adicione commons-fileupload-1.2.1.jar, httpclient-4.0.1.jar, e httpmime-4.0.1.jar ao lib. Crie a classe ProxyServlet. Você pode baixar o OAuth 1.3 e pegar o ProxyServlet de lá. Em seguida, é preciso configurar o web.xml para que ele saiba que o ProxyServlet existe. Vou mapear este proxyServlet para ele ser chamado para toda requisição que tenha o padrão /rockgwtback/*. Veja abaixo:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>
<div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"><span style="color: #c40000;"><</span>web-app<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>servlet<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>servlet-name<span style="color: #c40000;">></span>RockServlet<span style="color: #c40000;"></span>servlet-name<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>servlet-class<span style="color: #c40000;">></span>com.rockgwt.front.servlet.ProxyServlet<span style="color: #c40000;"></span>servlet-class<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>init-param<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>param-name<span style="color: #c40000;">></span>proxyHost<span style="color: #c40000;"></span>param-name<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>param-value<span style="color: #c40000;">></span>localhost<span style="color: #c40000;"></span>param-value<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"></span>init-param<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>init-param<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>param-name<span style="color: #c40000;">></span>proxyPort<span style="color: #c40000;"></span>param-name<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>param-value<span style="color: #c40000;">></span><span style="color: #1700ff;">8080</span><span style="color: #c40000;"></span>param-value<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"></span>init-param<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>init-param<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>param-name<span style="color: #c40000;">></span>secure<span style="color: #c40000;"></span>param-name<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>param-value<span style="color: #c40000;">></span>false<span style="color: #c40000;"></span>param-value<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"></span>init-param<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"></span>servlet<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"> </div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"><span style="color: #c40000;"><span class="Apple-style-span" style="color: black;"> </span><</span>servlet-mapping<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>servlet-name<span style="color: #c40000;">></span>RockServlet<span style="color: #c40000;"></span>servlet-name<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>url-pattern<span style="color: #c40000;">></span>/rockgwt/*<span style="color: #c40000;"></span>url-pattern<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"></span>servlet-mapping<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"> </div><div style="color: #00852e; font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"><span style="color: black;"> </span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>welcome-file-list<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"><</span>welcome-file<span style="color: #c40000;">></span>Rockgwtfront.html<span style="color: #c40000;"></span>welcome-file<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"> <span style="color: #c40000;"></span>welcome-file-list<span style="color: #c40000;">></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px; min-height: 15.0px;"><span class="Apple-style-span" style="font-family: monospace, 'Lucida Console', Monaco, fixed, monospace; font-size: small;"><span class="Apple-style-span" style="font-size: 12px; line-height: 14px;"><span class="Apple-style-span" style="font-family: Monaco, 'Lucida Console', Monaco, fixed, monospace; font-size: small;"><span class="Apple-style-span" style="font-size: 11px; line-height: normal;">
</span></span></span></span></div><div style="font: 11.0px Monaco; margin: 0.0px 0.0px 0.0px 0.0px;"><span style="color: #c40000;"></span>web-app<span style="color: #c40000;">></span></div></code></pre><br />
Veja que o servlet-mapping mostra como deverá ser a url a ser chamada pelo cliente GWT para o servidor Grails. ( /rockgwtback/* ).<br />
<strong>Aplicação Back-End (Grails)</strong><br />
<br />
Para o projeto Grails, ou você cria normalmente por linha de comando, ou pode até criar pelo Eclipse mesmo se estiver usando o STS (SpringSource Tool Suite). Criei este projeto Grails chamando-o de rockgwtback. Para este projeto, criei um controller chamado HendrixController que possui a action "musics". Esta action retorna uma lista de músicas do Hendrix. Veja abaixo:<br />
<br />
<pre style="background-color: #eeeeee; border: 1px dashed #999999; color: black; font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; font-size: 12px; line-height: 14px; overflow: auto; padding: 5px; width: 100%;"><code>package com.rockgwt.back.controller
class HendrixController {
def index = { }
def songs = {
println "chegou"
render(builder: 'json') {
songs = array {
song name: '1) Hey Joe', album: 'Are You Experienced'
song name: '2) Bold as Love', album: 'Axis: Bold as Love'
}
}
}
}
</code></pre><br />
Alterei também o nome da aplicação dentro do application.properties, colocando app.name=rockgwt, pois foi assim que eu coloquei a URL da requisição lá em cima, na aplicação GWT, e também no servlet-mappging do web.xml<br />
<br />
É isso!<br />
<br />
Assim, temos um sistema rodando com Grails e GWT, independentes um do outro, mas juntos formando a nossa aplicação. Faltaria aqui neste exemplo uma forma de juntar as duas aplicações para produção. Provavelmente faria isso com o Maven. Em produção, então, seria uma única aplicação mesmo.Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-4447358400792645990.post-79556082691377499162010-03-07T22:48:00.006-03:002010-03-08T21:52:04.716-03:00Grails + GWT = Rock n Roll: Parte 1 - Desbravando um novo mundoMeus amigos leitores (quantos devem ser estes leitores? deixe uma mensagem dizendo que vc é meu leitor! rs...) ... Bem vindos ao maravilhoso mundo do Rock n Roll. As bandas de rock muitas vezes são mal vistas por pessoas mais tradicionais. Outras vezes são veneradas por jovens rebeldes. Não importa. As boas bandas no final das contas só fazem uma coisa: dão um show de Rock n Roll. Mas o que é isso afinal? Sem dúvida a música é o principal produto desta fábrica rebelde. Por trás de pessoas tatuadas, cheias de brincos e roupa rasgada, por vezes alcoólatras e drogadas, bem no fundo há músicos que juntos ( e aqui está o mais importante: JUNTOS) conseguem tocar almas sedentas de Rock puro e simples. Já ouviu um <a href="http://skynyrd.com/v/CkTQUtx818w,lynyrd-skynyrdfree-birdbbc-1975">bom Lynyrd Skynyrd</a> com suas 3 guitarras, piano, batera, etc? É brincadeira o que esses caras tinham de musicalidade. Juntos eles deixam os fãs de rock embasbacados até hoje em dia.<div><br /></div><div>Depois desta introdução metafórica, vou iniciar hoje uma série de posts a respeito de GWT e Grails, duas tecnologias que juntas, para mim, estão despontando como a maior banda de Rock dos últimos tempos. </div><div><br /></div><div>Sobre o Grails todos sabem que eu acho que é simplesmente o melhor framework para desenvolvimento web na plataforma java (pelo menos daqueles que eu conheço).</div><div><br /></div><div>Sobre o GWT (Google Web Toolkit), eu sou um mero iniciante, ou melhor, curioso. Para ser sincero, tenho ficado um pouco cansado de lidar diretamente com HTML, CSS e Javascript, principalmente com a montanha de Javascript que se lida ao desenvolver aplicações AJAX. Mesmo usando bibliotecas como jQuery, ou Prototype, mesmo assim, é muito Javascript para se lidar. </div><div><br /></div><div>Por conta disso (e outros motivos), acho que o GWT pode ser de grande ajuda, de grande ajuda MESMO. Um dos grandes motivos também é a crença que eu estou ficando de que o GWT pode ajudar muito a se ter uma arquitetura decente também na camada View, e não apenas no model, no controller, na DAO, etc. Se formos olhando uma arquitetura de uma aplicação Grails tradicional de baixo para cima, podemos ver que:</div><div><ul><li>- a DAO está bem resolvida, </li><li>- os Serviços estão lá e cumprem seu papel (de controle transacional, por exemplo), </li><li>- os Controllers também tem seu papel de fronteira entre o mundo HTTP e nossas regras de negócio, </li><li>- mas aí chega na View e facilmente a gente começa a se perder e fazer uma bagunça com HTMLs, CSSs, Styles, Javascript em arquivos .js, Javascript inline, e por aí vai.</li></ul><div>Andei estudando um pouco de GWT para entender mais a fundo e estou começando a acreditar que ele pode ser usado para, mais facilmente, arquitetar decentemente a camada View. Por exemplo, pode-se usar um MVP (Model View Presenter) com o GWT. Alguém aí pode dizer que pode-se fazer isso também com o Javascript diretamente. Sim, até pode, mas que o GWT facilita as coisas, ah facilita. Primeiro: É JAVA! Você ganha ferramentas, refactorings, debugging, e muito mais. Ganha também o fato de que o GWT abstrai você de preocupações mundanas, como por exemplo a ofuscação e minimização do Javascript. Ele faz isso pra gente.</div><div><br /></div><div>Então é isso galera. Vamos começar.... Vou escrever os posts de acordo com a aventura que eu mesmo estou passando. Nunca desenvolvi uma aplicação real com GWT + Grails. Então vamos desenvolver uma juntos. Será pequena, mas vamos nessa!</div><div><br /></div><div><b>Entendendo o GWT</b></div><div><br /></div><div>Para começar eu instalei o <a href="http://code.google.com/intl/en/eclipse/docs/download.html">plugin do GWT para Eclipse</a>. Baixei também o <a href="http://google-web-toolkit.googlecode.com/files/gwt-2.0.3.zip">SDK do GWT</a> (neste momento estou usando a versão 2.0.3 do GWT). A página que uso para download de tudo é essa: <a href="http://code.google.com/intl/en/webtoolkit/download.html">http://code.google.com/intl/en/webtoolkit/download.html</a></div><div><br /></div><div>Fiz uma pequena experiência com o GWT sozinho, sem o Grails ainda. Para isso, pelo Eclipse eu criei um projeto GWT novo, usando os wizards que o plugin me dá. Ele cria um projeto já sendo executado de dentro do eclipse, tudo bonito. E esse novo projeto já tem uma pequena página de exemplo, com um diálogo modal, uma requisição Ajax e tudo mais. Tudo para você começar a entender o tal do EntryPoint, entender os Modules, os Widgets e tudo mais.</div><div><br /></div><div>Portanto, a primeira sugestão que eu dou é essa: brincar com a aplicação nova gerada pelo plugin do Eclipse.</div><div><br /></div><div>A primeira coisa que percebi com aplicações GWT é que tudo é AJAX. Toda a comunicação com o servidor é assíncrona. Aí, vi que a aplicação gerada pelo plugin do Eclipse tem um "layout" diferente da aplicação Grails. Então surgiu o primeiro problema: como juntar os dois mundos?</div><div><br /></div><div>Bom, em último caso, o GWT no fundo só serve para gerar os HTMLs e Javascript. Então você pode compilar seu projeto GWT, pegar o HTML e Javascript gerado, e copiar para seu projeto Grails para a parte de web-app. Simples assim! Mas isso não seria nada produtivo.</div><div><br /></div><div>Aí me veio a dúvida: como que as pessoas por aí juntam o Grails com o GWT? Elas fazem um projeto GWT e outro Grails, e depois juntam de alguma forma (com maven por exemplo)? Ou vai tudo num projeto só?</div><div><br /></div><div>E mais: se forem dois projetos distintos, como lidar com a <a href="http://en.wikipedia.org/wiki/Same_origin_policy">Same Origin Policy</a> (SOP)?</div><div><br /></div><div>E vi que realmente tem gente que usa a primeira forma, e gente que usa a segunda. E tudo gente de respeito por aí. Por exemplo, eu gosto muito o Matt Raible. Desde meus tempos de <a href="http://appfuse.org/">AppFuse</a> eu sou fã dele. Aprendi muito com a estrutura que ele montou para o AppFuse. </div><div><br /></div><div>E não é que o Matt Raible usa a primeira forma? Ele tem dois projetos, um GWT e outro Grails. Aí, em desenvolvimento ele deixa assim mesmo, como duas aplicações web rodando simultaneamente. E simplesmente lida com a SOP através de um Proxy espertinho na aplicação GWT. Este proxy recebe as requisições no servidor do cliente GWT, repassa para a aplicação Grails, recebe de volta a resposta desta, e repassa a mesma para o browser que tem o cliente GWT. Muito doido, mas funciona. </div><div>E em produção ele na verdade junta tudo. Ele tem lá um Maven bem esperto que faz tudo isso pra ele automaticamente, gerando um único WAR que tem a aplicação Grails e o cliente GWT. Aí simplesmente o problema do SOP desaparece.</div><div><br /></div><div>A outra alternativa é juntar tudo desde o início numa única aplicação. Para facilitar esta tarefa, um tal de <a href="http://www.cacoethes.co.uk/blog/">Peter Ledbrook</a> fez o <a href="http://www.grails.org/plugin/gwt">GWT Plugin para Grails</a> (não se deixe enganar por esta página. O plugin já está na versão 0.5 e suporta GWT 2.0).</div><div><br /></div><div>Eu, por enquanto, vou seguir este caminho mais simples (sempre gosto da simplicidade): vou usar o GWT Plugin para Grails. Parece tão simples agora......mas eu fiquei umas duas semanas até chegar a esta conclusão. E até agora queria entender porque o Matt Raible não usa o plugin. Talvez ele seja ruim por algum motivo. Se for isso, eu ainda não descobri.</div><div><br /></div><div>Então amigo, aí vão os passos iniciais:</div><div><ul><li>- Instalar o SDK do GWT</li><li>- Criar a variável de ambiente GWT_HOME apontando para a raiz do SDK do GWT</li><li>- Instalar o Plugin de Eclipse do GWT</li><li>- Criar uma aplicação pura e simples GWT usando o plugin do eclipse.</li><li>- Abrir a aplicação no browser usando a URL apresentada pelo Eclipse (com o parâmetro gwt.codesrv na URL - este parâmetro é que permite você alterar o código Java e imediatamente dar um refresh no browser e ver sua mudança lá).</li><li>- Garantir que o browser instalou o plugin de execução do GWT.</li><li>- Executar esta aplicação e brincar um pouco com os <a href="http://code.google.com/intl/pt-BR/webtoolkit/doc/1.6/DevGuideUserInterface.html#DevGuideUnderstandingLayout">diferentes Layouts de GWT</a>.</li></ul><div>Pronto. Agora que você brincou o GWT um pouco, vamos juntar os dois ( GWT + Grails ):</div><ul><li>- Criar uma nova aplicação Grails com o comando <span class="Apple-style-span" style="font-family:'courier new';">grails create-app rockgwt</span></li><li>- Vai pra dentro dela ( <span class="Apple-style-span" style="font-family:'courier new';">cd rockgwt</span> )</li><li>- Instalar o plugin GWT do Grails (usando o comando <span class="Apple-style-span" style="font-family:'courier new';">grails install-plugin gwt</span> ). Eu estou usando o grails 1.2.x e o plugin 0.5</li><li>- Criar um GWT Module com o comando do plugin <span class="Apple-style-span" style="font-family:'courier new';">grails create-gwt-module com.rockgwt.RockNRoll</span></li><li>- Criar a página principal do GWT para o seu Module. Eu fiz isso criando um controller do grails e uma Page index.gsp para este controller. Esta page é que irá hospedar o GWT:</li><li>1) <span class="Apple-style-span" style="font-family:'courier new';">grails create-controller com.rockgwt.controller.Principal</span></li><li>2) <span class="Apple-style-span" style="font-family:'courier new';">grails create-gwt-page principal/index.gsp com.rockgwt.RockNRoll </span><span class="Apple-style-span" style="font-family:arial;">(quando este comando te perguntar se você quer criar o Controller, diga que não, pois você acabou de criá-lo no passo anterior --- acho que o plugin não reconhece o Controller criado com package, sei lá).</span></li><li>- Compilar o seu módulo com o comando <span class="Apple-style-span" style="font-family:'courier new';">grails compile-gwt-modules </span><span class="Apple-style-span" style="font-family:georgia;">(demora um pouquinho mesmo)</span></li><li>- Executar o Development Mode com o comando <span class="Apple-style-span" style="font-family:'courier new';">grails run-gwt-client <span class="Apple-style-span" style="font-family:Georgia,serif;">Perceba que vai se abrir a janela da aplicação <b>Development Mode</b> do GWT</span></span></li><li>- Abra outro Terminal , vá para o seu projeto rockgwt e execute a aplicação grails (é isso mesmo, você terá duas coisas rodando: o <span class="Apple-style-span" style="font-family:'courier new';">grails run-app</span> da aplicação, e o grails run-gwt-client anterior, do GWT).</li><li>- Abra o browser e vá para a Home da sua aplicação GWT + Grails em: http://localhost:8080/rockgwt?gwt.codesvr=127.0.0.1:9997</li><li>- Na verdade, pode ser que na sua máquina a porta final seja outra, não sei. Para ganrantir sucesso, basta clicar no botão Launch Default Browser existente na janela do Development Mode.</li><li>- O endereço que você quer acessar no final de tudo será o seu controler Principal, ou seja:<br />http://localhost:8080/rockgwt/principal/index?gwt.codesvr=127.0.0.1:9997</li><li>- Não deixe de colocar este parâmetro maluco. Ele é o responsável por permitir que você altere o código Java e dê um refresh no browser para ver as modificações imediatamente.</li></ul><div>Agora, abra a sua classe RockNRoll e deixe ela assim:</div><div>(esta classe se encontra no seu projeto rockgwt em src/gwt/com/rockgwt/client)</div><br /><pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code>package com.rockgwt.client;<br /><br />import com.google.gwt.core.client.EntryPoint;<br />import com.google.gwt.user.client.ui.Button;<br />import com.google.gwt.user.client.ui.RootPanel;<br /><br />/**<br />* Entry point classes define <code>onModuleLoad()</code>.<br />*/<br />public class RockNRoll implements EntryPoint {<br />/**<br /> * This is the entry point method.<br /> */<br />public void onModuleLoad() {<br /><br /> final Button sendButton = new Button("Rock n Roll");<br /><br /> RootPanel.get().add(sendButton);<br />}<br />}<br /><br /></code></pre><br /><div>Pronto. Dê um refresh no seu browser, e o botão "Rock n Roll" estará lá.</div><div><br />UPDATE (8/3/2010):<br /><span style="color: rgb(0, 0, 102);">- Apenas para completar, para poder usra o Eclipse com tudo redondo, clique no botao direito do mouse em cima de src/gwt e selecione a opção Build Path > Use As Source Folder</span><br /><span style="color: rgb(0, 0, 102);">- E por último clique com o direito do mouse em cima do projeto rockgwt e selecione a opção Google > Webtoolkit Settings, lá dentro do wizard que se abre, selecione o primeiro checkbox que aprece lá em cima "Use Google Web Toolkit". Assim você terá tudo que precisa no classpath da aplicação no Eclipse.</span><br /><br /></div><div>Até a Parte 2 desta série... Aguardo Comentários para saber se estou agradando com este tema. Abcs</div><div><br /></div></div></div>Unknownnoreply@blogger.com8tag:blogger.com,1999:blog-4447358400792645990.post-38973902535225675222010-03-05T22:24:00.003-03:002010-03-05T22:36:14.960-03:00Executando códigos diferentes em Desenvolvimento e ProduçãoHá vezes em que queremos executar códigos diferentes dependendo do ambiente que a aplicação está sendo executada. Por exemplo, integrações com sistemas externos algumas vezes são simuladas em Desenvolvimento, e são executadas pra valer em Produção.<div><br /></div><div>Aí vai uma maneira simples de executar uma coisa em Desenvolvimento e outra em Produção (ou para quantos ambientes você tiver...).</div><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>import grails.util.Environment;<br /><br />class MeuController{<br /> Environment.executeForCurrentEnvironment {<br /> production {<br /> // AQUI vai o código que deve ser executado em produção<br /> }<br /> development {<br /> // AQUI vai o código a ser executado em desenvolvimento.<br /> }<br /> }<br />}<br /></code></pre><br /><div><br /></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-90708323044108592792010-03-04T14:42:00.002-03:002010-03-04T14:47:48.299-03:00Boas informações sobre grails em portuguêsPessoal<div><br /></div><div>para quem ainda não conhece, foi lançado o <a href="http://www.grailsbrasil.com/blogs/">Grailsbrasil Blogs</a> que é um agregador de <a href="http://www.grailsbrasil.com/blogs/">blogs em português sobre o Grails</a>. Lá você poderá ver diversos posts de diversas pessoas que escrevem sobre Grails e afins. Vale conhecer.</div><div><br /></div><div>Abcs</div><div>Felipe</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-42422279797058625652010-03-04T00:47:00.006-03:002010-03-04T01:18:46.443-03:00Grails e datas: Bind automático de atributo do tipo Date com formato customizadoSegue uma pequena receita de bolo para quem não está usando o <a href="http://grails.org/doc/latest/ref/Tags/datePicker.html">datePicker padrão do Grails</a>.<div><br /></div><div>O Grails vem com uma tag que é a g:datePicker.</div><div><br /></div><div>Ela serve para gerar listboxes (comboboxes) de dia, mês, ano, hora, minuto e segundo. Você pode até escolher quais desses comboboxes você quer que apareçam.</div><div><br /></div><div>Mas há vezes que não queremos usar estes combos, e simplesmente queremos um campo texto onde o usuário pode digitar a data num formato qualquer, como por exemplo, dd/MM/yyyy, ou dd/MM/yy, ou yyyyMMdd, ou...ou...ou...</div><div><br /></div><div>E ainda, se você tiver uma aplicação com internacionalização, aí pode ser que para usuários com Locale em português, o formato de entrada deste texto de data é dd/MM/yyyy , porém, se for em inglês, aí é MM/dd/yyyy.</div><div><br /></div><div>Bom, o objetivo aqui é mostrar como dizer para o Grails para usar este formato de entrada de datas para que ele faça o parse automaticamente toda vez que encontrar um atributo do tipo Date em um objeto de domínio (ou um objeto Command) que ele precisa fazer o bind dos parâmetros recebidos num POST (ou GET).</div><div><br /></div><div>Então:</div><div>1) O usuário preenche um campo input type="text", com uma data num formato que você determina, ex: MM/dd/yyyy</div><div>2) Ele envia este POST, que chega no seu Controller disponível no atributo params </div><div>3) você quer fazer o bind automático usando (digamos que meu objeto de domínio seja Pessoa, e nele haja um atributo <b>Date dtAniversario</b>):</div><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>Pessoa tx = new Pessoa(params)<br /></code></pre><div><br /></div>Para isso acontecer de forma correta, e o seu atributo dtAniversario estar preenchido corretamente com um valor do tipo Date, é preciso criar um PropertyEditorRegistrar customizado para você. Crie um pacote em src/groovy , digamos com.meusistema.editor , e crie lá o seu PropertyEditorRegistrar:<div><br /><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>package com.meusistema.editor;<br /><br />import java.text.SimpleDateFormat;<br />import java.util.Date;<br /><br />import org.springframework.beans.PropertyEditorRegistrar;<br />import org.springframework.beans.PropertyEditorRegistry;<br />import org.springframework.beans.propertyeditors.CustomDateEditor;<br />import org.springframework.context.i18n.LocaleContextHolder;<br /><br />public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {<br /><br /> def messageSource;<br /><br /> public void registerCustomEditors(PropertyEditorRegistry registry) {<br /> registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat(messageSource.getMessage("dateFormat4y",null,'dd/MM/yyyy',LocaleContextHolder.locale )),true));<br /> }<br /><br />}<br /><br /></code></pre><div><br /></div><div>Perceba que para funcionar corretamente, você precisa editar os seus grails-app/i18n/messages.properties (messages_en_US.properties, messages_pt_BR.properties) para que tenham cada uma a sua configuração correta. Ex para o pt_BR:</div><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>dateFormat4y=dd/MM/yyyy<br /></code></pre><div><br /></div>O passo seguinte é registrar este PropertyEditorRegistrar no arquivo grails-app/conf/spring/resources.groovy:</div><pre style="font-family: Andale Mono, Lucida Console, Monaco, fixed, monospace; color: #000000; background-color: #eee;font-size: 12px;border: 1px dashed #999999;line-height: 14px;padding: 5px; overflow: auto; width: 100%"><code>beans = {<br /><br /> customPropertyEditorRegistrar(com.manubia.propertyeditor.CustomPropertyEditorRegistrar) {<br /> messageSource = ref('messageSource')<br /> }<br /><br />}<br /></code></pre><br /><div>Pronto. É isso!</div><div><br /></div><div>Abcs a todos</div>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-4447358400792645990.post-52779922707276289852009-12-30T12:53:00.002-02:002010-01-02T00:15:08.552-02:00Como iniciar com Scrum - Bate papo com um futuro Scrum Master<span class="Apple-style-span" style="font-family: arial; font-size: small; "><div>Oi pessoal,</div><div><br /></div><div>eu comecei a orientar uma pequena empresa parceira em como iniciar a implantação do Scrum. Abaixo reproduzo meu e-mail para o membro da equipe q será o Scrum Master. Comentários? Aguardo contribuições....tenho muito interesse em conhecer diferentes realidades....</div><div>Um abraço</div><div><br /></div><div>--------------------</div><div>Minhas sugestões:</div><div>1) Você deve ser o Scrum Master da equipe</div><div>2) Estudar um pouco de scrum para entender o linguajar, e os conceitos.</div><div> a) o que é e qual é o papel do Scrum Master ( <a href="http://blog.eof.com.br/2008/03/06/qual-e-o-papel-do-scrum-master/" target="_blank">http://blog.eof.com.br/2008/<wbr>03/06/qual-e-o-papel-do-scrum-<wbr>master/</a> )</div><div> b) entender os principais conceitos: Product Backlog, Sprint Backlog. Sprint Planning Meeting, Sprint Review, Planning Pocker</div><div> ( <a href="http://laguiar.wordpress.com/2008/06/30/introducao-scrum/" target="_blank">http://laguiar.wordpress.com/<wbr>2008/06/30/introducao-scrum/</a> )<wbr> </div><div><br /></div><div>3) Criar o Product Backlog. Para isso, você deve interagir com o cliente. Ele é o Product Owner, e é ele quem sabe como gerar o Product Backlog, e é ele quem sabe priorizar este backlog. Sugiro, se necessário, criar um product backlog para cada produto do projeto ( Admin, Carrinho de Compras, Barramento SOA, etc). </div><div><br /></div><div>4) Criar um release Backlog para o release 1. Definir releases, junto com o cliente. Pegue o Product Backlog e quebre ele em dois ou três releases. Esses passos 3 e 4 são trabalhosos, e depende do seu esforço e do esforço do cliente. Você provavelmente vai ter umas 3 reuniões DE TRABALHO com ele, cada uma de umas 4 horas de trabalho. Diga a ele que é isso que você quer, que a empresa precisa: de uns 3 dias, de 4 hs, de trabalho conjunto seu e dele para gerar Product Backlogs e Releases Backlogs.</div><div><br /></div><div>5) Crie um conjunto de cartas de baralho para cada integrante da equipe. Cada um tem que ter um jogo completo dessas cartas. Sugiro comprar uma cartolina mais grossa e fazer as cartas. Tipo isso aqui: <a href="http://www.flickr.com/photos/tlaukkanen/488795952/sizes/m/">http://www.flickr.com/photos/tlaukkanen/488795952/sizes/m/</a> ...... os números das cartas são esses mesmos: 0, 1/2, 1, 2, 3, 5, 8, 13, 20, 40, 100, e ?.</div><div><br /></div><div>6) Junte toda a equipe e, juntos, façam um Planning Pocker, usando as cartas. Estimem cada item do backlog do Release 1. Como funciona este pocker:</div><div> - coloca-se no meio da mesa o item de backlog (ou fala-se o item).</div><div> - quem tiver dúvidas a respeito do item, pergunta, e as duvidas devem ser tiradas ali na hora. Todos devem entender o item de backlog.</div><div> - cada desenvolvedor escolhe uma carta. A carta representa os pontos de dificuldade de implementar o item de backlog. Um item muito facil de fazer é a carta 2. Um item dificílimo é a carta 40. As cartas menores que 2 e maior que 40 são para as exceções. Um item já feito recebe a carta ZERO.</div><div> - Após todos escolherem suas cartas, sem mostrar aos demais, todos juntos abrem suas cartas. </div><div> - Geralmente tem uma pessoa que escolhe uma carta de menor valor, e um outro que escolhe uma carta de valor mais alto. Por exemplo, um escolhe a carta 5 e outr escolhe a carta 20.</div><div> - O que escolheu o menor valor deve justificar porque escolheu um valor tao baixo. O que escolheu o maior valor deve fazer o mesmo: justificar.</div><div> - Após as justificativas, todos recolhem suas cartas e escolhem novamente outra carta (ou a mesma). </div><div> - Todos abrem suas novas cartas. Desta vez deve haver um consenso maior entre os participantes. </div><div> - Determine, finalmente, o valor de pontos deste item de backlog. O consenso é que determina. Se 4 escolheram a carta 13 e 1 escolheu a carta 20, 13 é o vencedor.</div><div> - E assim vai para todos os itens do backlog da Release 1. Faça na ordem de prioridade do backlog.</div><div><br /></div><div>7) Marque um Sprint Meeting. Uma reunião com todos da equipe, você e o Product Owner (cliente). </div><div> a) a reunião é de 4hs de duração. Não mais do que isso. Se ela começar a passar de 4hs, interrompa a reunião. Uma das características do Scrum é o conceito de janela de tempo. O tempo é fixo. O que der pra fazer é feito, o que não der, não deu. Tanto para as reuniões, quanto para o tempo do Sprint (2 semanas).</div><div> b) O cliente pode repriorizar os itens do backlog, pois esses agora já estão estimados, e o cliente pode mudar a prioridade. O cliente deve determinar a ordem de prioridade dos itens deste backlog.</div><div> c) Nesta reunião apresenta-se a lista de itens do Release 1 Backlog para todos. Caso haja alguma dúvida por parte dos desenvolvedores, este é o momento de perguntar as dúvidas ao Owner. Pode-se até mudar as estimativas de pontos do item devido ao melhor entendimento do item. É importante a equipe perguntar TUDO: testes de aceitação, comportamento de tela, requisitos técnicos, etc). Você como Scrum Master tem o papel de provocar a equipe para perguntar e ter certeza de que a equipe está entendendo.</div><div> d) A partir desses, que já estão priorizados, a EQUIPE escolhe os itens que conseguirá se comprometer para a entrega ao final do Sprint. Quem diz o quanto consegue entregar é a equipe. Neste momento a equipe está se comprometendo a entregar aqueles itens de backlog. Com isso, criou-se o Sprint Backlog. A equipe deve considerar a completude da entrega: implementação, testes, documentação.</div><div> e) Pronto. Aqui então a equipe tem o Sprint Backlog. São os items que serão desenvolvidos no Sprint que se inicia.</div><div> f) Outro ponto importate é você, Scrum Master, não permitir que o cliente induza a equipe a aceitar mais itens do que conseguiria.</div><div> g) E finalmente: o Product Owner tem que se comprometer a não alterar o Sprint Backlog durante o sprint. Nessas 2 semanas que se iniciam, o Sprint Backlog NÃO muda.</div><div><br /></div>8) Iniciar o Sprint de 2 semanas. Durante este período, seria bom a visita do Product Owner (cliente) regularmente. De 3 em 3 dias, ou algo assim. Desta forma a equipe tem a oportunidade de tirar dúvidas.<div><br /></div><div>9) O Sprint se inicia com uma reunião da EQUIPE, sem cliente. Nesta reunião, o objetivo é quebrar cada item de backlog em Tarefas técnicas: </div><div> a) Modelagem </div><div> b) Criação de scripts de Banco de Dados</div><div> c) Implementação de Regras de Negócio</div><div> d) Estudo e avaliação de tecnologia</div><div> e) Implementação de testes unitários</div><div> f) Implementação de testes de integração</div><div> g) documentação</div><div> h) Implementação de Telas (html, css, javascript)<br /><div> etc</div><div> - Cada item do Sprint Backlog deve ser quebrado em Tarefas.</div><div> - esta reunião pode ser logo em seguida da reunião de Sprint Planning Meeting que ocorreu com o Owner. Pode marcar o Meeting de manhã, e a reunião da equipe para quebrar em tarefas a tarde.</div><div><br /></div><div>10) Primeiro dia do Sprint: </div><div> a) Você, Scrum Master, promove o Daily Meeting, que é a reunião diária de 15 min, em pé! Tem que ser rápida. Aqui, cada um diz o que fez ontem, os problemas que está enfrentando, e o que fará hoje (ou amanhã). A própria equipe se organiza em dizer quem fará quais tarefas. Sempre em ordem de prioridade dos itens do backlog.</div><div><br /></div><div>11) Você, Scrum Master, deve deixar visível o Burn Down Chart. Desenhe ele no quadro para todos verem. Atualize diariamente, assim todos acompanharão o andamento do Sprint. Veja um exemplo aqui <a href="http://www.mountaingoatsoftware.com/system/hidden_asset/file/53/BurndownBarchart.xls">http://www.mountaingoatsoftware.com/system/hidden_asset/file/53/BurndownBarchart.xls</a> </div><div> - Neste gráfico, a barra representa o total de pontos que ainda faltam no Sprint. </div><div> - É importante diferenciar entre os pontos planejados do Sprint (que foram escolhidos pela equipe para formar o Sprint backlog), e os pontos que surgiram ao longo do Sprint. Coisas imprevistas que apareceram e tiveram que ser feitas. Essas, devem entrar como tarefas extras, e devem ficar evidente, para que o cliente entenda qualquer atraso ou não entrega do que foi acordado.</div><div><br /></div><div>12) Diariamente faça o Daily meeting, com todos da equipe, em pé. 15 min. Se houver algum problema, algum impedimento, que esteja causando paralisia da equipe, você como Scrum Master deve solucionar isso. </div><div><br /></div><div>13) Durante o Sprint de duas semanas, você poderia atuar como um projetista de Protótipo de Telas. Sente com o cliente, entenda os itens do backlog do Release 1 que não fazem parte do Sprint 1 (ou seja, vc estará planejando o sprint seguinte, enquanto a equipe trabalha no sprint atual). Você poderia ir criando protótipo de telas que seriam aceitos pelo cliente, e direcionariam o desenvolvimento da equipe. Power point é uma ferramenta simples e eficiente para isso. Mando um exemplo em anexo.</div><div><br /></div></div></span>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-4447358400792645990.post-5754948628393604412009-12-17T11:55:00.002-02:002010-01-02T00:15:49.080-02:00O que todos deveriam saber sobre MysqlNestes tempos de desenvolvimento com frameworks que tentam (e conseguem) facilitar muito nossa vida abstraindo detalhes de baixo nível, muitas vezes deixamos de nos preocupar com o que se passa lá por baixo.<div><br /></div><div>Mas, em aplicações que tendem a crescer, que com o tempo vão deteriorando em performance, muitas vezes os problemas estão no banco de dados, na forma como acessamos o banco, em como construímos nossas queries...</div><div><br /></div><div>Esta apresentação aqui, apresentada no Sun Tech Days 2009, é <b>muito boa</b> para quem usa Mysql.</div><div><a href="http://developers.sun.com/events/techdays/presentations/2009/pdfs/TD_SP_MySQL_Karlsson.pdf">http://developers.sun.com/events/techdays/presentations/2009/pdfs/TD_SP_MySQL_Karlsson.pdf</a></div><div><br /></div><div>Abcs</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-42254741114166269362009-12-05T13:32:00.003-02:002010-01-02T00:17:42.665-02:00Grails 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.<br /><br />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.<br /><br />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....<br /><br />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? <div><br /></div><div>Alguns casos reais confirmam que a resposta é sim. Por exemplo, o <a href="http://www.linkedin.com/">Linkedin.com</a> usa Grails para algumas partes. Este site tem as <a href="http://www.slideshare.net/glaforge/grails-at-linkedin-455651">seguintes características</a>: 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.</div><div><br /></div><div>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). </div><div><br /></div><div>Um grande player neste mundo de Cloud é a Amazon, com seu <a href="http://aws.amazon.com/">AWS</a> . Mas como desenvolver uma aplicação Grails para rodar neste ambiente virtual? A nuvem está lá, mas você não vê. Tem <a href="http://aws.amazon.com/ec2/">solução poder computacional escalável</a>, <a href="http://aws.amazon.com/s3/">solução de storage</a>, de <a href="http://aws.amazon.com/rds/">banco de dados relacional</a> na nuvem, e muito mais....</div><div>Mas como usar isso tudo? Como fazer uma aplicação Grails rodar nesta infra poderosa, mas complexa?</div><div><br /></div><div>Aí que eu acho que a SpringSource está dando um show! Primeiro os caras inventam o <a href="http://www.springsource.com/products/enterprise">Spring</a>, que é solução para DESENVOLVIMENTO. Depois começam a expandir <a href="http://www.springsource.com/products/run">para servidores</a>, que é solução para EXECUÇÃO. Depois fornecem solução para <a href="http://www.springsource.com/products/sts">ferramentas</a> ajudando mais ainda no quesito DESENVOLVIMENTO. Aí lançam produtos para <a href="http://www.springsource.com/products/manage">gerenciar e monitorar</a> (MONITORAMENTO). Por último, lançam um serviço que te ajuda <a href="http://www.springsource.com/products/cloudfoundry">executar aplicações java na infra de Cloud</a> da Amazon: <a href="http://www.cloudfoundry.com/">Cloud Foundry</a>.</div><div><br /></div><div>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. </div><div><br /></div><div>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 <a href="http://www.grails.org/blog/chasylee/Cloud+Foundry+plugin+released">Cloud Foundry Plugin</a> 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. </div><div><br /></div><div>Na teoria é lindo. Temos que ver na prática agora.</div><div>t+</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-4447358400792645990.post-34861688610509992142009-10-14T16:13:00.003-03:002010-01-02T00:16:14.860-02:00Enviando 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.<br /><br /><a href="http://www.campaignmonitor.com/css/">http://www.campaignmonitor.com/css/</a><br /><br />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.<br /><br />Abcs<br />FelipeUnknownnoreply@blogger.com1tag:blogger.com,1999:blog-4447358400792645990.post-12144153130392649682009-09-29T11:18:00.002-03:002010-01-02T00:16:14.860-02:00Serviços do Grails, Transações e Rollback automáticoComprei o livro recente que saiu do Glen Smith, o <a href="http://www.manning.com/gsmith/">Grails in Action</a>. 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:<div><br /></div><div>Nos serviços do grails ( classes *Service.groovy, dentro do diretório services), que possuem <a href="http://grails.org/doc/latest/guide/8.%20The%20Service%20Layer.html#8.1 Declarative Transactions">Transação Declarativa</a> , 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.</div><div><br /></div><div>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! :-)</div><div><br /></div><div>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.</div><div><br /></div><div>Então amigos: apenas classes derivadas de RuntimeException fazem o rollback acontecer.</div><div><br /></div><div>Abcs</div>Unknownnoreply@blogger.com3