Tabus e baixarias

Nas últimas semanas tenho trabalhado em um projeto de integração entre um sistema em C e um sistema em Java: o sistema original, legado, é completamente em C, e a equipe na qual eu trabalho está reescrevendo algumas partes em Java, e eu tenho que fazer o sistema antigo conversar com o novo. O sistema em C utiliza um sistema de comunicação interna que consiste basicamente em serializar bytes e mandar via UDP. Isso torna o processo bem rápido e eficiente, mas torna a comunicação entre plataformas bem complicada, por causa de problemas como alinhamento de bytes, padding, little endian, big endian, ponto flutuante, e por aí vai.

Por isso tive que rever vários conceitos, além de relembrar várias coisas em C. Não trabalhava com C desde 2002, quando participei do projeto AURORA, organizado pelo CenPRA. O objetivo do Aurora era de construir um dirigível capaz de voar sozinho, usando dados de sensores, GPS e de câmeras de vídeo. Minha missão dentro do projeto foi de desenvolver a infraestrutura para captação de imagens através de barramento IEEE 1394 (firewire), processamento e extração de certos parâmetros em tempo real e envio de alguns frames para uma estação de controle em terra. Foi C na veia, e ainda tive que estudar IEEE1394, drivers para linux, formatos de vídeo, processamento de imagens, envio de pacotes via UDP, e muitos outros conceitos básicos. Me diverti muito nesta época.

Voltando ao projeto atual, uma das tarefas necessárias era de receber um fluxo de bytes e extrair informações seguindo um descritor da mensagem. Como meus campos de informação dentro do fluxo de bytes tinham posições e tamanhos pouco convencionais (12 bits, 3 bits começando no bit 4 do byte 2, e por aí vai), não consegui encontrar nenhuma lib que resolvesse meu problema de forma adequada (em particular, nenhuma lib de extração de dados trabalha corretamente com campos de bits). Por isso resolvi extrair os bits na mão, escrevendo algumas operações binárias: o resultado final ficou bastante bom e conciso. Mas várias pessoas me perguntaram porque eu fiz a loucura de implementar isso na mão. Segundo eles, deveria ter pesquisado mais, porque provavelmente algum engenheiro da Sun havia feito algo melhor e mais robusto.

Concordo que seja possível que eu não tenha procurado o suficiente. Mas escrever algumas poucas linhas de operações binárias não me parece ser tarefa do outro mundo. Talvez eu seja arrogante o suficiente para me achar melhor do que os engenheiros da Sun (ou pelo menos tão competente quanto). Mas talvez também os programadores estejam criando tabus em relação a alguns tópicos de computação, chamados por muitos de baixarias.

A evolução na computação se faz por meio de criação de camadas de abstração, que nos permitem trabalhar sem nos preocuparmos com certos problemas. Hoje em dia, quem programa em Java, Python, Ruby e outras linguagens similares não se preocupa mais em gerenciar memória, em perder ponteiros. ORMs permitem que a base de dados seja vista como um conjunto de objetos. RMI e RPC enviam objetos e estruturas de dados de uma máquina para a outra. E acreditem: eu adoro estas abstrações! Adoro trabalhar com dicionários, listas e tuplas em Python, adoro não ter que gerenciar ponteiros em Java. Mas duas ressalvas precisam ser feitas.

A primeira já foi feita pelo Joel, no artigo The Law of Leaky Abstractions: estas abstrações são ótimas, mas não são perfeitas e muitas vezes falham. E quando falham, é preciso conhecer os fundamentos daquilo que está por baixo. Quando sistemas de ORM retornam resultados esdrúxulos, é preciso entender que o sistema subjacente não trabalha com objetos, mas com tabelas. Quando struts não da conta de tratar uma requisição, é preciso entender que no fundo no fundo, um form é uma string de requisição HTTP. Pode parecer bobo, mas é incrível perceber a quantidade de programadores que simplesmente não entendem o que se passa por debaixo do pano. Tudo é mágico.

A segunda ressalva é o ponto que eu quero ressaltar neste texto: estas camadas de abstração criam tabus. Hoje as pessoas tem receio de escovar bits, ou processar um UDP na mão. Muito em breve, SQL será algo de outro mundo, uma vez que Hibernate, RoR e Django permitem que objetos sejam criados de forma direta. HTTP e CGI já são conceitos do passado: hoje temos forms, event handlers e componentes. Para tudo, se busca um framework, uma lib. Para tudo, se acha que o que é feito pelos engenheiros da Sun é melhor. Eu tive esta nítida impressão conversando com um colega de trabalho que já tem muiiiiitos anos de estrada. Ele tem uma boa experiência em programação de microcontroladores, e ele é da época em que C era novidade no mercado. E segundo ele, mexer com bits era algo trivial e corriqueiro naquela época.

Que fique bem claro: eu adoro não ter que mexer nestas coisas. Mas sei que problemas deste tipo podem aparecer na nossa frente de vez em quando, e quando aparecerem nós temos que poder resolve-los. E para isso, não podemos ter medo de passar por cima das abstrações. Por isso, eu defendo que todo programador deveria mexer em algum projetinho em C de tempos em tempos (um ou dois meses a cada 5 anos por exemplo). Isso já seria suficiente para lembrar que o mundo computacional não é tão alto nível quando gostaríamos. E o prazer de voltar para nossas caixas pretas é incomensurável.

