Colocando o papo em dia com C#

Lá pelo idos de 2000, quando tive meu primeiro contato com C#, eu era um jovem “programador Java”, com alguns poucos anos de experiência em Delphi, sonhando com um futuro onde Java seria ubíquo e nada mais seria importante. Mas C#, em especial, capturou minha atenção por estar sendo desenvolvida por Anders Hejlsberg, o mesmo cara que já havia criado o Turbo Pascal e o Delphi, que eu era muito fã ⎼ e ainda tenho certa nostalgia, vez ou outra, confesso.

Na época, achei que não passava de um Microsoft copycat do Java e segui a vida.

O reencontro

Fast forward uns 9 anos e lá estava eu, na Locaweb, assumindo a gestão de um time que era quase 100% baseado em C#, depois de quase uma década sem dar qualquer 5 minutos de atenção à linguagem ou à plataforma .NET em si.

A primeira surpresa que tive foi que .NET ainda não tinha ferramentas robustas de build, gerenciamento de artefatos, dependências, etc e tal, como tínhamos Maven (a.k.a. The Official Internet Download Tool), Ant, Ivy, Hudson (a.k.a. Jenkins) e Artifactory na comunidade Java. Isso, obviamente, dava espaço para que diversos problemas emergissem do desenvolvimento de aplicações distribuídas, como era o nosso caso. Problemas estes, que a comunidade Java já tinha passado há anos e coçado a sua própria coceira.

Uma das minhas primeiras iniciativas no time, então, foi dar apoio aos ótimos engenheiros de software que tínhamos, para que eles resolvessem esse problema. (Alguns de nós éramos programadores Java experientes, by the way.) Além de liberar certo tempo nas sprints, para que pudessem se dedicar a isso, também contribui com algum código. E foi dessa iniciativa que nasceu o projeto IronHammer ⎼ curiosamente, escrito em Ruby, não C#. Assunto para um outro momento.

Fonte: https://github.com/leandrosilva/Iron-Hammer

A segunda surpresa que tive foi o quanto C# tinha evoluído e se tornado, na minha opinião, um Java melhor (a linguagem em si, não a plataforma), o que contrastava com a questão do ferramental precário, que me chamou atenção inicialmente. O próprio Visual Studio era super precário; sem ReSharper, ele não era muito mais do que um Notepad glorificado.

Propriedades auto-implementadas, inferência de tipos (var), expressões lambda, LINQ (que é uma das coisas mais fantásticas do .NET), extension methods, tipos anônimos, etc, eram features que C# já possuía há pelo menos 2 anos quando tomei conhecimento. E passados alguns poucos meses, quando eu mal tinha emergido da minha imersão em C# 3, veio o C# 4, trazendo dynamic e parâmetros opcionais, entre outras coisas.

C# 4.0 Breaking News! ⎼ Tech talk que fiz com um colega à época

Como eu queria ter tido essas features nos meus últimos anos de Java, quando eu estava completamente vidrado por linguagens dinâmicas e programação funcional (Ruby e Erlang).

É bom que se reconheça uma coisa: se a linguagem Java foi a grande influência da criação e dos primeiros anos de vida do C#, muito de sua evolução posterior se deu por influência de F#, que apesar de mais jovem, tem em seu DNA a família ML de linguagens, descendendo diretamente de OCaml. Uma bagagem e tanto.

Ela segue em frente

Fast forward mais uma década e aqui estou eu, construindo a Pricefy há 5 anos, mantendo uma base de código majoritariamente C#, e mais uma vez impressionado com o quanto a linguagem evoluiu desde a última vez que parei para refletir a respeito. Do async/await do C# 5 aos avanços de pattern matching e top-level statements do C# 9, o que fica muito claro para mim é que a evolução da linguagem continua a todo vapor, mantendo-a jovial e moderna. Até quando? Não sei. É verdade que tem gente que já está reclamando que a linguagem está ficando complicada demais e inchada. Aliás, essa não é uma reclamação nova, sejamos francos; já tem certo tempo que essa questão tem sido levantada. Há até quem diga que está se transformando em C++.

Mas a verdade é que não temos que usar todas as features e capacidades de uma linguagem, sejam elas novas ou de berço, simplesmente porque elas estão lá, disponíveis. Há tempo e utilidade para cada funcionalidade ⎼ “à moda antiga” não é automaticamente errado.

No caso específico de C#, que estamos discutindo, a esmagadora maioria das novidades que chegaram ao longo dos anos foram para simplificar o código que se escreve e não para deixá-lo mais complicado. O mesmo vale para C++, diga-se de passagem.

Como as coisas foram ficando mais simples ao longo dos anos.

Complicado é tentar guardar na cabeça cada uma das zilhares de funcionalidades da linguagem ou as trocentas maneiras de se fazer a mesma coisa.

