Sep 8 2009

Você deveria considerar Erlang para seu próximo grande projeto

Ao ler o título deste post, talvez você esteja se perguntando: por que eu deveria considerar Erlang para meu próximo grande projeto? Bem, meu objetivo com este post é te apresentar alguns importantes motivos para fazer isto.

Erlang nasceu no laboratório de ciência da computação da Ericsson na década de 1980, influenciada por linguagens como ML, Ada, Module, Prolog e Smalltalk, quando um time de três cientistas – entre eles, o grande Joe Armstrong receberam a missão de descobrir qual seria a melhor linguagem de programação para escrever a próxima geração de sistemas de telecom. Após dois anos de investigação e prototipação, estes cientistas descobriram que nenhuma linguagem era completa o bastante para tal objetivo e, conclusivamente, decidiram criar uma nova linguagem. Nasceu então Erlang, the Ericsson Language.

De lá pra cá, Erlang tem sido evoluida e usada para escrever grandes sistemas críticos, porque é exatamente nesse cenário que Erlang faz mais sentido. Se seu projeto é construir um sistema crítico, altamente tolerante a falhas, com disponibilidade 24×7 e veloz como o papa-léguas, sim, Erlang é para você. Mas se não é bem esta sua necessidade, se você quer apenas construir um pequeno sistema, com baixa concorrência, poucos usuários, pouca necessidade de performance e possibilidade de passar horas down em manutenção, não, você não precisa de Erlang. Que tal Basic?

Diferente de algumas linguagens que nascem para encontrar um nicho, Erlang nasceu com um problema a ser resolvido, com requisitos a serem atendidos. Tolerância a falhas, concorrência realmente pesada, computação distribuída, atualização da aplicação sem derrubá-la, sistemas de tempo real, este é o nicho de Erlang; foi para isto que Erlang nasceu.

Quem usa Erlang atualmente?

Além da Ericsson, é lógico, há algumas outras grandes empresas e projetos usando Erlang, como por exemplo:

- Facebook, no backend de seu sistema de chat, lidando com 100 milhõs de usuários ativos;
- Delicious, que tem mais de 5 milhões de usuários e mais de 150 milhões de bookmarks;
- Amazon SimpleDB, o serviço de dados do seu poderoso EC2;
- Motorola, CouchDB, RabbitMQ, Ejabbed, etc.

Ok, mas Erlang é propriedade da Ericsson?

Não, felizmente, não. Em 1998 a Ericsson tornou Erlang open source sob a licença EPL.

Quer mais uma boa notícia? Você não precisa de um servidor de aplicações para rodar sua aplicação Erlang, nem mesmo uma pesada IDE para escrevê-las. Tudo o que você precisa é de uma distribuição de Erlang para sua plataforma, seja Mac OS X, Linux, Windows, Solaris, ou qualquer outro sistema Unix-like, que traz consigo máquina virtual, compilador e vasta bibliotéca de módulos muito úteis – além do banco de dados Mnesia; e um editor de textos de sua preferência TextMate, por exemplo, tem um ótimo bundle para Erlang.

Algumas características de Erlang

1- Expressividade e beleza

Há quem diga que não, mas Erlang é uma linguagem muito bonita — ao menos pra mim. Dado as já citadas influências de Erlang, ela é uma linguagem bem expressiva e declarativa, que encoraja a escrita de código simples e objetivo, o que certamente torna seu código fácil de ler, de escrever e de organizar [em módulos reutilizáveis]. O snippet abaixo é um exemplo de implementação do famoso fatorial:

-module(sample1).
-export([fac/1]).

fac(0) -> 1;
fac(N) -> N * fac(N-1).

2- Forte uso de recursividade

Isto certamente é uma herança da veia funcional de Erlang que torna o código muito menos imperativo e mais declarativo. Mas além da beleza declarativa óbvia, é importante saber que Erlang foi projetada para lidar com iterações recursivas gigantescas sem qualquer degradação – e sem estourar o “heap” de memória.

3- Livre de efeito colateral

Uma das grandes capacidades dadas por Erlang é a construção de sistemas altamente concorrentes rodando em processadores com multiplos núcleos. Isto não combina nada com efeito colateral, por isso, em Erlang, uma vez que um dado valor tenha sido atribuído a uma variável, esta não poderá mais ser modificada – ou seja, estão mais para o que conhecemos por constantes do que para o que conhecemos por variaveis.

Se você já escreveu código concorrênte sabe o quanto tê-lo livre de efeitos colaterais te faz livre de dores de cabeça. Poucas coisas são tão deprimentes quanto debugar código concorrênte repleto de efeitos colaterais.

4- Pattern matching

Pattern matching em Erlang é usado para associar valores a variáveis, controlar fluxo de execução de programs, extrair valores de estruturas de dados compostas e lidar com argumentos de funções. Mais um ponto para código declarativo e expressividade. Vejamos o código abaixo:

-module(sample2).
-export([convert_length/1]).