Bricando com bits: Ponto flutuante

Nos últimos tempos tenho trabalhado na integração de dois sistemas, um em C (legado) e um e Java. A integração consiste em em ler mensagens enviadas pelo programa em C através de um socket e gerar objetos e ações correspondentes do lado Java. Nada de muito excepcional, é um cenário bastante comum. O divertido da coisa é que eu estou tendo que descer bem baixo na manipulação dos bits, e estou tendo que relembrar vários conceitos que aprendemos algum dia na faculdade e que consideramos como sendo inúteis pro resto da nossa vida, já que boa parte é resolvida por alguma API.

Ontem acabei de me deparando com um probleminha interessante, e que me fez relembrar o conceito de representação de números em ponto flutuante. Vou tentar explicar aqui a parte teórica da coisa, e no final apresento o problema e a solução. Se você não gosta de teoria, pode pular pro último parágrafo.

Muito bem. Suponha que você tenha o seguinte código em Java:

int i = 15;
float f = (float)i;
System.out.println(i+”, “+f);

O resultado da impressão será 15, 15.0.

Até aí nenhum novidade. O casting faz com que o número passe a ser tratado como ponto flutuante, mas mantém o valor inteiro. OK. Alguém aí já parou pra olhar como estes números são representados binariamente, por baixo do pano?


15 inteiro = 00000000 00000000 00000000 00001111
15 float = 01000001 01110000 00000000 00000000

Para alguns talvez isto não seja novidade, e para outros uma descoberta. E para alguns como eu era algo que eu já imaginava, mas nunca olhei a fundo.

No caso do 15 inteiro, nenhuma novidade: 15 = 2^3 + 2^2 + 2^1 + 2^0 = 8 + 4 + 2 + 1 = 15.

No caso do 15 float, é necessário entendermos o padrão que rege a representação dos números de ponto flutuante: IEEE 754. Resumindo: segundo o padrão, um número de ponto flutuante pode ser representado com precisão simples (32 bits, float em Java) ou precisão dupla (64 bits, double em Java). A IEEE define apenas a precisão de 32 bits como obrigatória, as outras sendo opcionais.

No caso da representação simples, o layout dos bits é o seguinte: o primeiro bit representa o sinal (0 positivo, 1 negativo), os 8 bits subsequentes representam o expoente e o restante (23 bits), a parte fracionária (chamada de mantissa). O desenho abaixo, obtido da wikipedia, ilustra este layout:

O expoente pode mapear valores entre -127 a 127 (no caso de um expoente de 8 bits). Para evitar o uso de bit de sinal, que pode dificultar certas operações, foi definido que o valor armazenado seria e + 127, onde e é o real valor a ser considerado.

A mantissa armazena um valor fracionario, e pode ser traduzido como 1.<padão de bits da mantissa>. Se a nossa mantissa for 1000000 00000000 00000000, é equivalente a 1.1000000 00000000 00000000. Aqui vale ressaltar mais um ponto: estamos considerando números binários, portanto os valores após às virgulas podem ser considerados como potências negativas de 2 (assim como números fracionários na base 10). No caso do exemplo, 1.1 é equivalente à 1 + 2^(-1) = 1 + 0.5 = 1.5.

Tendo estes elementos em mão é fácil calcular o resultado final: f = (sinal) * 2^(e -127) * 1.<mantissa>.

No caso do número 15, vamos ver como fica a coisa:

  • sinal = 0 = +1
  • expoente = 10000010 = 130. Portanto e = 130 - 127 = 3.
  • mantissa = 1110000 00000000 00000000, portanto 1.111 = 1 + 2^(-1) + 2^(-2) + 2^(-3)  = 15/8
  • Resultado = +1 * 2^3 * 15/8 = 8 * 15/8 = 15.

Muito bem. Mas a pergunta: porquê eu estou me preocupando com isso???

Simples: eu tenho que ler um fluxo de bytes, seguindo um descritor de mensagem, e reconstituir certos tipos de dados (ints, booleanos, campos de bits, etc…). Como eu tenho que ler e extrair alguns campos de bits perdidos no meio de bytes, resolvi implementar uma estratégia de baixo nível, lendo byte a byte e concatenando quando necessário em tipos de dados maiores. Além disso, estou usando como repositório temporário destes bits uma variável do tipo long, que possui 64bits, o que é mais do que necessário para armazenar qualquer dado que eu receba. A operação é simples: recebo um byte, e “colo” ele no long usando operações SHIFT e OR binários.

Agora suponha que o programa em C me mande o número 15 em ponto flutuante de 32 bits, como definido acima, e que eu armazene os bits recebidos em um long. Se ao final da leitura, eu pegar a variável temporária  e fizer um casting para float direto, a JVM vai pegar o valor do long (no caso 1097859072) e simplesmente converter para o padrão IEEE 754 correspondente, o que me resultaria num float com valor 1097859072.0, o que definitivamente não me interessa. O que eu preciso é de uma função que receba um long ou um int, seja capaz de olhar para o dado apenas como um repositório de bits e gere o float o double correspondente. No caso, os métodos Float.intBitsToFloat(int) e Double.longBitsToDouble(lont).