Honestamente? Eu não sei todas as features da linguagem. Não sei. Eu provavelmente nem devo saber, de bate pronto, assim, pá pum, todas as maneiras de se declarar e atribuir uma variável qualquer para salvar a minha própria vida. Isso porque, é quase impossível bem difícil você “modernizar” sua base de código de negócio pari passu com a linguagem de programação (ou mesmo framework) que você usa. Primeiro, porque com o passar dos anos a base de código vai ficando cada vez maior e essa “modernização” nem sempre vai trazer um retorno substancial para o problema de negócio, que o código se propõe a resolver, em função do esforço necessário para a tal “modernização”. Segundo que, vintage é cool. Não, é brincadeira. (Mas é verdade.)

O meu ponto é: manter uniformidade na base de código é mais importante e traz mais valor real ao negócio do que manter a base de código atualizada com as últimas novidades da linguagem. Um código uniforme, que qualquer dev do time navega bem e se sente em casa para implementar novas funcionalidades, modificar funcionalidades existentes e corrigir bugs, na minha cartilha, é mil vezes mais importante. Ou bem próximo disso.

Prefira a homogeneização

O que eu advogo é ir incorporando à base de código novidades da linguagem, especialmente, syntactic sugars, de maneira homogênea, para evitar criar uma colcha de retalhos. Sabe aquela base de código, que se você abrir três arquivos diferentes, parece que foram três pessoas de três lugares remotos do planeta que escreveram? Pois é. Em um arquivo, todas as variáveis são declaradas com anotação de tipo (a.k.a. tipo à esquerda); no outro, todas as variáveis são declaradas com inferência de tipo (var); e no último, há uma mistura dos dois estilos de sintaxe. E você, precisando editar o arquivo fica: e agora, que estilo devo seguir?

Eu sou do tipo que, em geral, segue o padrão do arquivo e mantém o estilo dele, seja este qual for. Exceto quando o dito arquivo não tem padrão nenhum ou não segue as convenções gerais do projeto; aí eu mudo, para enquadrá-lo no estilo majoritário do projeto.

Uma boa abordagem para solucionar este exemplo é ir homogeneizando em favor de var tanto quanto possível. Abriu um arquivo para editar, viu um trecho de código que está fora do “padrão”, refatora, padroniza. Um padrão ruim ainda é melhor do que a ausência de um padrão, porque se no futuro o time decidir mudar o padrão, vai ser muito mais fácil do que se não houvesse padrão algum.

Que tal um outro exemplo? Você precisa implementar uma nova funcionalidade na aplicação e, no processo, se depara com um método que possui uma cadeia de ifs relativamente complexa e tudo mais. Aí, analisando, você vê que o problema não é tão complexo quanto a implementação da solução. Você vê que a implementação poderia ser drasticamente simplificada usando pattern matching, que você andou estudando, mas em nenhum outro lugar da aplicação há uso de pattern matching ainda, muito embora o runtime da aplicação dê suporte a isso. O que você faz? Refatora. Porque vai haver ganho para o negócio. Quero dizer, a próxima pessoa que precisar dar manutenção neste código vai fazer isso de maneira mais confiante e rápida.

Fonte: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns

Um último exemplo que gostaria de dar aqui é o caso de interpolação de strings, que é super mundano. C# faz interpolação de strings de maneira elegante desde a versão 6, se não estou enganado, mas ainda é comum ver código por aí que não se beneficia disso, o que é uma pena. Porque além do código ficar mais limpo, quando você faz interpolação de strings, o compilador, gratuitamente, gera para você o código equivalente usando string.Format, que no final das contas, vai usar um StringBuilder. De graça e elegantemente. Quem não quer isso?

As minhas favoritas

Se você acompanhou meu rant até aqui, provavelmente, já teve uma ideia das minhas features favoritas do C# “moderno”, se é que posso chamar assim. Além delas, vou citar mais algumas.

Inferência de tipos

De longe, eu acho que o que eu mais gosto da linguagem C# é a inferência de tipos que ela proporciona o tempo todo (uma salva de palmas para a dupla “linguagem & compilador”), tanto na declaração de variáveis (var / dynamic), quanto na invocação de métodos que possuem type parameters (a.k.a. generics).

Out variables

Eu odiava quando tinha que declarar uma variável antes de usá-la em um método que tem parâmetros out.

Declaração prévia.

Mas aí, finalmente, no C# 7, alguém teve a brilhante ideia de resolver isso, permitindo que se declarasse variáveis out direto da invocação de métodos com parâmetros out.

Declaração concomitante ao uso.

Named Tuples, Discarts e Deconstruction

Tuplas estão entre minhas construções favoritas em qualquer linguagem de programação. Meu primeiro contato com elas foi em Scala, depois em Erlang. Para expressar retorno de métodos e aplicar pattern matching, por exemplo, tuplas são extremamente poderosas. Junte a isso a capacidade de fazer desconstrução de objetos e descartar valores que não importam no momento e você tem uma oportunidade fantástica de escrever código limpo e expressivo. C# 7 nos deu isso.