convert_length(Length) ->
    case Length of
        {centimeter, X} ->
            {inch, X / 2.54};
        {inch, Y} ->
            {centimeter, Y * 2.54}
    end.

Fala por si, não?

5- Concorrência baseada em passagem de mensagens (a.k.a. Actors)

Acho que concorrência baseada em passagem de mensagem entre atores é uma das features mais populares de Erlang. Vejamos o porque com o famoso exemplo do Ping-Pong:

-module(sample3).

-export([start/0, ping/2, pong/0]).

ping(0, Pong_PID) ->
    Pong_PID ! finished,
    io:format("Ping finished~n", []);

ping(N, Pong_PID) ->
    Pong_PID ! {ping, self()},
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,
    ping(N - 1, Pong_PID).

pong() ->
    receive
        finished ->
            io:format("Pong finished~n", []);
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    end.

start() ->
    Pong_PID = spawn(sample3, pong, []),
    spawn(sample3, ping, [3, Pong_PID]).

Neste pequeno snippet podemos observar algumas características de Erlang já citadas neste post, tal como pattern matching na captura das mensagens e recursividade no controle das iterações.

Agora, falando do aspecto concorrente em sim, algumas coisas são particularmente interessantes aqui:

a. Em Erlang, a concorrência acontece entre processos leves, diferente de linguagens como C++ e Java, que baseiam sua concorrência em threads nativas de sistema operacional [caríssimas];
b. Em Erlang, há um tipo de dado chamado PID, o qual é o identificador do processo paralelo (mais conhecido como Actor) e para o qual as mensagens podem ser enviadas.

Releia o código acima com estas informações em mente e veja como concorrência em Erlang é algo completamente descomplicado e natural. Depois, pense no mesmo código implementado em C#, Java ou C++.

Gostei de Erlang, há algum livro para eu começar a estudar?

Sim, há dois livros excepcionais. Um, do próprio criador da linguagem, Joe Armstrong:

E outro de Francesco Cesarini e Simon Thompson:

Além disso, há o próprio material on line que é muito bom e barato. :)

Conclusão

Erlang possui muitas outras características e informações bem interessantes, mas que me falta espaço neste post para citar e apresentar, senão ele ficará absurdamente gigantesco para o meu gosto. Mas acredito que pelo que já apresentei até aqui, você já tenha motivos suficientes para pensar em Erlang com carinho e conciderá-la para seu próximo grande projeto.

Em breve devo escrever algo sobre OTP, já que neste post apresentei características mais inerentes à linguagem em si e nem tanto sobre a plataforma.

Até mais!


Jul 30 2009

Quer saber onde será seu próximo trabalho?

Então não deixe de visitar…

O site é recém-nascido, mas já tem uns recursos bem legais, como sistema de pesquisa por palavras-chave, núvem de tags e entre outros. Vale a pena conferir e acompanhar sua evolução – e oportunidades de bons trabalhos.

Mais uma iniciativa bem interessante da Caelum. :)


May 25 2009

Então, falando em Java…

Ontem aconteceu a terceira edição do já tradicional e indispensável evento Falando em Java, organizado pela Caelum em São Paulo.

O evento, como tudo que a Caelum faz, foi muito bem organizado, num excelente espaço, com ótimos coffebreaks (eu perdi o do almoço, porque fui comer comida chinesa no Hong He. #fail) e boas pessoas. Afinal de contas, como disse o Sério Lopes e o Fábio Kung na abertura do evento, a Caelum são as pessoas.

O que achei das apresentações?

As duas apresentações do Jim Webber foram fantásticas, mostrando uma visão bem realista de SOA, ESB e outras buzzworlds do momento, e dando uma boa luz no caminho dos serviços baseados na Web, REST e microformatos. Com certeza foram palestras que me fizeram reafirmar uma antiga crença: Você aprende muito com seus erros; e mudar de opinião porque aprendeu um pouco mais sobre algo, não é demerido nenhum, muito pelo contrário, é sinal de amadurecimento. O Jim é um exemplo disso.

Só tenho uma coisa a acrescentar, Jim: Quem mata gatinhos pequeninos quando alguém usa URL em lugar de URI, ou faz de um ESB um grande espaguete digno de festa do Bexiga, é o diabo, não Deus. Anote isso ai pra sua próxima aprensentação. :)

A palestra do Guilherme Silveira junto com o Felipe Sabella teve uma ótima dinâmica, muito bem entrosada e com conteúdo homeopaticamente dosado. Nota 10. Mas infelizmente, nem todas as duplas tiveram o mesmo entrosamento, o que em alguns momentos acabou prejudicando bastante a mensagem das apresentações. Uma pela.

Destaque para o Paulo Silveira que, como sempre, não deixou a peteca cair e salvou a palestra; para o Ricardo Nakamura, que cativou a galera de primeira com sua revelação de que dorme com o pijama do Jaspion; e para o Kung que sempre tira uma carta ou duas sinistraças da manga.