PS: Quem estiver interessado em ter uma leitura mais teórica e aprofundada sobre o tema pode ler o artigo What Every Computer Scientist Should Know About Floating-Point Arithmetic.

MyFaces e Spring Web Flow

Mais uma da série “MyFaces e …”. Há algum tempo atrás, escrevi sobre a integração entre MyFaces e Tiles, para resolver o problema de gerenciamento de layouts. Agora, irei falar sobre como resolver a limitação do JSF no que diz respeito ao controle de escopo de managed beans.

Aqueles que desenvolvem aplicações web com JSF sabem que este framework possui três níveis de escopo: request, session e application. Para os desavisados isto parece bom, mas não irá demorar muito para você perceber que existe uma lacuna grande aqui - você com certeza irá precisar de um escopo que seja maior que request, e menor que session.

Um caso bem simples é o de utilizar commandLinks ou commandButtons dentro de um dataTable utilizando escopo request - ao clicar no link ou botão, simplesmente nada acontece, porque isto inicia uma nova requisição, mas os dados da dataTable que está sendo referenciada pelo link ou botão acionado não existe mais, pois estava disponível somente na requisição anterior. Solução número 1: colocar o dataTable no escopo session. A partir daí, tudo funciona … mas os dados ficam inutilmente ocupando espaço de memória até que a sessão se encerre. Para este problema específico, o MyFaces extendeu esta componente, criando o atributo preserveDataModel, que indica se o estado dos dados devem ser descartados antes dos dados serem novamente exibidos. No entanto, há outros casos que seria útil ter um escopo intermediário: casos de uso onde os dados são preenchidos em mais de uma requisição (agendamento de hotel, por exemplo); executar o mesmo caso de uso em duas janelas (tabs) na mesma sessão de usuário …

Alguns frameworks oferecem soluções para este problema: JBoss Seam, Shale Dialog Manager, Spring Web Flow. O primeiro parece muito bom, e aparentemente resolve bem este, e outros problemas. Os outros dois atacam especificamente o problema do controle de escopo, através do controle dos fluxos de requisições dos casos de uso. Por eu estar preocupado somente com o problema de escopo, descartei o JBoss Seam - seria um tiro de canhão pra matar uma mosca - e foquei nos outros dois.

Comecei com o Shale Dialog Manager, que conforme consta, foi fortemente inspirado no Spring Web Flow (SWF). Infelizmente, ele apresentou algumas limitações que me fizeram desistir dele:

  1. Você não pode iniciar um outro fluxo estando no meio de outro (exceto que você o defina como parte do fluxo) - parte dos casos de uso de minhas aplicações são acessíveis por meio de menus - se para cada fuxo de operação (caso de uso) eu tivesse que inserir os fluxos acessíveis pelo menu, eu só faria isso na vida
  2. Não é possível executar o mesmo fluxo em duas janelas (tabs) diferentes na mesma sessão

Dado isto, passei a testar o SWF, e a vida se tornou maravilhosa. Em primeiro lugar: a documentação é excelente - poucas vezes na vida encontrei um software open source tão bem documentado (recomendo a leitura do artigo do Leonardo que fala a respeito deste assunto); a integração com JSF é muito simples (aliás, o SWF é independente de tecnologia de apresentação - funciona com JSF, Struts, Tapestry, etc); não possui as limitações do Shale Dialog Manager; possui suporte a testes de unidade e testes de sistema dos fluxos de execução.

A integração do SWF com o MyFaces é bastante simples, e bastante bem documentada. Para definir cada fluxo, você deve criar um arquivo xml para cada um deles (este é o único ponto que não me deixou muito satisfeito, mas …). Os fluxos podem ser definidos por:

  • ViewState: utilizado para definir uma interface a ser exibida, através da qual o usuário fornecerá dados ao sistema
  • ActionState: utilizado para executar ações de negócio, e definir o próximo estado do fluxo baseado no resultado da execução
  • DecisionState: realiza uma decisão sobre próximo estado do fluxo, através do teste de expressões booleanas
  • SubflowState: inicia um novo fluxo como subfluxo do fluxo atual, permitindo a passagem de parâmetros
  • EndState: indica o estado final de um fluxo, determinando o fim de sua execução

Com apenas estes estados, é possível definir os mais complexos fluxos de operação de uma aplicação web. No meu modo de ver, este framework é excepcional. Recomendo fortemente. E se você ainda não conhecia nada com relação ao Spring Framework, sugiro que você dê uma olhadinha em tudo o que ele pode fazer, e seus subprojetos. Para o desenvolvedor Java, pode ser extremamente útil, podendo complementar, ou mesmo substituir diversas tecnologias J2EE.

Powered by ScribeFire.

O legal de um blog como o log4dev…

É que é um lugar com gente inteligente, bem educada. Daqueles onde só pode falar quem sabe sentar-se à mesa e usar os talheres. Não tem muito lugar para criança.