Fonte: https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#tuples-and-discards

O snippet que apresentei na seção anterior, falando de pattern matching, é outro exemplo prático e elegante do uso de tuplas.

Throw Expressions

Essa feature, também do C# 7, tornou bastante prático, quando é preciso checar se um determinado identificador contém um valor e, se sim, atribuí-lo a um outro identificador; ou senão, lançar uma exceção.

Fonte: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/throw#the-throw-expression

Anteriormente, esse código obrigaria um tipo de if-else.

Null-coalescing Assignment

C# 6 havia introduzido Null-conditional Operator (.? e []?) na linguagem com bastante sucesso, eliminando uma série de expressões check-before-access. C# 8, por sua vez, trouxe um novo syntactic sugar para ir um pouco além (????=), super útil para fazer check-then-assign. Trocando em miúdos e exemplificando com o código abaixo, o que isso significa é que, usando o operador ??=, o valor da direita só será atribuído à variável da esquerda, se esta variável for nula.

That sugar, baby!

Record Types

Para fechar, uma feature mais recente, do C# 9, que eu sinceramente ainda não apliquei em códigos de produção. Diferente das structs, que são value types que todos nós conhecemos de longa data, os records são, na verdade, syntactic sugar para classes que implementam uma série de funcionalidades que favorecem o emprego de “imutabilidade” de dados, como é o caso do operador with, que faz cópia com modificação (a.k.a. Nondestructive Mutation). É isso. Embora, eles também possam ser mutáveis, seu objetivo principal é ser immutable & data-centric types.

Declaração em uma linha, via positional syntax, checagem de igualdade de valor e nondestructive mutation, usando with.
Records são imutáveis por padrão, quando declarados via positional syntax.

De início, quando comecei a ouvir falar dessa feature, achei que fosse só hype e pensei: já temos struct e class, para que mais um tipo? Mas quando fui me informar melhor, estudar a proposta, enxerguei benefícios práticos.

Essa é, portanto, uma feature que vejo como forte candidata a ser introduzida em bases de código mais datadas. E isso, porque ela diminui a quantidade de código que você tem que escrever para ter certas garantias e comportamentos por vezes desejados (imutabilidade, igualdade de valor, etc). C# 9 faz isso de graça, sem que você tenha que escrever um tanto de código boilerplate, que teria que dar manutenção depois.

Conclusão

C# evoluiu demais nos últimos anos. Especialmente, na última década.

A linguagem tem ficado mais complexa? Tem. Mas não é o tipo de complexidade que você e eu somos obrigados a lidar no nosso dia a dia. É complexidade que os desenvolvedores da linguagem precisam lidar. Você e eu podemos, ou não, usar as novas features que ela oferece; que na maioria dos casos, insisto, só facilitam o nosso trabalho.

Você não precisa guardar toda a sintaxe da linguagem na sua cabeça. Há coisas melhores para ocupar a nossa memória. Apenas lembre-se de sempre checar se não há uma maneira melhor ou mais simples de fazer a tarefa da vez.

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. 🙂

Biblioteca Digital da UNICAMP pra quem quiser ver

Não sei há quanto tempo a Biblioteca Digital da UNICAMP está disponível na web pra quem quiser ver, mas só hoje descobri. (Alguém sabe me dizer o quanto estou atrasado em minha descoberta?)

Há muito material interessante, em diversas áreas de conhecimento. No tópico de teses, por exemplo, há mais de 25 mil trabalhos, de 20 institutos diferentes. De artes a química, passando por engenharia de alimentos e computação, você encontra de tudo.

Espero que seja útil pra você também. 🙂

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.

Como está seu marketing pessoal?

[Novo endereço: leandrosilva.com.br.]

Bruno Pereira escreveu dois posts (esse e esse) muito relevantes sobre marketing pessoal, em especial, para desenvolvedores de software, que eu não poderia deixar de citar e sugerir aqui em meu blog.

Ler esses posts me fez lembrar do keynote de Chad Fowler na Rails Summit sobre tornar-se uma pessoa brilhante. Facinante!

É isso ai, fica aqui a dica…

Seja brilhante!!!

Saia da zona de conforto!

[Novo endereço: leandrosilva.com.br.]

Carlos Brando, figura tarimbada do mundo Ruby on Rails, postou hoje um texto de conteúdo fantástico: Por que o salário dele é maior do que o meu?

Um trecho que achei d+ foi este aqui:

“E quando você descobrir que pessoas mais novas ou que pareçam menos experientes que você tem um salário maior que o seu, nem pense em ficar chateado ou resmungar, você recebe tanto quanto ele, a diferença é que boa parte do seu dinheiro é gasto no seu comodismo.”

Leitura mais que obrigatória!

Executar JRuby a partir do Java

[Novo endereço: leandrosilva.com.br.]

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
&#91;/sourcecode&#93;

<em>Matematica.java</em>


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…

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!

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!