O Sérgio e o Guilherme Moreira, no entanto, a pesar de dominarem o assunto que apresentaram, me deixaram um tanto decepcionado, porque a palestra não me pareceu muito condizente com o tema proposto. Por outro lado, devem ter feito vibrar os hibernate buys.

E claro, hehehe, eu não podia deixar de citar o mico o imprevisto do cluster com JBoss não funcionar na apresentação deles. Não por culpa do JBoss, é lógico, porque todos sabemos que há várias empresas mundo afora que rodam JBoss em cluster.

Eu mesmo fui um dos que entrou na zueira, mesmo não tendo nada contra o JBoss. Muito pelo contrário, é um dos meus preferidos – talvez por ainda não ter tentado colocá-lo em cluster. rsrs

Fica aqui minha remissão. I’m sorry!

Ponto positivo também pras Caelets, muito discretas e distintas, bem distantes da vulgaridade que vemos em muitos eventos por ai. Gostei do bom senso.

Ah! Tem também uma novidade que vale a pena citar: Logo mais teremos o livro da Caelum de Arquitetura e Design de Software à luz da Plataforma Java, com prefácio do gujeiro velho de guerra, Phillip Calçado. Se não me engano, o lançamento será em novembro.

Bem, no mais, é isso. Se você quiser uma cobertura mais completa sobre o evento, dê uma lida nas minhas tuitadas. Eu ralei pra caramba pra tuitar o evento interinho, não vou digitar tudo outra vez. Dá uma forcinha, vai. Dá uma olhada lida lá. :)

Até a próxima!


Feb 22 2009

Message-Driven Beans e Transações

Sexta-feira passada, postei  em meu twitter que estou tocando um projeto que envolve MOM. Hammm… JMS, para ser mais específico… rsrsrs…. Bem, o fato é que este projeto acabou me motivando a escrever este post sobre o assunto transação e EJB3 Message-Driven Beans (MDB),  apenas para fazer alguns lembretes e dar algumas dicas, já que este é um assunto fundamentalmente importante. Vamu que vamu então…

A primeira coisa importante a saber é que transações não são propagadas do produtor de mensagens JMS para o MDB que as consome, independentemente destes trabalharem com CMT ou BMT. Isso se dá pela própria natureza assíncrona de um sistema baseado em MOM, uma vez que uma mensagem pode levar horas para ser consumida, se assim fizer sentido ao sistema. Assim sendo, uma transação de um MDB está limitada ao escopo do método onMessage, que recebe e trata as mensagens.

A segunda coisa importante a saber é que quando o MDB faz o seu próprio gerenciamento de sua transação, a mensagem consumida não é parte transação. Ao passo que, se a transação do MDB for gerenciada pelo contêiner EJB, sim, a mensagem é parte da transação.

Sabendo essas duas coisas, somos levados a uma questão: O que acontece entãos se a transação falhar? Bem, está é a terceira coisa importante que precisamos saber. Sempre que o método onMessage completa sua execução sem erros, o contêiner EJB notifica ao provedor JMS reconhecendo o recebimento da mensagem. No entanto, se o correr uma RuntimeException durante a sua execução, o provedor JMS não receberá esta notificação de reconhecimento e, muito provavelmente, disponibilizará a mensagem para ser novamente consumida pelo MDB. Isso poderia causar um problema sério de loop infinito, em caso de poison messages, mas felizmente há antidoto para isso: Configurar o número máximo de tentativas de entrega de mensagens. Uff!!!

Como lidar então com exceções, rollback e reentrega de mensagens?

Há momentos em que a reentrega de mensagens é fundamental; e há momentos que não. Se ocorrer uma exceção de negócio, por exemplo, você não vai querer que a mensagem seja entregue novamente. Mas, se ocorrer um exceção por indisponibilidade de uma recurso (banco de dados, web service, etc), seria fundamental que a mensagem fosse entregue novamente, depois de algum tempo. Com isso em mente, vamos discutir um pouquinho o assunto.

Primeiro de tudo, não se esqueça que MDB’s não retornam exceções ao cliente que produziu a mensagem. Se a sua lógica de negócio em cima da mensagem produzida pelo cliente prevê uma exceção, pare e pense um pouco a respeito. Se você puder responder a ele com uma outra mensagem que ele (ou outro MDB responsável por notificar clientes) poderá consumir no provedor JMS, ótimo; caso contrario, esqueça, mensagens assíncronas não é uma opção para você.

Seguindo adiante com a solução, você precisa saber como MDB’s CMT ou BMT se comportam quando o assunto é transação. As diferenças básicas entre esses dois modelos de gerenciamento de transações são as seguintes:

Se seu MDB está baseado em CMT, significa que as mensagens que ele consume são parte da transação. Portanto, se a transação sofrer rollback, automaticamente, o consumo da mensagem também será revertido e o provedor JMS deverá tentar entregar a mensagem novamente.

Já, se seu MDB está baseado em BMT, significa que se a transação sofrer rollback o consumo da mensagem não será revertido, como acontece automaticamente no caso de CMT, e você poderá manter isso em segredo do provedor JMS, evitando que ele entregue a mensagem novamente. Ou, se fizer sentido ao seu requisito, você poderá notificá-lo do rollback de maneira bem simple e segura, basta lançar uma EJBException que é pá/pum.