Então, lendo o último post do Miguel, eu fiquei com vontade de falar “Java is TeH SuXXXXXoR”. Mas como precisamos fazer as coisas aqui com estilo, acabei escrevendo isso aqui, ó.

MyFaces e Tiles

Estou iniciando o desenvolvimento de uma aplicação utilizando MyFaces. Esta escolha se deu porque gosto do esquema do Java Server Faces (JSF), e após ter trabalhado com a implementação JSF da Sun, resolvi dar uma chance ao MyFaces, pois parece ter algumas soluções interessantes para alguns problemas que enfrentei com o anterior. Em conjunto, estou utilizando diversas componentes providas pelo Tomahawk, que extendem as funcionalidades das componentes básicas do JSF, além de oferecer diversas outras componentes.

JSF é muito bom, mas não resolve todos os problemas. Um destes problemas é a questão de gerenciamento de layouts - em JSF, se você quiser que todas as páginas de sua aplicação obedeçam a um mesmo layout, você tem que replicá-lo em todas as suas páginas; imagine se depois você quiser mudar esse layout, o problema que não vai ser alterar todas as páginas. Para resolver este problema, integramos ao JSF o Tiles. Este é um framework inicialmente desenvolvido dentro do Struts, mas que agora foi separado deste, que simplifica o desenvolvimento de interfaces de aplicações web. Ele se baseia no padrão Composite View, permitindo que se definam fragmentos de página (menu, header, footer, etc …), que podem ser agrupados em um layout, que pode ser aplicado às suas páginas. O conceito é bastante interessante, e para quem quiser mais detalhes, sugiro que leia o tutorial do Tiles.

Vou aqui explicar como estou realizando esta integração. A primeira coisa que fiz foi definir a estrutura do meu layout, que é composto por:

  • Header
  • Menu
  • Conteúdo

Basicamente, o header e o menu são sempre o mesmo. A única coisa que varia é o conteúdo a ser exibido. Com isto, defini uma página ( layout.jsp ) onde defini o posicionamento destas componentes de layout, e a utilizei em uma definition no arquivo de configuração do Tiles:

      <definition name="layout" template="/layout.jsp" >
             <put-attribute name="header" value="/header.jsp" />
             <put-attribute name="menu" value="/menu.jsp" />
             <put-attribute name="content" value="/blank.jsp" />
      </definition>

