Processo de build com o Maven

Por Lucas Cavalcanti em 07/07/08

O Maven é uma ferramenta de gerenciamento, construção e implantação de projetos muito interessante, que te ajuda no processo de gerenciamento de dependências e no de build, geração de relatórios e de documentação. Na Caelum esta é a ferramenta usada em todos os projetos internos e nas consultorias.

Muitas pessoas migram seus projetos para o Maven, mas acabam arrumando mais problemas que soluções, pois não conseguem configurá-lo corretamente, e acabam desistindo e fazendo tudo na mão, ou voltando para o Ant. Mas se você conseguir ajustar as configurações, o Maven vai te ajudar muito e vai compensar todos os (poucos) problemas que ele eventualmente causa. No início do uso do Maven, espere formar com ele uma relação de amor e ódio.

Para começar a usar o Maven, tudo o que você precisa fazer é baixá-lo e configurar umas poucas variáveis de ambiente. Depois de ter feito isso, é só digitar mvn [target] na linha de comando. Alguns sistemas operacionais já te oferecem essa instalação através do macport ou apt-get.

A unidade básica de configuração do Maven é um arquivo chamado pom.xml, que deve ficar na raiz do seu projeto. Ele é um arquivo conhecido como Project Object Model: lá você declara a estrutura, dependências e características do seu projeto. A idéia é bem parecida com o build.xml do Ant: você deixa o pom.xml na raiz do seu projeto para poder chamar as targets de build do seu projeto. O menor arquivo pom.xml válido é o seguinte:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>br.com.caelum</groupId>
  <artifactId>teste</artifactId>
  <version>1.0</version>
</project>

Que contém apenas a identificação do projeto, e uma informação a mais: modelVersion, que é a identificação da versão do arquivo pom.xml e deve ser sempre 4.0.0. A identificação do projeto consiste em três informações:

  • groupId: um identificador da empresa/grupo ao qual o projeto pertence. Geralmente o nome do site da empresa/grupo ao contrário. Ex: br.com.caelum.
  • artifactId: o nome do projeto. Ex: teste.
  • version: a versão atual do projeto. Ex: 1.0-SNAPSHOT.

Essas informações são usadas em muitos lugares, ccomo o controle de dependências que é, na minha opinião, a funcionalidade mais útil do Maven. Por exemplo, para dizer que o log4j 1.2.15 é uma dependência da sua aplicação é só acrescentar no seu pom as linhas:

<project>
...
  <dependencies>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.15</version>
    </dependency>
  </dependencies>
...
</project>

Quando necessário, o Maven vai baixar pra você o jar do log4j 1.2.15, e todas as suas dependências, e vai colocá-las no classpath da sua aplicação durante os builds, testes, etc. Ou seja, você não precisa mais entrar no site do log4j, baixar um zip com vários jars e ter que procurar quais jars devem ser colocados no classpath! No Repositório de Bibliotecas do Maven você encontra os jars que você pode colocar como dependência do seu projeto, e o pedaço de xml que você deve copiar e colar dentro da tag dependencies do seu pom para incluir essas bibliotecas.

Todos os jars baixados pelo Maven são guardados na pasta repository dentro da M2_HOME que você configurou quando instalou o Maven. Assim, se mais de um projeto seu depende do mesmo jar, ele não é baixado de novo.

A grande diferença entre o build.xml do Ant e o pom.xml do Maven é o paradigma. No Ant usamos esse XML praticamente como uma linguagem de programação, onde você da comandos em relação ao build do projeto. No Maven usamos o XML para definir a estrutura do projeto, e a partir dessas declarações o Maven possui targets bem definidos que usam essas informações para saber como realizar aquela tarefa. Um exemplo: para compilar com o Ant criamos um target que chama o javac, mas para compilar com o Maven usamos um target já existente (não o criamos), e ele vai usar a informação que define onde está o código fonte e para onde ele deve ser compilado (sendo que muitas dessas informações possuem convenções e defaults, e nem precisam ser configuradas).

Além dos principais targets do Maven, você pode executar targets de plugins. Você só precisa digitar na linha de comando:

mvn [nomedoplugin]:[target]

e então o Maven baixa o plugin, se necessário, e executa a target pra você. Existe uma lista bem grande de plugins do Maven e uma boa parte desses plugins podem ser usados sem nenhuma configuração adicional no seu pom.

Para dar um exemplo de plugin do Maven nada melhor do que o plugin que cria um protótipo de projeto do Maven: o Archetype. É bem parecido com o scaffold do Ruby: ele cria um protótipo de projeto a partir de um modelo escolhido. O jeito mais fácil de usar esse plugin é digitando na linha de comando:

mvn archetype:create

E então o Archetype vai perguntar qual é o tipo de projeto que você deseja, o groupID, artifactID, version e o pacote referentes ao seu projeto. Depois disso você terá uma estrutura de projeto pronta para ser usada.
Por exemplo se você escolheu o tipo de projeto maven-archetype-quickstart, o Archetype vai criar uma estrutura de pastas parecidas com a seguinte:


teste
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- br
    |           `-- com
    |               `-- caelum
    |                   `-- teste
    |                       `-- App.java
    `-- test
        `-- java
            `-- br
                `-- com
                    `-- caelum
                        `-- teste
                            `-- AppTest.java

E então é só continuar o seu projeto a partir daí. O código de teste já vem separado do código principal, e o junit já vem como dependência da aplicação. Você também pode criar as pastas src/main/resources e src/test/resources para colocar os recursos (arquivos de configuração, de teste, e etc) do código principal e do de testes, respectivamente. Tudo que estiver dentro dessas pastas é copiado diretamente para o diretório onde as classes são compiladas, sem que seja necessário fazer nenhuma configuração adicional.

Se você, por algum motivo, não gostou da estrutura que o Maven criou, ou está querendo migrar um projeto para o Maven que não segue essa estrutura, você pode configurar os diretórios do projeto
acrescentando algumas linhas no pom:

<project>
...
<build>
    <sourceDirectory>
      ${project.basedir}/src/java/main
    </sourceDirectory>
    <testSourceDirectory>
      ${project.basedir}/src/java/test
    </testSourceDirectory>
    <resources>
          <resource>
                 <directory>
                   ${project.basedir}/src/resources/main
                 </directory>
          </resource>
    </resources>
    <testResources>
          <testResource>
                 <directory>
                   ${project.basedir}/src/resources/test
                 </directory>
          </testResource>
    </testResources>
</build>

...
</project>

Nesse exemplo o diretório principal de código e de recursos estarão em src/java/mainsrc/resources/main respectivamente, e os diretorios de teste em src/java/test e src/resources/test.

Agora com um projeto Maven já preparado, vamos para a principal funcionalidade: o build. O build do Maven é baseado no conceito de ciclo de vida: o processo de construção e distribuição da sua aplicação é dividido em partes bem definidas chamadas fases, seguindo um ciclo. O ciclo padrão é o seguinte:

  • compile - compila o código fonte do projeto
  • test - executa os testes unitários do código compilado, usando uma ferramenta de testes unitários, como o junit.
  • package - empacota o código compilado de acordo com o empacotamento escolhido, por exemplo, em JAR.
  • integration-test - processa e faz o deploy do pacote em um ambiente onde os testes de integração podem ser rodados.
  • install - instala o pacote no repositório local, para ser usado como dependência de outros projetos locais
  • deploy - feito em ambiente de integração ou de release, copia o pacote final para um repositório remoto para ser compartilhado entre desenvolvedores e projetos

Você pode invocar qualquer dessas fases na linha de comando, digitando:

mvn [fase]

Por exemplo se você digitar mvn package o Maven vai executar todas as fases anteriores do ciclo até a fase package. Uma lista completa das fases do ciclo de vida possíveis pode ser encontrada aqui.

Algumas das fases do ciclo possuem plugins associadas a elas, e esses plugins são executados assim que a fase é chamada para ser executada. Você pode também registrar plugins para rodarem em qualquer fase do ciclo, conseguindo, assim, personalizar o build do seu projeto facilmente. Por exemplo, se você quiser criar um jar com o código fonte do projeto, e que esse jar seja gerado depois que o projeto foi empacotado, é só acrescentar no seu pom:

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-source-plugin</artifactId>
        <executions>
          <execution>
            <id>attach-sources</id>
            <phase>package</phase>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

Assim, o plugin Source vai executar seu goal jar na fase package do ciclo de vida. É como se fosse chamado mvn source:jar quando o build passa pela fase de package. A fase package já possui um plugin associado a ela: o jar:jar (supondo que é um projeto jar), então o plugin source só será executado depois do jar:jar. Em geral se você registrar mais de um plugin pra mesma fase, eles serão executados na ordem em que eles forem declarados. O jeito de configurar o plugin para colocá-lo dentro de uma fase do ciclo geralmente está no site principal do plugin, na seção Usage.

O Maven possui ainda outras funcionalidades interessantes, como geração de relatórios. Alguns plugins também merecem uma atenção especial, como o Eclipse que gera informações de projeto para o eclipse (.classpath e .project), o Antrun que te permite executar código Ant dentro do Maven, o Cobertura que gera um relatório mostrando a cobertura de testes no seu projeto, o Jetty que sobe uma instância do Jetty com sua aplicação deployed, o Selenium que sobe uma instância do servidor do Selenium para poder fazer os testes de aceitação do selenium, enfim, existem vários plugins interessantes e é relativamente fácil achar o plugin que faz o que você precisa. É igualmente fácil, também, fazer um plugin para o Maven, o chamado Mojo.

Aqui na Caelum, além do Maven e JUnit, usamos muito o Selenium, juntamente com o SeleniumDSL, para os testes de integração, e o Cruise Control para o controle da integração contínua. Esperamos colocar tutoriais e vídeos sobre essas ferramentas também.

JAXB - XML e Java de mãos dadas

Por Guilherme de Almeida Moreira em 27/02/08

Você já participou de um projeto que precisou ler um arquivo de configuração em xml? Já precisou consumir um xml e transformá-lo em objeto? O que você usou? Quem já trabalhou com xml sabe da dificuldade que podemos encontrar pelo caminho, e é esse tipo de dificuldade que a especificação Java Architecture for XML Binding ou simplesmente JAXB tenta resolver.

Imagine a seguinte situação: Precisarmos enviar os dados contidos em um objeto para um outro servidor. Temos muitas opções para fazer o envio, como por exemplo colocar essas informações em um arquivo de texto seguindo uma máscara pré-definida. Porém apenas as aplicações que conhecessem essa máscara entenderiam os dados, e perdemos portabilidade. Usando xml a situação já é outra: qualquer aplicação, independende de linguagem, entenderá os dados contidos no arquivo xml.

Antes de falarmos sobre o JAXB vamos primeiro conferir alguns conceitos:

XML
XML é uma linguagem de marcação que serve para guardar dados de uma forma estruturada. Essa estrutura é definida pelo próprio usuário ou por um schema. Um xml é um arquivo de texto puro, portanto independente de plataforma, por isso é muito utilizado para transmitir dados entre diferentes aplicações e sistemas. Exemplo:
carro.xml


<?xml version="1.0" encoding="UTF-8"?>
<carro>
  <nome>Fusca</nome>
  <portas>2</portas>
  <motoristas>
    <motorista>
      <nome>Guilherme</nome>
    </motorista>
    <motorista>
      <nome>Leonardo</nome>
    </motorista>
  </motoristas>
</carro>

XSD
XSD é o schema citado na seção anterior, ele define quais são as regras que a estrutura do xml deve seguir, possibilitando a validação desse xml. Exemplo:


<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <xsd:element name="carro" type="Carro" />
  <xsd:complexType name="Carro">
    <xsd:sequence>
      <xsd:element name="nome" type="xsd:string" minOccurs="1"
        maxOccurs="1" nillable="false"/>
      <xsd:element name="portas" type="xsd:int" minOccurs="1"
        maxOccurs="1" nillable="false"/>
      <xsd:element name="motoristas" type="Motorista" minOccurs="0"
        maxOccurs="unbounded"/>
    </xsd:sequence>
  </xsd:complexType>
  <xsd:complexType name="Motorista">
    <xsd:sequence>
      <xsd:element name="nome" minOccurs="1" maxOccurs="1"
        type="xsd:string" nillable="false"/>
    </xsd:sequence>
  </xsd:complexType>
</xsd:schema>

O primeiro ponto da especificação apresenta uma ferramenta chamada Binding Compiler, cuja função é transformar um xsd em um conjunto de classes que tenham uma estrutura compatível com a estrutura do xml que esse xsd define.

No XSD de exemplo definimos a seguinte estrutura: Um elemento carro deve ter um elemento nome e um elemento motoristas (do tipo Motorista), seguindo essa ordem, primeiro nome e depois motoristas. Depois definimos o tipo Motorista que deve conter apenas um nome.

O Binding Compiler é independente da implementação do JAXB, ou seja, quem define como ele será executado é quem implementa a especificação, porém a maioria e inclusive a própria RI(Reference Implementation) cria um comando que pode ser chamado pela linha de comando do Sistema Operacional, o xjc. Por exemplo no Linux:

xjc carro.xsd -d src -p br.com.caelum

Se você já está usando o Java 6, o JAXB já vêm junto com o JDK.

Com esse comando o Binding Compiler gera três classes: Carro.java, Motorista.java e a ObjectFactory.java. As classes Carro e Motorista seguem a estrutura do xsd.

Gerando e Lendo XML
A segunda parte da especificação define o que temos que fazer para transformar objetos em xml e vice-versa. A API do JAXB é quem se responsabiliza por essas transformações.

Transformando objetos em xml
O processo de transformar um objeto em xml é chamado de Marshal. Com o JAXB para transformar um objeto em xml precisamos de um JAXBContext, esse context é quem fornecerá o Marshaller. O Marshaller é quem finalmente transforma um objeto (JAXBElement) em xml. O JAXBElement contém o objeto de verdade a ser serializado e algumas propriedades do xml. É aqui que entra a importância do ObjectFactory criado pelo Binding Compiler, ele é responsável por criar uma instância do JAXBElement apropriada para o tipo de objeto a ser serializado.


  JAXBContext context = JAXBContext.newInstance("br.com.caelum");
  Marshaller marshaller = context.createMarshaller();
  JAXBElement<Carro> element = new ObjectFactory().createCarro(carro);
  marshaller.marshal(element, System.out);

Parseando xml em objetos java
Para fazer o caminho contrário, ou seja popular um objeto java com dados de um xml também precisamos de um JAXBContext, porém agora temos que pegar um Unmarshaller. O Unmarshaller recebe um arquivo xml e devolve um JAXBElement contendo um objeto populado.


  JAXBContext context = JAXBContext.newInstance("br.com.caelum");
  Unmarshaller unmarshaller = context.createUnmarshaller();
  JAXBElement<Carro> element = (JAXBElement<Carro>unmarshaller.
    unmarshal(new File("resources/carro.xml"));
  Carro carro = element.getValue();

Conclusão
O JAXB facilita muito a vida dos programadores java, fazendo o consumo e criação de xml menos trabalhosos. Essa API também fornece outros recursos como, validação, geração de schema (a partir de classes java, cria um xsd), opções para trabalhar com Namespace e etc. Comente nesse post outras oções do JAXB e outras bibliotecas que você usa no seu dia-a-dia.

Ruby, Capistrano e administração remota

Por Lucas Cavalcanti em 06/08/07

Algumas vezes nos deparamos com a seguinte situação:

Temos uns 15 computadores em rede e precisamos executar um mesmo comando em cada um deles, como por exemplo instalar a JavaVM…

Uma primeira solução seria logar como root máquina por máquina, e executar o comando em todas… Muito ruim…

Agora, se você souber um pouquinho de bash script, em cinco minutos conseguirá fazer um script que loga via ssh em todas as máquinas e executa o comando nelas. Mas ainda tem o problema de você ter que digitar o password para cada máquina, e de ter que assistir a execução do comando, caso ele precise de alguma confirmação, ou algo do tipo…

Felizmente existe uma ferramenta muito útil que pode nos ajudar nessa tarefa entediante. E ela se chama Capistrano (www.capify.org). Com os mesmos 5 min você consegue escrever um script que execute o comando que você quer, digite as confirmações pra você nos lugares necessários, e você só precisará digitar a senha uma única vez!

O Capistrano requer que você o use em sistemas compatíveis com bash, e que exista um mesmo usuário em todas as máquinas que tenha uma única senha.

Para o script funcionar você precisa, com o capistrano instalado corretamente:

  • Definir o usuário a que você vai se conectar nos micros:

    set :user = "root" #pode ser qualquer usuário

  • Definir os ips dos micros aos quais você irá se conectar:

    role :micros = "192.168.0.10", "192.168.0.11"

  • Ou se for uma faixa de IPs:

    role :micros = *("192.168.0.10" .. "192.168.0.25").to_a

  • E, finalmente, definir a tarefa que você quer executar, ou seja, o(s) comando(s). Para isso temos que passar os roles a que queremos nos conectar (os micros)
    task :instalaJava, :roles => :micros do
      run "apt-get install java-virtual-machine"
    end
  • Pronto! O comando será executado em todas as máquinas!
    Ou melhor, quase pronto…
    Você sabe que o apt-get pode pedir confirmação para continuar instalando o programa pedido…
    Você pode automatizar isso usando um recurso muito útil do capistrano:

    run "apt-get install java-virtual-machine" do |channel, stream, data|
      #channel é um objeto que nos permite enviar dados para o comando
      #stream identifica se a saída do comando foi a padrão (:out) ou a de erros(:err)
      #data é uma string com a saída do comando
      if data =~ /Do you want to proceed/ #se na saída do comando tiver essa frase
        channel.send_data "y\\n" #manda pro comando um y e um enter
      end
      puts data #pra saída do comando aparecer na tela
    end

    Agora sim, o script vai fazer o que a gente queria: executar o comando e confirmar!
    O comando run vai ser executado em todos os micros paralelamente, e o bloco de código entre do e end do run vai ser executado toda vez que o comando “apt-get” jogar algum texto na tela ou na saída de erros.

    Pra executar o script basta salvar o código que escrevemos como “capfile” e chamar pela linha de comando cap + o nome da nossa task:

    $ cap instalaJava

    O comando irá mostrar qual task está sendo executado e pedirá uma vez a senha. E é só ficar assistindo, ou ir fazer algo mais produtivo.

    Nesse arquivo capfile você pode usar toda a sintaxe e as funcionalidades de um rakefile e, consequentemente, código em ruby. Então dá pra fazer coisas bem legais, como chamar tasks dentro de tasks, popular um role dinamicamente, executar uma tarefa antes e/ou depois da task chamada, enfim, muitas coisas…

    Você só terá que se preocupar com uma coisa: se um dos micros que estiver no role da task não estiver acessível pela rede, ou se acontecer um erro fatal qualquer, o capistrano não irá executar a task em nenhum dos micros.

    É isso. Divirta-se!

WebServices sem servidor de aplicação no java 6

Por Nico Steppat em 11/07/07

Criando WebServices num servidor de aplicação como glassfish ou jboss ficou muito fácil. Basta escrever um Session Bean Stateless e usar as anotações do pacote javax.ws que JavaSE 6 já fornece. Veja o exemplo:

@Stateless
@Remote(Oi.class)
@WebService
public class OiMundo {
  
  @WebMethod
  public String oi(String nome) {
    return "Hallo " + nome;
  }
}

public interface Oi 
  String oi(String nome);
}

Pronto, fazendo deploy dessa classe no servidor do aplicação cria automaticamente o WebService e disponibiliza o WSDL. Aqui tem uma discussão mais ampla sobre webservices em um servidor de aplicação.

Sem servidor de aplicação

Com javaSE 6 nas mãos você nem precisa de um servidor de aplicação, nem mesmo um web server! Basta usar as anotações @WebService e @WebMethod na sua classe e a ferramenta apt (annotation processing tool – não o apt do Debian!, encontra-se na pasta bin do jdk) para gerar um WebService.

A conhecida classe:

@WebService
public class OiMundo {
  
  @WebMethod
  public String oi(String nome) {
    return "Hallo " + nome;
  }
}

O comando (imagina a classe OiMundo está no pacote br.com.caelum.ws):

$apt br/com/caelum/ws/OiMundo.java

gera um pacote br.com.caelum.ws.javaws com duas classes, uma classe Oi.java para os parâmetros do WebService (no nosso caso String nome do método oi) e outra OiResponse.java que representa o retorno.

Falta publicar o serviço/endpoint e iniciá-lo:

public class PublicaService {
  
  public static void main(String[] args){
    OiMundo service = new OiMundo();
    Endpoint endpoint = Endpoint.publish("http://localhost:8080/oi", service);        
  }
}

Pronto, o WebService está rodando sem servidor de aplicação nem web container. Tudo isso com javaSE 6 jdk. Para acessar a wsdl dele, usa-se o URL:

http://localhost:8080/oi?wsdl

Claro que isso não substitui o servidor por questões de performance, configuração e manutenção, mas é útil na hora de desenvolver e testar um serviço rapidamente.

Cliente

Para criar o cliente do serviço, javaSE 6 já vem com as ferramentas necessárias. Precisamos usar o wsimport (na pasta bin do jdk) para gerar as classes do cliente. Com o serviço rodando, fazemos na linha de comando:

$wsimport -keep -p br.com.caelum.cliente http://localhost:8080/oi?wsdl

A opção keep não apaga os arquivos fontes e p gera as classes dentre do pacote especificado.

Para chamar o serviço pelas classes geradas, escreva a seguinte classe dentro do pacote br.com.caelum.cliente:


public class TesteServico {
  
  public static void main(String[] args) {
    
  br.com.caelum.cliente.OiMundo port = new OiMundoService().getOiMundoPort();
  System.out.println(port.oi("Johann"));
  }
}

Repare que a classe OiMundo foi gerada pelo wsimport e é uma interface, não confundir com o serviço OiMundo que é uma implementação.

WebServices no WebContainer

Num WebContainer, usando Tomcat ou Jetty, é preciso usar um framework para criar um WebService. Já foi mencionado o Xfire nesse mesmo blog (que agora faz parte do projeto CXF), mas existem outros como o AXIS da Apache ou Metro do java.net. Metro é a implementação usada pelo Glassfish.

Vamos usar o Metro para criar um WebService dentro numa aplicação web usando Tomcat.

Faça o download do metro, instale e copie os jars da pasta metro/lib para a pasta tomcat/shared/lib, ou então para o lib exclusivo do seu projeto. São os seguintes:

  • webservices-api.jar
  • webservices-extra.jar
  • webservices-tools.jar
  • webservices-extra-api.jar
  • webservices-rt.jar
  • wstx-services.war

Crie um aplicação web comum e adicione a classe TchauMundo no seu projeto com as anotações:

@WebService
public class TchauMundo {

  @WebMethod
  public String tchau(String nome) {
    return "Tschüss " + nome;
  }
}

Gere novamente o serviço e execute o seguinte comando na pasta src do seu projeto:

$apt br/com/caelum/ws/TchauMundo.java

As classes geradas devem ficar na pastas WEB-INF/classes.

Agora é preciso registrar um servlet e listener que recebe as requisições do WebService. O web.xml:

<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>webServiceServlet</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>webServiceServlet</servlet-name>
<url-pattern>/tchau</url-pattern>
</servlet-mapping>
</web-app>

Além disso, falta um outro xml (mais um!) com a definição do serviço/endpoint. O arquivo sun-jaxws.xml deve estar na pasta WEB-INF. Veja o exemplo:


<endpoints xmlns='http://java.sun.com/xml/ns/jax-ws/ri/runtime' version='2.0'>
<endpoint
name='tchauMundo'
implementation='br.com.caelum.ws.TchauMundo'
url-pattern='/tchau'/>
</endpoints>

Fazendo o deploy e acessando a seguinte url:

http://localhost:8080/ws/tchau

temos o nosso serviço:

WebServices sem servidor de aplicação

A entrada de uma API para produzir e consumir webservices no Java SE 6 gerou muita polêmica, por inflar o tamanho da Java RE e ser pouco necessário na maioria dos projetos. Apesar disso, ter uma API unificada, além de ferramentas facilitadoras, possibilita uma mais fácil adoção de webservices por parte dos desenvolvedores Java.

Competições de programação: Google, IBM e FISL

Por Guilherme Silveira em 19/04/07

A alguns anos atrás fiz uma entrevista em uma grande empresa de Java, uma dessas com 3 letras, e a primeira frase que o entrevistador me dirigiu ao ver meu curriculum foi um sonoro:

Essas competições de programação não dizem nada!

Essas competições não dizem se o programador é um bom engenheiro de software ou profundo conhecedor de alguma tecnolgoia, é verdade. Nem é essa a intenção.
As questões dessas provas apresentam problemas mais complexos que o comumente exigido no mercado de TI, e não foca tanto na parte de desenvolvimento, mas mais na capacidade de encontrar soluções. Para um cientista da computação (ou um matemático, no meu caso), essa capacidade é mais importante do que o próprio desenvolvimento e codificação. Vou relatar aqui três competições as quais tiver a oportunidade de participar este ano. Em todas as três, grandes empresas como o Google e IBM, sempre estavam a caça de talentos. Pode-se dizer que um bom resultado nessas competições certamente atrairão diversas propostas de emprego, mestrado e doutorado.

Primeiro veio o Google Code Jam Latin America. Os problemas tinham o mesmo nível das competições internacionais, mas com menos fases. Na última etapa, em Belo Horizonte, tive a chance de encontrar alguns conhecidos e fazer novas amizades no meio dos 50 finalistas, dentre elas Jelani Nelson, aluno de PhD e técnico do MIT, participando pela Virgin Islands; Vinicius Fortuna ex-competidor e agora engenheiro de software do Google em Nova Iorque, que veio entrevistar os competidores; Wanderley Guimarães, atual técnico do IME-USP, além de já conhecidos e excelentes competidores do ITA e PUC-RIO. Pelo vídeo promocional, da para você ter uma idéia do quão a sério o Google leva essas competições:

Depois teve a final mundial da ACM (ACM-ICPC) em Tokyo, Japão, onde fui representando o time do IME-USP, depois da qualificação pela Maratona de Programação brasileira. Lá contamos com a presença de diversos nomes interessantes, como o criador do Ruby, Yukihiro Matsumoto, que palestrou sobre sua linguagem de programação. Entre seus comentários oportunos, disse que eficácia não é a grande preocupação de Ruby; 80% do design de uma linguagem está feito se o nome for curto, pequeno e bonito (Ruby). Outra frase dele foi “Web 2.0, o que é isso?”. Modesto.

Também estava presente Stuart Feldman que, além de presidente da ACM e criador do primeiro compilador de Fotran 77, fez parte do grupo que criou o Unix e o make. Yuhichi Nakamura, criador do Xerces (xml4j), e diretor do IBM Tokyo Research Lab estava ajudando na coordenação do evento.

Nessa competição, mais de seis mil equipes participaram do evento nas competições regionais, de mais de 1700 universidades diferentes do mundo inteiro. A competição foi emocionante, com os quatro times brasileiros bem colocados. Além disso, dois times brasileiros resolveram 4 problemas, um marco na história da competição, onde o problema mais simples envolvia um algoritmo do tipo Longest Common Subsequence. Esse é um algoritmo muito usado em inúmeros lugares, como em biologia computacional para verificar padrões de sequencias de DNA. No fim das contas, os problemas dessas competições não são tão irreais assim! Alguns problemas são muito difíceis, sendo que nenhuma equipe do mundo conseguiu resolver. Você pode ver a prova da competição aqui.

O evento aconteceu no Hilton Tokyo Bay, um hotel dentro da Disney de Tokyo e muito bonito. Os quartos tinham vista para o monte Fuji e foi possível conhecer um pouco do país e de sua história.

07-DH_CON-_D4V0186[LO] 07-DH_TP-_MG_0281
07-DH_CON-_D4V0178[LO] 07-DH_CON2-_D4V0240[LO]

O Fórum Internacional de Software Livre preparou esse ano uma competição diferente, a Arena de programação, composta por duas fases. Para poder se inscrever, era necessário resolver um problema de lógica no próprio site, no estilo python-challenge, mas bem mais simples. A idéia era encontrar o caminho para a inscrição usando de seus dotes “hackers”. O Dorneles postou a descrição de como se inscrever em detalhes.

Chegando no evento, pronto para a primeira fase, a Arena era um cercado de vidro, quase um aquário, onde os animais eram os programadores.

A primeira fase terminou com alguns conhecidos entre os top. O Hugo Corbucci, colega de viagem ao fisl pelo IME-USP, o Klaus Wustefeld e o Kalecser Kurtz, o Dornelles Tremea e eu nos qualificamos entre os top 12. A segunda fase começou no dia seguinte e era composta por, supostamente, 24 horas de programação non-stop. Os 12 primeiros da fase anterior foram divididos em quatro grupos de três competidores. Na minha equipe? Kalecser, expert em Java e Dornelles, expert em Python e linux em geral. A tarefa? Resolver quatro bugs ou feature requests do Debian. Isso mesmo. Eles deram quatro códigos de bugs que estão ocorrendo em pacotes do Debian ou que feature requests que os usuários postaram e pediram para as equipes resolverem. Excelente proposta!

O primeiro bug envolvia um problema com o teclado. Você que é fã incondicional do Debian pode alternar para o tty1 (Ctrl+alt+F1), ativar o CAPS LOCK e tentar digitar “ABCD”… se o resultado for “ABcD”, você reproduziu o bug - que só funciona com alguns layouts de teclado. Encontramos muitas informações sobre o assunto e não conseguimos resolvê-lo, fomos capazes de encontrar textos indicando que o problema era mais delicado. Tudo isso após passar por muito código fonte em bash, perl etc. O segundo bug estava no pacote cdebconf, que é responsável pelo processo de configuração de pacotes debian durante o processo de instalação dos mesmos. Após correr por diversos arquivos feitos em C, com um “quê” de orientação a objeto, fomos capazes de isolar o bug e corrigi-lo, e o patch deve se tornar disponível em breve.

O terceiro bug envolve o bugtracking do debian, que não apresentava os dados da maneira que era requisitado, enquanto o último problema estava ligado ao particionador, que encontrava problemas durante o redimensionamento em determinados casos. Muito código perl apareceu enquanto solucionávamos o terceiro bug, mas não houve tempo suficiente para sequer olhar com calma o último. No dia seguinte, a segunda parte dessa fase envolveu o desenvolvimento do zero de um programa para facilitar a internacionalização dos pacotes debian. Nosso objetivo era utilizar o Lucene para fazer o partial matching de palavras ou frases já traduzidas anteriormente em outros pacotes, mas devido ao padrão de i18n adotado pelo debian, o Java acabou segurando um pouco o desenvolvimento, mas ainda fomos capazes de mostrar algo funcional com uma implementação do Edit Distance para procurar palavras similares.

O resultado de tudo isso saiu no final do dia, durante o fechamento do evento, nossa equipe ficou em primeiro lugar! O que ganhei com tudo isso? Conheci novas pessoas que trabalham com projetos open source de áreas diferentes de Java, coloquei em prática o meu conhecimento de C que só usava na teoria, e ajudei com um projeto que jamais sonhei ser possível ajudar. Claro, o notebook que deram para cada um de nossa equipe também conta! Fui entrevistado a respeito dessa competição. Espero encontrar vocês nas próximas!