Um pouco mais sobre Reconhecimento de Mensagem

A propriedade acknowlodgeMode, que determina o modo de reconhecimento da mensagem, pode ser definida como Auto-acknowledge ou Dups-ok-acknowledge. A primeria opção, instrui o contêiner EJB a notificar o reconhecimento da mensagem dentro do contexto da transação, seguindo as regras já citadas; e a segunda, o instrui a fazer isso num outro momento qualquer.

Essa propriedade pode ser ignorada, a menos que você esteja implementando um BMT ou um CMT com atributo de transação definido como NotSupported. Do contrario, o reconhecimento sempre ocontecerá no contexto da transação.

Se quiser saber mais, indico que você leia esse material.

Resumindo: Tome cuidado com suas exceções e não deixe que elas explodam sem mais nem menos, sem um tratamento adequado, nem nada, porque elas podem detonar suas transações e ainda causar problemas de negócio, com a reentrega de mensagens. Um bom log também vai muito bem, meu caro! :)

Bom, é isso ai… Espero que te ajude!

E se você ainda não leu esses dois posts: Cuidado com suas exceções e Transacionando EJB3 Session Beans, quando tiver um tempinho, leia. Talvez lhe sejam úteis também.

Até a próxima!


Jan 30 2009

Web services RESTful em Java com Jersey

Estou estudando hoje a JSR 311 e sua implementação de referência, o projeto Jersey, por conta de um requisito arquitetural que surgiu em meu novo projeto aqui na CVC Turismo. Trata-se da integração de várias aplicações legadas, desenvolvidas com tecnologias completamente heterogêneas – Java, ASP e PL/SQL Web Toolkit.

Estamos, eu e meus colegas da equipe de arquitetura, considerando bastante o uso de web services REST. Por isso meu interesse pelo Jersey, uma vez que a plataforma base desse projeto é Java.

Para aqueles que se interessam pelo assunto, tempos atrás gastei investi um tempo lendo o material produzido pelo Bruno Pereira, o qual considero a melhor referência de REST em lingua portuguesa. (Jabá!!!)

Hoje resolvi ler também um de seus artigos produzidos para a revista Java Magazine – que IMHO não é lá uma ótima revista de Java, mas que volta e meia publica um ou dois artigos interessantes -, para refrescar a memória.

Taí! Para quem quer ter um primeiro contato com Jersey e a JSR 311, ficam aqui as dicas.


Jan 29 2009

Por que não tentar de outra maneira?

Eu nunca programei uma única linha sequer de código em Clojure, apenas dei uma boa olhada no site oficial da linguagem para conhecer sua proposta – que é muito interessante, digasse de passagem – e tenho acompanhado as notícias que rolam aqui e ali. (Coisa de quem é vidrado por programação.)

No entanto, hoje, li uma notícia sobre Clojure que me fez pensar um pouco além da própria programação…

“Por que as pessoas normalmente não tentam fazer as coisas de outra maneira?”

Achei muito interessante o fato de terem usado Clojure, que é uma linguagem jovem, para implementar concorrência em um grande e importante sistema de informação, mesmo não havendo ainda relatos de que já o tenham feito antes.

Inovação, pioneirismo, pragmatismo… Pensamento fora da caixa!!!

Isso também é sobre ser inteligente. Se já inventaram a solução do problema de concorrência, de maneira elegante, por que ainda ficar batendo cabeça com linguagens “consolidadas”, mas que não o resolvem tão eficientemente? Aham? Por que a linguagem ainda é muito nova?

Esse é só um exemplo, mas há muitos outros em nosso dia a dia. Dê uma olhada ao seu redor. Dê uma olhada em seus próprios desafios. Aliás, aproveite, e dê uma olhada em suas próprias soluções!

O quanto você tem sido tradicionalista e conservador?

O medo das novas soluções, das novas maneiras de fazer as coisas, normalmente, são um atraso para os novos bons resultados. Afinal de contas, normalmente, quem sai na frente lucra primeiro…


Aug 22 2008

Erros primários que um arquiteto não pode cometer

Assumir o boné de arquiteto em um projeto de software é uma tarefa que requer maturidade, muito além do simples conhecimento técnico, pois boa parte do sucesso de um projeto de software depende de decisões arquitetônicas.

Presto consultoria nessa área há algum tempo, e não foi uma nem duas vezes que vi pessoas cometerem erros primários ao assumirem esse boné – ao ponto de quase levarem projetos à ruina. Baseado nessa experiência, quero citar cinco erros primários que um arquiteto pode cometer ao ignorar:

1- O time de desenvolvimento

Não, desenvolvedores não são como apertadores de porcas e parafusos de linha de produção. Se você ainda acredita no conto de fadas do processo fabril para desenvolvimento de software, sinto lhe dizer que isso não passa de balela. A verdade é que desenvolvimento de software é um processo criativo, mais humano que mecânico.