Note que para cada componente do layout há uma página associada. Estes valores podem ser alterados em tempo de execução, e irei utilizar esta funcionalidade para exibir diferentes conteúdos da minha aplicação (alterando a página a ser exibida pela componente content. Abaixo, mostro o código de layout.jsp, onde posiciono um elemento embaixo do outro:

      <tiles:importAttribute scope="request" />
      <f:subview id="header">
           <tiles:insertAttribute name="header" flush="false"/>
      </f:subview>
      <f:subview id="menu">
           <tiles:insertAttribute name="menu" flush="false"/>
      </f:subview>
      <f:subview id="content">
           <tiles:insertAttribute name="content" flush="false"/>
      </f:subview>

Um problema desta forma de integração que adotei é que, para cada página (conteúdo) que desejo exibir, preciso criar dois arquivos jsp: um para definir o conteúdo, e outro para inserir este conteúdo dentro do layout. Abaixo, um exemplo da página que insere o conteúdo dentro do layout. Note-se que isto é feito utilizando a definition que criamos anteriomente, e sobrescrevendo-se a propriedade content:

      <f:view>
          <tiles:insertDefinition name="layout" flush="false" >
             <tiles:putAttribute name="content" value="/pageContent.jsp" />
          </tiles:insertDefinition>
      </f:view>

Certamente esta não é a melhor solução, mas foi a primeira que veio à minha cabeça. No futuro, pretendo gastar um pouco de neurônios pensando em uma forma de gerar apenas um arquivo jsp por página a ser exibida.

Para configurar o Tiles para ser utilizado pelo JSF, basta você acrescentar o seguinte código em seu arquivo web.xml (além dos jar’s do Tiles):

    <servlet>
       <servlet-name>tiles</servlet-name>
       <servlet-class>org.apache.tiles.servlet.TilesServlet</servlet-class>
       <init-param>
          <param-name>org.apache.tiles.DEFINITIONS_CONFIG</param-name>
          <param-value>/WEB-INF/tiles-defs.xml</param-value>
       </init-param>
       <load-on-startup>2</load-on-startup>
    </servlet>

Nesta solução, o que eu gostaria de alcançar era:

  • facilidade de gerenciamento de layout
  • manter o controle de navegação do JSF
  • facilidade de integração

Tirando o overhead de gerar um arquivo a mais por página gerada, acho que a solução é boa. E tem funcionado bem para os meus propósitos. Se você tiver alguma sugestão, dica, crítica, não esqueça de deixar seu comentário. Se preferir, podes entrar em contato pelo alexandre(at)log4dev.com.

Powered by ScribeFire.

Lendas urbanas: custo de alocação em Java

Segundo este artigo da IBM, estudos mostram que o tempo de alocação de um objeto em Java, utilizando a palavra chave new, é menor que o custo de um malloc em C. Os dados são interessantes: um new requer 10 instruções de máquina, enquanto que um malloc requer entre entre 60 e 100 instruções. Outro dado interessante do artigo é que o uso do Garbage Collector tornaria o processo mais eficiente do que o gerenciamento manual de memória, pelo fato do primeiro tratar de blocos maiores.

Meus comentários a respeito:

  1. A performance de Java melhorou muito ao longo do tempo. Mas o footprint da JVM, para qualquer programinha, ainda é muito grande. Alocação de objetos em Java pode ser mais rápido do que em C/C++, mas com certeza programas em C/C++ fazem um uso mais racional de memória.
  2. Esta área de otimização de memória, footprint, execução é a área de aprimoramento que a Sun deveria se concentrar exclusivamente, em vez de ficar inventando moda com closures, Java FX e outras coisas script-like. Java não vai conseguir concorrer com linguagens de script. Java tem que concorrer com C++.
  3. A minha experiência com alocação de grandes quantidades de dados em Java, aplicada ao processamento de sequências genéticas, não foi das melhores.

Thread dumps e stack traces

Ian Daniel, engenheiro de suporte da Atlassian recomendou através do blog da empresa dirigido a desenvolvedores o seguinte artigo - 0xCAFEFEED: Of Thread dumps and stack traces … .

O artigo é bastante interessante, e começa com uma introdução básica sobre exceções em Java que todo mundo que começa a programar nessa linguagem deveria ler - isso evitaria cerca de 50% dos posts em fóruns de usuários de diversas aplicações. Depois da introdução, ele começa a discutir sobre formas inovadoras de uso de stack traces que as exceções provêem: por exemplo, o uso do método printStackTrace() da classe Exception para descobrir como se chegou à execução de um determinado trecho de código. Por fim, o autor do artigo fala sobre thread dumps - como obtê-lo, explica as informações que são visualizadas, e como utilizá-las para debugar uma aplicação.

Um artigo bastante simples, mas muito informativo, e com conhecimentos que são úteis para a maioria dos programadores Java. Recomendo.

Powered by ScribeFire.

Envio de arquivos com nomes corretos

Quando clicamos em um link direto para um arquivo em uma página web, o servidor se encarrega de gerenciar a transmissão dos dados e de gerar o cabeçalho correto para que a transferência se faça corretamente.

Mas muitas vezes, a transmissão do arquivo é executada por um serviço que o desenvolvedor está implementando. Isto acontece muito quando o documento é gerado dinâmicamente pelo servidor a cada requisição. Por exemplo, um serviço que gere um boleto para pagar uma conta. Provavelmente o boleto não está gravado no HD do servidor, mas é criado a cada requisição.

Neste caso, muitas vezes quando queremos salvar o arquivo gerado no HD local, o nome sugerido para o arquivo pelo navegador é o nome do link clicado. Por exemplo: eu clico em www.meuservidor.com/gera_boleto.do, peço para salvar o arquivo e o navegador propõe o nome gera_boleto.do. Isto pode ser indesejável caso o arquivo deva ter um nome específico.

Para resolver este problema, basta adicionar o header Content-Disposition com valor

“inline; filename=nomedomeuarquivo”

no cabeçalho da resposta antes de enviar o arquivo propriamente dito. A forma de definir o header depende de cada linguagem. Em Java, desenvolvendo com Servlets, o comando seria

response.setHeader(”Content-Disposition”, “inline; filename=teste.doc”);

Dez dicas sobre Python para programadores Java

1. Python não suporta a instrução switch. A solução mais trivial para obter o mesmo comportamento seria usar if else encadeado. Mas a solução mais pythonica (se é que o termo existe) é usar dicionários para isso, aproveitando o fato que a implementação desta estrutura de dados é extremamente eficiente e que funções são first-class objects.

Exemplo:


def funcA()
def funcB():
switch = {1: funcA, 2:funcB}
switch[valor]()

2. Em Python, tudo é objeto, desde os tipos primitivos int, boolean até o arquivo onde o código é gravado (chamado de módulo), passando por funções.

3. A cultura Java diz que o acesso a atributos de um objeto deve ser feito por meio de métodos getters e setters. Portanto, quando um programador Java escreve em Python, a tendência é criar estes métodos. A cultura Python define que o acesso aos atributos se faz através da chamada direta ao atributo. Por exemplo, se tenho o objeto meuObjeto, que possui o atributo meuAtributo, então o acesso se faz via meuObjeto.meuAtributo. Este padrão pode parecer estranho à primeira vista, pois estaria quebrando alguns paradigmas de programação orientada a objeto permitindo acesso direto à variável. E se eu quiser fazer validações antes de definir o valor do atributo? Está tudo previsto! Python por trás dos panos chama os métodos _get_<atributo> e _set_<atributo>. O comportamento padrão é simplesmente obter ou gravar o valor do atributo. Mas caso o desenvolvedor deseje determinar um comportamento específico, basta sobreescrever o método.

4. Variáveis e métodos privados em Python são definidos com dois _ antes do nome. Porém, eles não estarão completamente inacessíveis de fora. Simplesmente, o nome dele será concatenado com o nome da classe (name mangling) para dificultar o acesso. A idéia geral por trás disso é que ao definir um método ou atributo como privado, o desenvolvedor sinaliza que o uso da variável diretamente pode causar efeitos indesejáveis. Aquele que o fizer estará fazendo por sua conta e risco!

5. Métodos estáticos em Java são no fundo uma forma de contornar o fato que a linguagem exige que todos os métodos tem que estar em uma classe. É um recurso muito utilizado para se criar coleções de funções utilitárias genéricas (como a classe Collections). Em Python, o equivalente pode ser feito por meio de funções definidas no módulo, fora de um objeto.

6. Python suporta programação orientada a objetos, procedimental e funcional! Use e abuse desta liberdade.

7. Dicionários são extremamente eficientes e muito simples se operar em Python. Se estiver pensando em criar uma classe apenas para transportar dados, pense duas vezes se usar um dicionário não é a melhor solução.

8. Existem duas formas de se criar atributos de uma instância de um objeto em Python: ou dentro do método _init_, utilizando self.minhavariável, ou em qualquer ponto do código após a criação. Em Python, os atributos e variáveis são criados no momento do primeiro uso. Por exemplo:

class MinhaClass:
pass

instancia = MinhaClass()
instancia.variavel=1

O código acima cria uma classe MinhaClasse, uma instância desta classe e adiciona um atributo variável. Atributos criados dentro do código da classe e fora do método _init_ são equivalentes a variáveis estáticas em Java.

9. Variáveis e atributos tem escopo de módulo, classe, método ou função, mas não tem escopo de bloco. Isso significa que uma variável criada dentro de um if continuará viva fora dele. Cuidado!

10. A questão de uso de indentação para delimitação de blocos pode parecer estranho à primeira vista. Mas não é. Basta usar um editor decente, como PyDev para Eclipse ou python-mode para Emacs. O efeito colateral é que o código fica naturalmente organizado e fácil de ler.

Lambda Probe

Pra quem desenvolve ou monitora aplicativos rodando em Tomcat/JBoss, a ferramenta LambdaProbe pode ser de grande valia. Este aplicativo web, que roda dentro do próprio servidor de aplicações, fornece uma interface web bastante intuitiva fornecendo uma grande quantidade de informações úteis. Dentre elas, a lista de aplicativos instalados e ativos no servidor, número de sessões por aplicativos, objetos salvos na sessão, datasources, memória utilizada e acesso aos logs de servidor. Além disso, permite executar deploy, undeploy, start e stop de cada aplicativo. Pode ser instalado a partir do site www.lambdaprobe.org

Java.io.File

Outro dia estava desenvolvendo um programa em Java e precisei manipular alguns arquivos. Até ai, tudo bem. Java, para variar, possui um conjunto de classes em sua API padrão específica para tratamento de arquivos no pacote java.io.

Mais especificamente, eu estava usando a classe java.io.File, que possui uma abstração para acesso de arquivos independente do sistema operacional. Foi ai que tive um problema inesperado quando precisei de criar um link simbólico.

Para aqueles que não estão acostumados com o conceito, um link simbólico é, simplificadamente, um arquivo especial que no fundo só é um apontador para outro arquivo. Quem quiser maiores informações pode ler aqui. Este conceito de arquivo é muito usado em sistemas *nix, mas hoje ele está presente em outros sistemas também, como o Windows.

Apesar de ser um conceito muito bem difundido, para meu espanto, a classe java.io.File não consegue criar um link simbólico! O engraçado é que ela faz várias outras coisas: cria arquivos, cria diretórios, apaga arquivos, renomeia arquivos… Mas não cria links simbólicos!

Imaginei que poderia haver alguma outra forma de se criar um link simbólico em Java, mas após uma pesquisa rápida não achei nenhuma solução prática que não fosse a execução direta no sistema operacional do comando que cria links simbólicos, o que não é uma solução muito boa pois é dependente de sistema operacional e, se estamos programando em Java, uma das coisas que provavelmente estamos buscando é portabilidade (e, no meu caso, isto é verdade).

Para aumentar ainda mais minha perplexidade, achei um bug aberto no bug tracker da Sun descrevendo este problema em 1997. Só que o bug continua aberto e sendo atualizado com informações. Um dos últimos posts neste bug diz que talvez no Java 7 (que ainda está meio longe de aparecer, ainda estamos no Java 5) a interface para manipulação de arquivos em Java irá mudar (http://bugs.sun.com/bugdatabase/view_bug.do;:YfiG?bug_id=4042001 e http://bugs.sun.com/bugdatabase/view_bug.do;?bug_id=4313887).

Provavelmente esta funcionalidade foi deixada de lado na classe Java.io.File para que Java pudesse manter compatibilidade com todos os sistemas operacionais que rodam Java. No entanto, por ser uma funcionalidade muito aceita e muito útil, acho que o certo seria termos uma interface Java.io.IFile que possuisse todos os métodos e, cada sistema operacional teria sua implementação especifica desta interface. Estes detalhes, inclusive, poderiam ser totalmente abstraídos para o desenvolvedor, que poderia continuar usando a classe Java.io.File. A única coisa é que se ele tentasse criar um link simbólico em um sistema de arquivos que não suportasse isso, uma exceção seria gerada.

É pitoresco como uma linguagem muito utilizada como Java possui uma deficiência grave como esta. Alias, na minha opinião, a interface de manipulação de arquivos como um todo em Java é bem fraca. Não existe, também, um mecanismo atômico para copia de arquivos. Se alguém quiser copiar um arquivo, acho que a melhor solução será abrir um streaming para ler um arquivo, criar um novo arquivo e abrir outro streaming copiando todo o conteúdo do arqiuvo original. Algo muito mecânico se comparado com algumas outras grandes facilidade proporcionadas pela plataforma Java.

Enfim, apesar de Java ter muitas facilidades, é necessário algumas evoluções am alguns campos ainda. Apesar da minha reclamação, de certa forma isto é natural. Se todos os sistemas que existissem não precisassem de evolução a computação por si só perderia sentido.

Java X Ruby

Eu nunca programei em Ruby. Mas tenho ouvido falar muito, muito bem, desta linguagem. E sobretudo tenho ouvido falar muito bem do Ruby On Rails. Mas como não se pode fazer tudo ao mesmo tempo, acabei optando por inicialmente aprender Python. Anyway, é sempre bom estar acompanhando o que se passa no mundo das outras linguagens.

O artigo “Ruby for the Java world” é um artigo bem interessante que faz uma comparação entre Ruby e Java (e interessante para programadores java como eu) e apresenta a ferramenta JRuby que permite que uma use recursos da outra e vice-versa.

Java e Linux

Uma das coisas mais interessantes que eu já presenciei é a diferença de percepção existente entre os desenvolvedores Java em geral e as pessoas da comunidade Linux. Nos últimos dois anos eu participei de vários eventos da tecnologia Java no Brasil. Dentre eles, os mais importantes com certeza foram os dois Sun Tech Days. Para aqueles que não conhecem, o Sun Tech Days é o maior evento de Java no Brasil. Ele é organizado pela Sun Microsystems Inc. e também acontece em outros países. Estes eventos seriam os segundos mais importantes depois do Java One, maior evento de Java do mundo que acontece anualmente em San Fracisco.

Nestes eventos sobre Java, é muito interessante a forma como a grande maioria dos desenvolvedores tem uma atitude de repúdio com o Windows, a tecnologia .NET e outras coisas vindas da Microsoft. Este tipo de comportamento é de certa forma normal em vários grupos de pessoas envolvidas com tecnologia. Até aí não vejo nada demais. A Microsoft mexe com o âmago de muitas pessoas, seja lá em qual sentido isto acontece. Obviamente, este tipo de comportamento é seguido pela defesa de tecnologias livres, aí incluindo, é claro, o sistema operacional Linux. Isto é ainda mais exacerbado pelo recente movimento da própria Sun na abertura do código-fonte de vários de seus produtos, inclusive o sistema operacional Solaris e da IDE de desenvolvimento NetBeans.

Neste ponto chego ao ponto interessante que comentei no inicio deste artigo. Ao mesmo tempo em que muitas pessoas envolvidas com Java no Brasil são a favor do software livre e do Linux, boa parte das pessoas da comunidade que eu já conheci não gostam da tecnologia Java. Existem, obviamente, vários motivos para este tipo de comportamento da pessoas da comunidade. Muitos não vêem com bons olhos as iniciativas de software livre da própria Sun devido às restrições das licenças da Sun. Especificamente em relação a Java, as licenças da maquina virtual Java e do SDK não são licenças de software livres. Os softwares são distribuídos gratuitamente e sua especificação é desenvolvida através do Java Community Process (JCP), que é um processo aberto a toda a comunidade para desenvolvimento das especificações de tecnologias relativas ao Java. No entanto, a implementação da Sun da máquina virtual Java e do seu SDK não possuem o código-fonte aberto, ainda.

Digo ainda porque existe uma certa boa vontade da Sun em liberalizar um pouco algumas questões relativas à licença do Java. Acho que ainda estamos um pouco longe de termos o código-fonte do Java SE da Sun liberados para a comunidade de software livre, mas no último Java One um dos principais assuntos foi a flexibilização da licença Java para que a implementação de Java SE da Sun possa, por exemplo, ser distribuída sem grandes problemas em distribuições Linux.

Apesar desta aparente boa vontade, posso estar enganado, mas as notícias mais recentes que tenho é que as distribuições Linux que antes estavam vendo com bons olhos a iniciativa da Sun para mudança da licença do Java agora estão com um pé atrás novamente. Isto porque as mudanças feitas na licença aparentemente não foram suficientes para que o Java possa ser disponibilizado juntamente com os outros pacotes de instalação das distribuições.

Esta questão legal, que é importante por um lado, acaba afetando diretamente os usuários finais que tem várias dificuldades para instalar o ambiente Java em Linux e utilizar este ambiente, por exemplo, dentro de seu navegador para acessar a um Internet Banking ou outra aplicação baseada em Java. Este tipo de dificuldade prática causada pelos problemas de licença acaba pesando muito na visão da comunidade de software livre sobre a tecnologia Java.

Não sei até que ponto este tipo de comportamento entre as pessoas envolvidas com a tecnologia Java e com a comunidade de software livre acontece em outras partes do mundo. No entanto, é interessante por si só verificar este tipo de relação no Brasil porque o Brasil é de longe o país com a maior comunidade de usuários Java do mundo e um dos mais ativos participantes em projetos de software livre também.

Google WebToolkit

A Google é uma das poucas empresas que eu conheço que consegue superar minhas espectativas a cada novo lançamento. É incrível a capacidade que eles tem de desenvolver produtos interessantes, e sobretudo a capacidade que eles tem de oferecer ferramentas de alto nível para desenvolvedores.

O mais novo exemplo é o Google WebToolkit, que acaba de sair do forno. A idéia é fornecer uma API para facilitar a programação de aplicativos WEB AJAX em Java. Eu só li a documentação, e portanto tenho no momento uma visão superficial do sistema. Mas basicamente a idéia é que o desenvolvedor escreve uma interface gráfica em Java, como se fosse um programa desktop com interface SWING, e o Google WebToolkit compila sua página em código Javascript. Se não me engano, o funcionamento é muito parecido com o Java Server Faces da Sun.

Vantagens desse mecanismo:

  1. Torna o processo de desenvolvimento de interfaces mais rápido, sobretudo para aqueles que não são experts em Javascript (e nem querem se tornar), e para aqueles que querem manter a compatibilidade de browsers
  2. Facilita o processo de debug, uma vez que pode se utilizar JUnits e debuggers de Java para testar a interface
  3. Permite acesso a todas as funcionalidades da API Java…eles que se virem pra traduzir isso em Javascript.

Para aqueles que trabalham com Eclipse, mais um ponto interessante: o GWT já vem com um script projectCreator, que cria toda a árvore de arquivos, scripts e classpath necessários para criar um projeto dentro do IDE.

Bom, por enquanto é só pessoal. Assim que tiver mais detalhes, entro em contato.

Ambiente de desenvolvimento

Há um tempo atrás eu pensei em escrever um post descrevendo o ambiente de desenvolvimento que eu uso para escrever aplicações Web em Java. Basicamente Eclipse+Tomcat+Ant. A idéia era fazer um passo a passo, explicando os motivos das esolhas, características e vantagens do setup utilizado. Confesso que nunca escrevi por pura preguiça. Mas hoje resolvi escrever algo sobre ambientes de desenvolvimento de forma mais genérica. Dedico ao meu colega Raphael, que vive dizendo que ama emacs, mas vira e mexe me pentelha porque não consegue trabalhar direito com Eclipse e Tomcat.

Primeira má notícia: configurar ambiente de trabalho pode ser longo e chato. Sobretudo se estiver trabalhando com Linux e ferramentas livres. IDEs pagas em geral são mais bem documentadas e portanto mais fáceis, o que aliás justifica o preço pago. E mesmo assim, tem que se perder um tempinho pra deixar o pacote do jeito desejado. Segunda má notícia: ter um bom ambiente de desenvolvimento é condição sine qua non para ter uma produtividade adequada.

Não vou entrar no mérito de quais ferramentas são as melhores. Isso é muito pessoal, e vai do gosto de cada um…tentar discutir isso é entrar numa guerra de crenças quase religiosas. Até hoje não se chegou a um consendo sobre qual editor é melhor: VI ou Emacs. Eu prefiro Emacs, porque até hoje sou incapaz de abrir um arquivo e editar o seu conteúdo em VI. O fato é que existe uma grande quantidade de setups adequados para se desenvolver uma aplicação desde que bem configurados. Do meu ponto de vista, uma configuração básica deve ter um esquema de controle de versão, um bom editor de código (se possível com syntax highlight, auto-complete, sistema de refactoring, busca eficiente), e um bom sistema para compilação, testes, integração e distribuição do aplicativo. No melhor dos mundos, cada uma dessas ações deve ser executada com apenas um comando (one step build, uma das características definidas por Joel Spolsky para medir a qualidade de uma empresa de desenvolvimento). O compilador pode ser javac, gcj, jikes, gcc. O builder pode ser o ANT, Make, Shell Script. Sistema de versão pode ser CVS, subversion, ou qualquer outro que exista ou que venha a existir e que eu não conheço. Editor tem aos montes: eclipse, emacs, jEdit, VI, entre outros. Não importa muito qual o conjunto de ferramentas utilizado, desde que seja eficiente, compartilhado por toda a sua equipe de desenvolvimento, e possa ser usado a qualquer momento sem nenhuma grande complicação: ter que modificar comandos manualmente durante o processo ou tentar montar isso emergencialmente pode ser uma fonte de erros e dores de cabeça.

Portanto, conselho de amigo: antes de mais nada, defina quais ferramentas você prefere, assuma essa escolha, e passe um tempinho deixando tudo funcionando perfeitamente. Isso com certeza irá poupar muito tempo mais tarde.

Next Page »