Assim, é preciso levar em conta que cada time de desenvolvimento é diferente do outro, tendo qualidades e deficiencias que de maneira alguma podem ser ignoradas. Ao contrario disso, devem ser colocadas sobre a mesa e ponderadas antes de quaisquer decisões arquitetônicas. Ignorar isso pode ser a ruina do projeto.

Portanto, não basta somente o “sr arquiteto” dominar as técnicas e tecnologias escolhidas para o projeto. É preciso que todo o time de desenvolvimento também esteja apto a operacionalizar cada decisão com qualidade.

Um bom arquiteto precisa saber trabalhar com o conhecimento e a experiência do time. Precisa constantemente promover a evolução técnica do time, mas também saber abrir mão da última versão daquele framework mais legal de todos, quando o time ainda não está apto a usá-lo e não há tempo hábil para capacitação.

Outra coisa fundamental é tornar o time de desenvolvimento participante nas decisões arquitetônicas do projeto. Isso torna o time mais convicto das decisões e também mais comprometido com a sua operacionalização.

2- O escopo do projeto

Não é incomum ver arquitetos querendo conceber soluções que vão atender os requisitos de hoje e os da próxima década. Isso é péssimo, porque leva a elocubrações sem fim. Além, é claro, do iminente risco de construir uma arquitetura frankenstein.

“Ah! Mas e se daqui a um ou dois anos vocês quiserem que o sistema de pagamento possa também receber pagamentos por moeda estelar? Será que não seria melhor se modelassemos algo mais flexivel?” – ah meu Deus!

Tenha bem claro qual o objetivo do projeto, onde ele começa e onde ele acaba. Não fique aquém, nem vá além.

Ah! E não se esqueça de deixar também o time de desenvolvimento bem ciente desses limites.

3- A compatibilidade

Não estabelecer claramente as versões de sistema operacional, banco de dados, IDE, frameworks e bibliotecas, com as quais o software em desenvolvimento deve ser compativel é roubada. Porque o que funciona na versão 1.0 do framework XPTO, talvez não funcione na versão 2.0. Quem garante? Eu que não me atrevo!

Já enfrentei tantos problemas trabalhando em projetos onde não existia essa prática que até me dá arrepio pensar nisso. Você debuga pra cá, debuga pra lá, e quando vai ver, foi um bendito JAR que foi mudado e ninguém te avisou, ou mesmo se preocupou em saber se quebraria o que você fez. Pior ainda quando isso acontecia no momento da implatação do produto final. Urgh!!!

Agora, como fazer isso de maneira eficiente? Bem, para o caso de bibliotecas e afins, sugiro que você dê uma olhada no Artifactory, ele serve de repositório para o Maven. E para sistema operacional, banco de dados, IDE, e outros elementos de infra, um quadro na parede já resolve bem o problema.

4- O design evolutivo

A chave para o design evolutivo é fazer o mais simples possível e ir refatorando depois, conforme a necessidade. Sim, o estado da arte nunca acontece na primeira implementação; tentar fazer isso pode leva-lo à ruina.

Seja prático. Primeiro tenha algo funcional; depois, evolua.

5- As opções de ferramentas

Como se diz “se você só tem um martelo, tende a enxergar todos os problemas como um prego”. Isso é perigoso, porque te faz desperdiçar tempo e dinheiro – e ainda conceber soluções burras.

Faz um bom tempo que o Phillip escreveu este post e ele ainda é muito atual. Infelizmente. Sim, infelizmente, porque ainda tem muita gente que se recusa a ter mais do que um martelo em sua caixa de ferramentas.

Trabalho com Java há muito tempo, mas hoje esta não é a única ferramenta em minha caixa. Há tantas ferramentas boas no mercado, porque ignorar isso?

Há ainda muitos outros erros, certamente…

Que tal você registrar alguns nos comentários deste post?

Valeu!


Aug 14 2008

Executar JRuby a partir do Java

Nos últimos tempos tenho dedicado boa parte do meu tempo livre estudando JRuby. Tem sido uma verdadeira diversão!

Com o objetivo de compartilhar um pouco do que tenho aprendido, eis aqui este post…

Quer dizer que o osrevni inverso também é verdade?

Muito se fala da capacidade do JRuby de acessar código Java de maneira tão natural quanto o faz com seu próprio código – o que é definitivamente fantástico. Mas não tenho visto muitos exemplos de código Java acessando código JRuby. Por quê? Não sei dizer. Talvez porque não tenham visto tanta utilidade nisso. Essa não é minha opinião, já que tenho interesse em implementar algumas coisas em JRuby e usar a partir do Java.

Uma das coisas que fiz nesses meus estudos sobre usar JRuby a partir de Java foi testar o compilador jrubyc para gerar .class, mas não cheguei bem onde eu queria – até troque umas palavras com Charles Nutter a respeito -, porque o .class gerado por ele não é do tipo que se pode instanciar e usar diretamente num código Java, dada a natureza totalmente dinâmica de Ruby. Nas palavras do próprio Charles:

The code compiled by jrubyc is not a “normal” Java class[...] This is not a Java class you can instantiate and call methods on directly from Java[...].

Como não desisti, tenho algumas alternativas para compartilhar.

OBS.: Para executar os exemplos apresentados é necessário ter jruby.jar no classpath do seu projeto. Quando escrevi este post estava usando a versão 1.0, porque não havia uma versão mais atual na máquina que eu estava usando. Mas agora já atualizei o código para a versão 1.1.3.

Primeira alternativa: JRuby puramente Ruby

No exemplo abaixo, criou uma classe Ruby comum – sem qualquer recurso específico do JRuby – e, logo após, a carrego, instancio e executo seu método a partir do Java.

matematica_apenas_ruby.rb

class MatematicaApenasRuby
  def soma(a, b)
    a + b
  end
end

MatematicaApenasRubyTest.java

public class MatematicaApenasRubyTest {
    public static void main(String args[]) throws Exception {
        List pathsLoad = new ArrayList();
        pathsLoad.add("/Workspace/Ruby/IntegracaoJava/lib/");

        Ruby rubyRuntime = JavaEmbedUtils.initialize(pathsLoad);
        rubyRuntime.getLoadService().load("matematica_apenas_ruby.rb", false);

        Object mat_ruby = rubyRuntime.evalScriptlet("MatematicaApenasRuby.new");
        Integer res_ruby = (Integer)JavaEmbedUtils.invokeMethod(rubyRuntime, mat_ruby, "soma", new Integer[] {3, 2}, Integer.class);

        System.out.println("Soma 3 + 2 invocando diretamente JRuby: " + res_ruby);
    }
}

O que esse código Java faz é bem simples, ele:

1- Inicializa um ambiente de runtime para Ruby, indicando onde estão os arquivos .rb;
2- Executa o script de criação da classe MatematicaApenasRuby;
3- Executa o script de instanciação da classe MatematicaApenasRuby;
4- E, por fim, invoca o método soma passando dois parametros.

Que tal, acho simples? Pois muito bem, continuemos…

Segunda alternativa: JRuby implementando Java

Neste segundo exemplo, o que eu faço é criar uma classe JRuby que estende uma classes Java abstrata, o que facilita ainda mais na hora de usar a partir do Java. Vejamos como fica.

matematica_impl_java.rb

require 'java'

class MatematicaImplJava < Java::IntegracaoPoliglota::Matematica
  def soma(a, b)
    a + b
  end
end

Matematica.java

package integracao.poliglota;

public abstract class Matematica {
    public abstract int soma(int a, int b);
}

MatematicaImplJavaTest.java

public class MatematicaImplJavaTest {
    public static void main(String args[]) throws Exception {
        List pathsLoad = new ArrayList();
        pathsLoad.add("/Workspace/Ruby/IntegracaoJava/lib/");

        Ruby rubyRuntime = JavaEmbedUtils.initialize(pathsLoad);
        rubyRuntime.getLoadService().load("matematica_impl_java.rb", false);

        Object mat_impl_java = rubyRuntime.evalScriptlet("MatematicaImplJava.new");
        Matematica matematica = (Matematica)JavaEmbedUtils.rubyToJava(rubyRuntime, (IRubyObject)mat_impl_java, Matematica.class);

        System.out.println("Soma 10 + 2 usando a interface Java: " + matematica.soma(10, 2));
    }
}

E esta alternativa, gostou? Eu gostei bastante. Porque no meu caso, o que eu quero é poder definir uma interface em Java e implementar com JRuby a la Ruby Way e depois usar no Java. É claro que não estou levando em conta o fator “performance”, só estou considerando o fator “alternativa de implemententação”. Só isso.

Mas vamos lá, o que esse código faz?

1- A classe JRuby estende uma classe Java abstrata, como dito antes;
2- A classe que faz o teste, em linhas gerais, faz um cast do objeto JRuby para a classe Java abstrata;
3- E no final das contas, invoca o método da classe Java.

Será que programação poliglota é o futuro?

Se é ou não é, eu não sei. Mas sei que deixei de ser um arquiteto de uma nota só há muito tempo; e estou muito emplogado com JRuby – ele é o melhor dos dois mundos!

E você, o que acha? Deixe um comentário…


Aug 12 2008

Cuidado com suas exceções!

Um tema bastante trivial, mas não pouco importante, são as sempre presentes Exceções. Outro dia desses me deparei novamente com elas – em um dos projetos que presto consultoria [em arquitetura] – e resolvi escrever este post, como uma pequena “dica”, digamos assim, para quem ainda não está totalmente seguro com o tema. Por fim, ele também servirá como complemento ao meu post anterior que aborda o tema transações em EJB3 Session Beans.

Conceituando as coisas

Em Java há dois tipos de exceções:

- Checadas (checked),
- E não checadas (unchecked).

As exceções checadas são identificáveis em tempo de desenvolvimento e, obrigatoriamente, devem ser capturadas (try…catch) e tratadas – seja com uma mensagem “amigável” ao usuário, ou com um algoritmo alternativo, ou seja lá como for. Estas exceções são identificáveis em tempo de desenvolvimento, por isso, são muito uteis na hora de sinalizar que uma “regra de negócio” foi violada – também conhecidas como exceção de aplicação – e, portanto, algo deve ser feito.

Alguns exemplos deste tipo de exceção seriam:

- MaioridadeNaoIdentificavelException
- ValorPagamentoMenorTaxaEmbarqueException
- DataReservaInvalidaException

Já as exceções não checadas normalmente não são identificáveis em tempo de desenvolvimento, por isso são conhecidas como exceções de runtime – e não coincidentemente, são filhas de RuntimeException. Estas exceções geralmente (mas não invariavelmente) denotam erros de sistema que não são recuperáveis.

Exemplos destas exceções na própria API do Java são:

- SecurityException
- NullPointerException
- MissingResourceException

E, como uma prática comum, também é bom notar que exceções não checadas não são declaradas na clausura throws dos métodos os quais podem lançá-las.

Tomando decisões

Quando você entende este conceito fica simples saber quando usar uma ou outra, não? Sim. Quer dizer, sim, mas também, não! Mas por que não? Hammm… Veja só…

IllegalArgumentException denota que um parâmetro inválido foi passado a um método, correto? Sim, é isto que diz a documentação. Bem, neste caso, imagine que você estivesse escrevendo um método de negócio, e neste método você tivesse que consistir os seus parâmetros. Imaginou? Tá. Agora, o que você faria se um dos parâmetros fosse invalido?

a) Lançaria uma IllegalArgumentException
b) Criaria uma exceção própria [checada] para denotar parâmetros inválidos

Sem pensar muito, você ficaria tentado a optar por (a), certo? Creio que sim. Mas esta não seria uma boa opção, se estes parâmetros forem realmente essenciais para o dado método; e se for possível para o código que executa este método tomar uma “decisão de negócio”, se souber que um ou mais parâmetros são inválidos. Neste caso, o melhor é a opção (b). É preciso ficar atento para escolher a melhor opção em cada situação.

Um pouco sobre exceções em EJB3

Todos os métodos de um EJB3 Session Bean lançam uma exceção do tipo EJBException, que é não checada, caso algo de errado aconteça. Isto automaticamente desencadeia um processo de rollback na transação atual, e grava um registro de log no application server para conhecimento do administrador do sistema.

Como fica então se quisermos lançar nossas próprias exceções? É muito simples, mas é também preciso se ater a um detalhe:

“Se a exceção que você lançar for não checada, ela será automaticamente encapsulada por uma EJBException, o que torna o tratamento desta exceção nada fluente na aplicação cliente, uma vez que não será pego de maneira ‘especifica’ pela seu bloco catch. Isto quer dizer que você somente conseguirá capturar uma EJBException, e não uma NaoConseguiEnviarEmailException, por exemplo.”

No caso deste meu cliente, que usa Oracle Application Server, a EJBException encapsula uma OracleRemoteException, que por sua vez encapsulada a exceção que de fato foi lançada. Que beleza, né? Beleza nada, uma praga! rsrsrs

Então, cuidado! Se você estiver trabalhando com EJB3 e quiser lançar uma exceção não checada, não se esqueça que o cliente de seu EJB não saberá (de forma natural, com um simples try…catch) que exceção realmente foi lançada.

Aqui permanece então o que foi dito acima:

- Exceção de negócio, prefira que seja checada,
- E de sistema, prefira que seja não checada, caso realmente não possa trata-la.

Talvez agora você esteja se perguntando: Mas e a transação, como fica? Ela será abortada quando uma exceção checada for lançada?

A resposta é… Não, ela não será abortada. Porque nem sempre uma exceção de negócio requer um rollback de transação. Aliás, também não será registrado qualquer log no application server – porque erros de negócio são irrelevantes a administradores de sistema.

Mas nem tudo esta perdido. EJB3 provê uma anotação para possibilitar que você aborte uma transação caso uma dada exceção checada ocorra.

@javax.ejb.ApplicationException tem um atributo rollback que pode ser definido como true ou false, indicando que a transação deve ser abortada ou não. Assim, basta fazer esta anotação em sua exceção e pronto!

Essa é uma maneira fácil de garantir a atomicidade de sua transação, porque caso ocorra alguma exceção de negócio que de fato viole o acordo da transação, automaticamente, a transação será abortada.

Bom, é isso… Chega de exceções por hoje!


Aug 12 2008

Transacionando EJB3 Session Beans

Um dos requisitos mais importantes em aplicações de software que lidam com modificações de dados persistentes é o controle transacional. O que significa que não podemos simplismente ignorá-lo. Por isso, resolvi escrever este post.

Antes de mais nada, que raios é uma transação?

Uma transação, em linhas gerais, do ponto de vista de negócio, denota uma troca entre duas partes. Por exemplo, numa compra online de livro, você troca uma certa quantia em dinheiro debitada de seu cartão de crédito, por um livro de sua escolha. Ao participar de uma transação de negócio como esta, você procura se certificar de que o valor debitado em seu cartão de crédito é de fato o valor da compra. Caso contrario, há a possibilidade de você estar sendo enganado sem nem mesmo tomar conhecimento – o que não é nada divertido.

Ótimo. Mas onde isso impacta nossos softwares?

Em termos de software, um bom design de seus objetos de negócio não garante que tudo estará bem ao final de um transação. Uma pena. Mas o problema não está no objeto de negócio por si só, ou mesmo no seu processo de negócio. O buraco ainda é um pouquinho mais embaixo.

Em uma aplicação de software, uma transação é bem semelhante ao conceito “toma lá, dá cá” que acabei de apresentar acima, recheado de atividades inter-relacionadas que devem ser completadas em conjunto. A este conjunto de atividades dá-se o nome de unidade de trabalho. (Martin Fowler inclusive catalogou em seu livro Patterns of Enterprise Application Architecture um design pattern que reside nesse campo de transação de negócio chamado Unit Of Work.) Assim, o objetivo final de um transação é executar uma unidade de trabalho, de ponta a ponta, resultando em uma troca 100% confiável. Afinal, ninguém quer comprar gato por lebre, não é mesmo?

Sabendo disso, como podemos garantir esta confiabilidade?

Aqui entra o tal ACID

Quatro características são fundamentais em transações para estas sejam seguras.

1- Atômica, porque uma transação deve executar completamente ou definitivamente não executar. Isso significa que cada trarefa em uma unidade de trabalho deve executar sem qualquer erro, pois se algum erro ocorrer, a transação deve ser abortada e todas alterações revertidas para o estado anterior ao início da transação.

2- Consitente, já que ninguém gostaria de pagar R$ 50,00 no cartão de crédito e, por fim, ao receber a fatura de cobrança, ver que está sendo cobrado R$ 500,00 – e não R$ 50,00. Essa é uma responsabilidade que cabe ao desenvolvedor, que deve consistir cada dado antes de persisti-lo, e ainda, garantir que as tabelas (em caso de banco de dados) estejam preparadas para receber os tais dados.

3- Isolada, porque não é conveniente que os dados estão sendo manipulados por uma transação sejam ao mesmo tempo modificados por outra unidade de trabalho. Imagine que desagradável seria acontecer isso no momento de finalizar uma compra on-line, por exemplo.

4- Durável, uma vez que os dados precisam ser armazenados fisicamente em algum local enquanto a transação está acontecendo, para que não se percam caso o sistema trave.

Daí o acrônimo ACID.

Finalmente, como EJB3 nos permite controlar transações?

EJB3 dá-nos uma maneira muito simples de controlar transações em Session Beans através de uma simples anotação: @javax.ejb.TransactionAttribute.

Você pode aplicar essa anotação tanto em métodos individualmente, quanto na própria classe do bean – o que torna abrangente a todos os métodos. Ou mesmo, se for o caso, você pode aplicar na classe do bean e em seus métodos individualmente, ao mesmo tempo – neste caso, a anotação do método sobrepõem-se à da classe do bean.

@javax.ejb.TransactionAttribute recebe como atributo uma enum TransactionalAttributeType, que tem as seguintes opções:

- MANDATORY, define que o método do bean deve ser parte do escopo de transação do cliente, pois o bean pode não iniciar sua própria transação. Caso o cliente não tenha uma transação iniciada, uma falha ocorrerá e será lançada uma exceção javax.ejb.EJBTransactionRequiredException.

- REQUIRED, significando que o método do bean deve ser invocado no escopo de uma transação. Caso o cliente não houver chamado este método como parte de uma transação, uma nova transação será iniciada. Mas será encerrada ao final da executação deste método.

- REQUIRES_NEW, significa que sempre uma nova transação é iniciada, independente do cliente ter feito a invocação do método do bean em um escopo de transação ou não. O que acontece é que a transação do cliente é suspensa até que o método do bean retorne; e a nova transação, obviamente, só é válida durante a execução do método do bean.

- SUPPORTS, indica que o método do bean pode ser invocado dentro de um escopo de transação ou não. Ele pode, inclusive, interagir com outros beans que não estão inseridos em um escopo de transação.

- NOT_SUPPORTED, suspende a transação até que o método do bean termine a sua execução. Isso faz com que a transação não seja propagada a qualquer outro método de bean que este invoque.

- NEVER, define que um método do bean não pode jamais ser invocado em um escopo de transação. Caso isso aconteça, uma exceção javax.ejb.EJBException será lançada.

Conclusão

Esse modelo de controle transacional é bastante simples de se aplicar, mas ao mesmo tempo poderoso. Como ele fica muito fácil definir as unidades de trabalho – ou, escopos de transação – e garantir as características ACID em sua aplicação de software.

Mas se você não usa EJB3, tudo bem também, não há problema, fico te devendo um post sobre controle transacional com Spring Framework. =)

Até a próxima!