Página 4 de 10

Instalando, configurando e usando o Maven para gerenciar suas dependências e seus projetos Java

Este artigo é uma introdução ao Maven, uma ferramenta de gerenciamento de dependências e ciclo de vida de um projeto em Java.

Para usar o Maven, você precisa ter o JDK instalado. Você também pode integrá-lo à sua IDE. Para saber como instalar o JDK e o Eclipse, consulte os seguintes artigos:

O que é o Maven?

O Maven é uma ferramenta de gerenciamento de dependências e do ciclo de vida de projetos de software no sentido técnico. Isso inclui:

  • Facilitar a compilação do código, o empacotamento (JAR, WAR, EAR, …), a execução de testes unitários, etc.
  • Unificar e automatizar o processo de geração do sistema. Nada mais de uma coleção de passos e scripts a serem executados manualmente.
  • Centralizar informações organizadas do projeto, incluindo suas dependências, resultados de testes, documentação, etc.
  • Reforçar boas práticas de desenvolvimento, tais como: separação de classes de teste das classes do sistema, uso de convenções de nomes e diretórios, etc.
  • Ajuda no controle das versões geradas (releases) dos seus projetos.

Conceitos importantes

Artefato (artifact)

Um artefato é geralmente um arquivo JAR que fica no repositório do Maven, mas pode ser de outro tipo.

Cada artefato é identificado através dos seguintes elementos:

  • Grupo: é como o endereço do site ao contrário, como br.com.starcode, org.apache, com.google, com.ibm, etc.
  • Identificador único de artefato: geralmente é o nome do projeto. Ele deve ser único por grupo.
  • Número de versão: a versão do projeto, como 1.4.2 ou 3.0. Se houver o sufixo -SNAPSHOT (0.0.1-SNAPSHOT, por exemplo) significa que o projeto está em desenvolvimento e o pacote pode ser alterado.
  • Tipo do projeto: jar, war, ear, pom (projeto de configuração).
  • Classificador: identificação opcional para diferenciar variações da mesma versão. Por exemplo, se o programa é compilado para diferentes versões do Java podemos usar os classificadores jdk4 e jdk6. Se há variações específicas para Sistemas Operacionais, podemos ter os classificadores linux e windows.

Repositório local

É um diretório em seu PC onde os artefatos são armazenados após baixados de um repositório remoto na internet ou na intranet. Você também pode instalar os artefatos dos seus projetos nesse repositório executando o install do Maven. Continue lendo para entender o que é isso.

O repositório possui uma estrutura padrão onde o Maven consegue encontrar os artefatos através da identificação do mesmo.

Repositório remoto

Consiste numa aplicação que disponibiliza artefatos do Maven. Pode se um repositório público na Internet, onde criadores de bibliotecas e frameworks disponibilizam seus artefatos, ou pode ser um repositório privado da empresa, disponível na intranet.

Existe um repositório central que já vem configurando no Maven, mas algumas empresas criam seus próprios repositórios. Inclusive você pode criar o seu instalando o Artifactory ou Nexus num servidor.

Quando adicionamos esses repositórios remotos em nossa instalação do Maven, ele é capaz de localizar e baixar automaticamente as dependências através da identificação do artefato.

Arquivo POM

O arquivo pom (pom.xml) é a configuração principal do Maven e estará presente em todo projeto. Nele você declara a identificação do seu projeto (que após gerado será também um artefato Maven), as dependências, repositórios adicionais, etc.

Há um arquivo pom por projeto, mas ele pode herdar configurações de um parent pom, isto é, como se houvesse um projeto "pai".

Ciclo de vida padrão do Maven

O Maven possui um ciclo de vida padrão. Cada passo do ciclo de vida é chamado de Goal e possui plugins específicos. Os mais importantes são:

  • validate: valida o projeto e se as informações necessárias para os próximos passos estão disponíveis, como as dependências por exemplo.
  • compile: compila o código-fonte.
  • test: executa os testes unitários (JUnit, por exemplo).
  • package: empacota o código compilado em um JAR, WAR, etc.
  • integration-test: executa os testes de integração.
  • install: adiciona o pacote gerado ao repositório local, assim outros projetos na mesma máquina podem usar essa dependência.
  • deploy: copia o pacote final para o repositório remoto, disponibilizando-o para outros desenvolvedores e outros projetos.

Os itens acima, nessa ordem, são passos comuns para geração de uma versão de qualquer sistema, não é mesmo?

No Maven, você pode configurar detalhadamente cada um desses passos e até incluir novos. Por exemplo, alguns frameworks que geram código-fonte usam um goal generate-sources para fazer isso.

Além disso, não é necessário executar todos os passos sempre. Você pode escolher qual deseja executar num determinado momento, mas o Maven sempre executará todos os passos anteriores.

Por exemplo, enquanto você está desenvolvendo um módulo, a cada alteração pode executar o passo test para executar a validação, compilação e então os testes unitários. Então você só precisa executar os passos posteriores quando tiver concluído o trabalho.

Para maiores informações sobre o ciclo de vida, consulte a documentação.

Estrutura padrão de um projeto Maven

A estrutura padrão do projeto inclui boas práticas (como separar as classes de teste das classes do sistema) e facilita aos novos desenvolvedores encontrar o que eles querem, já que todos os projetos seguirão uma estrutura semelhante.

Veja a seguir os principais diretórios utilizados:

  • src/main/java: aqui fica o código-fonte do sistema ou biblioteca.
  • src/main/resources: arquivos auxiliares do sistema, como .properties, XMLs e configurações.
  • src/main/webapp: se for uma aplicação web, os arquivos JSP, HTML, JavaScript CSS vão aqui, incuindo o web.xml.
  • src/test/java: as classes com seus testes unitários ficam aqui e são executadas automaticamente com JUnit e TestNG. Outros frameworks podem exigir configuração adicional.
  • src/test/resources: arquivos auxiliares usados nos testes. Você pode ter properties e configurações alternativas, por exemplo.
  • pom.xml: é o arquivo que concentra as informações do seu projeto.
  • target: é o diretório onde fica tudo que é gerado, isto é, onde vão parar os arquivos compilados, JARs, WARs, JavaDoc, etc.

Para ver mais detalhes sobre a estrutura de diretórios do Maven, consulte a documentação.

Usando o Maven em projetos já existentes

Você pode ter ficado desapontado com a estrutura anterior, pois estava pensando em usar o Maven em um projeto que já começou, mas não quer ou não pode mudar a estrutura de pastas atuais.

Saiba que o Maven é flexível e permite alterar toda a estrutura padrão.

Por exemplo, é comum usar a pasta src para os fontes, ao invés de src/main/java. Para ajustar isso, basta adicionar uma tag <sourceDirectory> dentro da tag <build>, assim:

<project>
    ...
    <build>
        <sourceDirectory>src</sourceDirectory>
        ...
    </build>
    ...
</project>

Não vou ir fundo nessa questão, mas se o leitor tiver um projeto em condições semelhantes, sugiro uma leitura mais completa da documentação, começando com Using Maven When You Can’t Use the Conventions.

É claro que nem tudo é tão simples. Muitos projetos usam estruturas tão diferentes que se exige a refatoração desta estrutura.

Benefícios do Maven

A adoção do Maven no desenvolvimento traz de imediato os seguintes benefícios:

Centralização das informações

O Maven centraliza as informações dos projetos no arquivo pom.

Sendo assim, não é preciso configurar várias ferramentas, build scripts, servidores e IDEs durante o desenvolvimento. O Maven segue o conceito DRY (Don’t Repeat Yourself).

Além disso, o Maven também disponibiliza formas de analisar o projeto. Por exemplo, o goal dependency:analyze exibe as dependências declaradas que não estão sendo usadas e as usadas via terceiros, mas não declaradas no pom.

Padronização do ambiente de desenvolvimento

Através da especificação do projeto, incluindo suas características e dependências, o Maven constrói a estrutura necessária do projeto, baixando automaticamente as versões corretas das dependências (JARs, por exemplo) de um repositório central ou de um repositório privado (contendo sistemas da empresa).

Você não precisa entrar no site de cada biblioteca e framework usado e então fazer manualmente o download e adicionar os jars no seu classpath.

Dessa forma, cada desenvolvedor consegue configurar rapidamente um ambiente para desenvolvimento com a garantia de que esse ambiente é igual ao dos outros desenvolvedores.

Gerenciamento de dependências

Como já mencionei, o Maven faz o download automático de dependências para o projeto e os adiciona ao classpath do seu projeto.

Cada dependência pode ter também as suas próprias dependências. Elas são chamadas dependências transitivas. O Maven resolve essa árvore de dependências e traz tudo o que você precisa.

Em alguns casos, podem haver problemas de conflitos, no caso da árvore de dependências incluir versões diferentes de um mesmo artefato. O Maven vem com mecanismos para resolver isso.

Facilidade de compreensão do projeto

Ao usar a convenção de diretórios sugerida pelo Maven os desenvolvedores terão mais facilidade em compreender a estrutura do projeto, afinal todos os projetos seguirão uma estrutura básica de diretórios, como vimos anteriormente.

Automação

O Maven gerencia o ciclo de vida da aplicação. Após configurar um projeto, você será capaz de executar comandos que vão desde a compilação até a geração de documentação (Javadoc) e um site padrão contendo informações sobre o projeto.

Uma vez feita a configuração necessária, o projeto pode ser baixado e compilado sem nenhum esforço. Novas versões podem ser geradas em servidores de Integração Contínua e testadas automaticamente sempre que necessário.

Um alerta

Embora os tópicos anteriores tenham enumerado diversas vantagens do uso do Maven, este não é uma "bala de prata", ou seja, uma solução mágica para o projeto.

Dependendo da complexidade do projeto, pode ser bem complicado criar uma configuração adequada para ao Maven.

Além disso, o Maven não irá livrá-lo de problemas como:

Incompatibilidade de dependências

O projeto depende dos frameworks A e B. O framework A depende a versão 1.0 da biblioteca C. O framework B depende da versão 2.0 da biblioteca C.

O Maven não vai resolver sozinho isso, mas facilita muito a resolução do problema já que podemos usar as informações do mecanismo de resolução de dependências para identificar os conflitos.

Algumas tecnologias simplesmente não vão funcionar bem com o Maven

Alguns autores de frameworks ou arquiteturas inventam o seu próprio jeito de trabalhar. Isso significa que para usar o Maven é necessário alguma adaptação, o que nem sempre é trivial.

No entanto, é possível escrever plugins para o Maven que façam o trabalho para você. Geralmente a comunidade de cada framework, se não os próprios autores, já terão resolvido esse problema. Embora existam casos em que essas soluções acrescentem novas limitações.

Ódio do Maven

A verdade é que existe muita gente que odeia o Maven por ter vivido experiências ruins com ele, principalmente no início. Infelizmente, não sei se este artigo terá o poder de curá-lo de traumas passados ao tentar usar o Maven sem a devida orientação. 😉

No entanto, não deixe que isso influencie você neste momento. Mesmo que não pretenda usar o Maven em seus projetos, vale a pena conhecê-lo. Você pode ser obrigado a usá-lo na empresa ou mesmo num projeto opensource de que vai participar.

Se você não gosta do Maven, tenha duas coisas em mente:

  1. Existem várias alternativas, desde scripts Ant até outras ferramentas de resolução de dependências mais avançadas como o Graddle.
  2. Embora algumas pessoas atinjam um excelente nível de produtividade sem o Maven, se considerarmos um contexto mais amplo, como um projeto que com mais de meia dúzia de desenvolvedores, alguns deles novatos, o Maven pode trazer mais vantagens que desvantagens se bem configurado por um desenvolvedor experiente.

Instalando o Maven

Acesse a página do Maven e clique no item Download do menu.

A página disponibiliza diferentes versões para diferentes ambientes. Baixe o arquivo da última versão de acordo com seu sistema operacional. Destaquei na imagem a versão zip para Windows que usarei neste exemplo:

01

A versão mais atual do Maven na data de criação deste tutorial é 3.2.1. O pacote baixado é nomeado apache-maven-3.2.1-bin.zip. Veja o arquivo aberto no 7-Zip:

02

Descompacte o conteúdo para a pasta c:\starcode\.

03

Configura o resultado na imagem a seguir:

04

Configurando o Maven

O Maven é configurado através do arquivo settings.xml que fica no diretório c:\starcode\apache-maven-3.2.1\conf.

Abra o arquivo usando um editor avançado, como o Notepad++. Você vai ver que existem diversos blocos de XML comentados com possíveis configurações e explicações sobre elas.

Em um ambiente simples você não vai precisar mexer em muita coisa. Porém, vamos ver alguns pontos mais importantes.

Proxy

É muito comum precisarmos autenticar o acesso à internet em um Proxy quando estamos no trabalho. Procure a tag <proxy>, a qual deve estar comentada no arquivo de configuração. O trecho é o seguinte:

<proxy>
    <id>optional</id>
    <active>true</active>
    <protocol>http</protocol>
    <username>proxyuser</username>
    <password>proxypass</password>
    <host>proxy.host.net</host>
    <port>80</port>
    <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
</proxy>

Se você tem um proxy na sua rede, mova o bloco acima para fora do comentário, então substitua os parâmetros de acordo com seu ambiente. Mantenha a tag <proxy> dentro de <proxies>.

Veja abaixo um exemplo de uso:

<proxies>
    <proxy>
        <id>proxy</id>
        <active>true</active>
        <protocol>http</protocol>
        <host>proxy.intranet.empresa.com</host>
        <port>8080</port>
        <nonProxyHosts>localhost,127.*,192.*</nonProxyHosts>
    </proxy>
</proxies>

Local do repositório

O Maven utiliza um diretório local para baixar os artefatos da internet. O diretório padrão fica dentro pasta do usuário, na pasta .m2. Um exemplo no Windows é c:\users\luiz\.m2\repository.

Entretanto, tenho o hábito de mudar esse diretório para junto de meus arquivos de desenvolvimento. Para isso, basta editar o settings.xml, movendo a tag <localRepository> para fora do comentário e adicionando o caminho, por exemplo:

<localRepository>c:\starcode\apache-maven-3.2.1\repo</localRepository>

Não se esqueça de criar o diretório especificado caso o mesmo não exista.

Configurando as variáveis de ambiente

Para usar o Maven em linha de comando você deve adicionar o caminho para os executáveis ao PATH do ambiente. No Windows, pressione Ctrl+Break para abrir a tela de informações do do sistema.

05-configurar-maven

Clique na opção Configurações avançadas do sistema, à esquerda da janela.

06-configurar-maven

Na aba Avançado da tela de Propriedades do Sistema, clique em Variáveis de Ambiente....

07-configurar-maven

Você pode adicionar a variável de ambiente apenas para o usuário atual ou globalmente para todo o sistema. Faça o que for melhor para o seu caso. Alguns ambientes corporativos impedem o acesso à configuração de sistema por questões de segurança, então você terá que configurar apenas seu usuário.

Clique em Novo... e crie a variável M2_HOME com o valor apontando para o diretório base do Maven. No nosso exemplo o valor é c:\starcode\apache-maven-3.2.1.

08-configurar-maven

Clique em OK para criar a variável.

09-configurar-maven

Agora vamos incluir o diretório com os executáveis do Maven ao PATH. Localize a entrada, selecione-a e clique em Editar....

10-configurar-maven

Adicione ao final um ponto e vírgula e o caminho para a pasta bin do Maven (;%M2_HOME%\bin), assim:

11-configurar-maven

Clique em OK para confirmar a edição e OK novamente para confirmar as alterações nas variáveis do sistema.

Vamos então testar a instalação. Abra o CMD (linha de comando) e digite mvn -version. Você deve ver algo como na figura abaixo:

12-configurar-maven

Se ocorreu algum problema, verifique se você tem o Java instalado e configurado corretamente, incluindo as variáveis de ambiente JAVA_HOME e PATH incluindo o Java. Caso tenha dúvidas, acesse o artigo citado no início sobre a instalação do JDK.

Usando o Maven

Usando a instalação do Maven no Eclipse

Para integrar o Maven e Eclipse eu aconselho o plugin M2E. Note que o M2E é um plugin do Eclipse que faz integração com o Maven.

Existe também o Maven Eclipse Plugin, aquele onde se digita eclipse:eclipse para gerar um projeto para o Eclipse. Este é um plugin do Maven que simplesmente gera os arquivos de configuração necessários para a IDE. Não confunda os dois.

A distribuição Eclipse for JEE Developers já vem com o plugin M2E e uma instalação interna do Maven. Veja como instalar e usar o Eclipse acessando o artigo citado no início.

Se você tem uma versão diferente do Eclipse, use o menu Help > Eclipse Marketplace..., pesquise por M2E e instale o plugin.

Com o plugin instalado e o Eclipse aberto, acesse o menu Window > Preferences... e vá até a opção Maven > Installations.

01-maven-eclipse

Veja que já existe uma instalação "embarcada", mas com uma versão desatualizada. Vamos adicionar o nosso Maven.

Clique em Add... e selecione a pasta com a nossa instalação, no caso: c:\starcode\apache-maven-3.2.1.

02-maven-eclipse

Note que ele já encontrou nosso arquivo de configuração.

Vá até o menu User Settings. Há um warning lá dizendo que a configuração do usuário não existe. Você pode criar um outro settings.xml na pasta indicada ou simplesmente use um artifício (que eu sempre uso), que é definir o mesmo arquivo da configuração global.

03-maven-eclipse

Caso não tenha entendido, o Maven possui um arquivo de configuração global que afeta diretamente a instalação e fica na pasta conf. Entretanto, cada usuário do sistema pode ter um arquivo próprio e sobrescrever as configurações globais que ele desejar. No entanto, se você é o único usuário do computador, não é necessário ter os dois arquivos.

Criando um projeto Maven simples no Eclipse

Com o Maven configurado, vamos criar um novo projeto no Eclipse. Acesse o menu File > New > Maven Project. Selecione a opção Create a simple project (skip archetype selection) e clique clique em Next >.

01-projeto-simples

Vamos preencher a identificação do projeto, que nada mais é do que a identificação de um artefato.

02-projeto-simples

O Group Id para o exemplo será br.com.starcode e o Artifact Id será teste-maven-01. A versão e o tipo de artefato (Packaging) já devem estar preenchidos, então simplesmente deixe como está. O nome e a descrição são opcionais.

Clique em Finish para ver o projeto criado.

Note que ele ainda não está definido com as configurações de um projeto Java, então clique com o botão direito sobre o projeto, acesse o menu Maven > Update Project....

03-projeto-simples

Clique em OK para atualizar o projeto com as configurações do Maven e agora temos a estrutura característica.

04-projeto-simples

Adicionando manualmente uma dependência

Agora vou ilustrar como podemos adicionar algumas dependências ao projeto. Acesse o site mvnrepository.com, que contém um índice das dependências disponíveis no repositório do Maven. Pesquise por commons-lang.

05-projeto-simples

Selecione o item Apache Commons Lang, como indicado na imagem abaixo:

06-projeto-simples

Clique sobre a última versão (3.3.2 na data em que escrevo o artigo).

07-projeto-simples

Selecione e copie a identificação do artefato, conforme a imagem abaixo:

08-projeto-simples

Agora volte ao Eclipse e clique duas vezes sobre o arquivo pom.xml para editá-lo. Provavelmente o editor foi aberto no aba Overview (veja abaixo do editor) com diversos campos e informações sobre o projeto.

Clique na aba pom.xml para mostrar o código fonte.

09-projeto-simples

Adicione a tag <dependencies> logo abaixo da tag <description> e cole o conteúdo do site dentro dela.

Dica: Pressione CTRL+A para selecionar todo o conteúdo do arquivo e depois CTRL+I para indentar (tabular) o arquivo.

10-projeto-simples

O conteúdo do pom.xml deve ser o seguinte:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>br.com.starcode</groupId>
    <artifactId>teste-maven-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Teste Maven 01</name>
    <description>Um teste de projeto simples com o maven</description>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.3.2</version>
        </dependency>
    </dependencies>
</project>

Salve o arquivo. O plugin M2E irá identificar a alteração, baixar automaticamente a dependência do repositório central para o seu repositório local e adicioná-la ao classpath do projeto.

Confira a entrada Maven Dependencies na imagem a seguir:

11-projeto-simples

Pronto! Agora você já pode usar qualquer classe da biblioteca Apache Commons Lang. 😉

Fiz uma classe de exemplo (File > New > Other..., selecione Class), com o seguinte conteúdo:

package br.com.starcode.testemaven01;

import org.apache.commons.lang3.StringUtils;

public class ClasseDeTeste {

    public static void main(String[] args) {

        System.out.println(StringUtils.capitalize("luiz"));

    }

}

Executei o método main clicando com o botão direito sobre a classe, menu Run As > Java Application. Veja o resultado:

12-projeto-simples

Executando os passos (goals) do Maven

Vamos supor que estamos construindo uma nova biblioteca. Precisaremos testá-la (test), empacotá-la (package) num jar e distribuí-la (deploy) para o uso de terceiros, não é mesmo? O Maven nos ajuda grandemente com esses passos naturais do ciclo de vida de um projeto.

Vamos usar nosso projeto de exemplo e criar uma classe utilitária chamada SuperUtil:

package br.com.starcode.testemaven01;

import org.apache.commons.lang3.StringEscapeUtils;

public class SuperUtil {

    /**
     * Possibilita exibir um texto contendo HTML no navegador sem ataques XSS.
     * @param html Entrada do usuário (pode ter HTML, mas não deve ser renderizado, somente exibido)
     * @return Texto sem posíveis tags HTML
     */
    public static String escapeHTML(String html) {
        return StringEscapeUtils.escapeHtml4(html);
    }

}

Veja no Eclipse:

01-executando-com-maven

Vamos ainda criar um teste unitário para nossa classe, as primeiro temos que adicionar a dependência do JUnit ao nosso projeto. Para isso vá até o site mvnrepository.com e pesquise por junit. Vá até a última versão, copie o trecho do XML e adicione na seção de dependências do seu pom.xml.

Adicione também a tag <scope>test</scope> à esta dependência, para informar ao Maven que ela somente será usada no teste. Sim, o Maven é “esperto” e não incluirá, por exemplo, o JUnit na pasta WEB-INF/lib de uma aplicação web.

Veja como ficou o pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>br.com.starcode</groupId>
    <artifactId>teste-maven-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Teste Maven 01</name>
    <description>Um teste de projeto simples com o maven</description>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.3.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

Ao salvar o arquivo o Maven deve baixar o JUnit automaticamente.

Agora crie a classe de teste SuperUtilTest em src/test/java:

package br.com.starcode.testemaven01;

import org.junit.Assert;
import org.junit.Test;

public class SuperUtilTest {

    @Test
    public void escapeHTMLTest() {

        String escapedHtml = SuperUtil.escapeHTML("<script>alert(1);</script>");
        String expected = "&lt;script&gt;alert(1);&lt;/script&gt;"; 
        Assert.assertEquals(expected, escapedHtml);

    }

}

Caso queira executar o teste já, clique com o botão direito sobre a classe e acesse o menu Run As > JUnit Test:

02-executando-com-maven

Confira o resultado:

03-executando-com-maven

Sucesso! 😀

Imagine agora que tenhamos criado diversas classes e métodos. Temos uma versão beta de nossa biblioteca.

Vamos testar o projeto usando o Maven. Clique no projeto com o botão direito e na opção Run As > Maven test:

04-executando-com-maven

Na primeira execução o Maven vai baixar diversos plugins e dependências internas para a execução do projeto. Aguarde um pouco e confira o resultado de todos os testes do projeto:

05-executando-com-maven

Ok, agora vamos gerar um JAR do projeto. Clique no projeto com o botão direito e na opção Run As > Maven build.... Isso é necessário porque não é uma opção pronta para o passo package. Vá até o campo Goals e digite package.

06-executando-com-maven

Clique em Run e aguarde.

07-executando-com-maven

Se você observar o log no Console notará que os testes foram executados. Lembra que eu disse que os passos anteriores sempre são executados? Espero que agora tenha entendido melhor.

Note a última linha antes das palavras BUILD SUCCESS. Ali está o caminho do Jar gerado. Ele foi gerado dentro da pasta target do projeto.

Selecione a pasta target e Pressione F5 para atualizá-la. Abra-a clicando na seta à esquerda e confira:

08-executando-com-maven

Vamos agora executar o install, isto é, instalar o jar no repositório local. Clique no projeto com o botão direito e na opção Run As > Maven install. Aguarde um pouco e veja o resultado:

09-executando-com-maven

As duas últimas linhas antes da mensagem de sucesso demonstram os locais onde o Jar e o seu arquivo POM foram instalados:

[INFO] Installing D:\starcode\workspaces\workspace_demo\teste-maven-01\target\teste-maven-01-0.0.1-SNAPSHOT.jar to c:\starcode\apache-maven-3.2.1\repo\br\com\starcode\teste-maven-01\0.0.1-SNAPSHOT\teste-maven-01-0.0.1-SNAPSHOT.jar

[INFO] Installing D:\starcode\workspaces\workspace_demo\teste-maven-01\pom.xml to c:\starcode\apache-maven-3.2.1\repo\br\com\starcode\teste-maven-01\0.0.1-SNAPSHOT\teste-maven-01-0.0.1-SNAPSHOT.pom

Vamos abrir o diretório do repositório local e dar uma olhadinha:

10-executando-com-maven

Agora você pode usar este artefato em outros projetos na sua máquina local, adicionando a seguinte dependência:

<dependency>
    <groupId>br.com.starcode</groupId>
    <artifactId>teste-maven-01</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

Qualquer projeto com essa dependência vai usar o nosso jar gerado e, automaticamente, incluir também o jar do Apache Commons Lang que definimos em nosso projeto.

Analisando as dependências

Vamos aprender a analisar as dependências de um projeto.

Abra novamente o seu arquivo pom.xml e vá até a aba Dependency Hierarchy. Você deve estar vendo isso:

01-maven-dependencies

Abaixo, mais um exemplo de dependências extraído da minha biblioteca T-Rex:

maven-dependency-hierarchy

Usando o Maven em linha de comando

Tudo o que fizemos anteriormente através da IDE pode ser feito via linha de comando. É importante entender isso porque quando o projeto for compilado em um servidor de Integração Contínua, por exemplo, ele não contará com as facilidades do plugin que usamos.

Irei ilustrar a seguir a execução do goal install via linha de comando.

O primeiro passo é abrir o CMD e ir até o diretório do projeto. Então basta digitar o comando maven install.

11-executando-com-maven

Configura o resultado da execução:

13-executando-com-maven

Pronto!

Explicando: nós adicionamos o Maven ao path do sistema, lembra? maven é o nome do executável do Maven e install é o goal que você deseja executar. Também há usar o executável mvn, que é apenas um atalho para evitar digitar mais caracteres.

Da mesma forma, poderíamos executar mvn test ou mvn package como fizemos nos tópicos acima via menu do Eclipse para ativar os respectivos Goals.

Um goal muito importante ainda não mencionado é o clean, que serve para limpar todos os arquivos gerados da pasta target. Ele é muito importante para limpar eventual “sujeira” de gerações anteriores.

Quando estiver tendo problemas estranhos ou for gerar uma versão "oficial", sempre use comandos comandos como mvn clean install ou mvn clean deploy para garantir uma geração "limpa".

Note que você pode especificar vários goals simultaneamente para o Maven executar. Nos exemplos acima, o Maven vai primeiro limpar o projeto e depois executar o install ou o deploy.

Passando parâmetros para o Maven

Há ainda situações onde precisamos ajustar a execução do Maven através de parâmetros.

Por exemplo, em certas ocasiões queremos gerar um jar ou war para testes, porém o build do Maven falha porque um teste unitário terminou em erro.

Para resolver essa situação sem excluir o teste unitário, é possível configurar o Maven para “pular” os testes com o seguinte comando:

mvn -DskipTests clean install

14-executando-com-maven

Configura o resultado da execução:

15-executando-com-maven

O mesmo resultado pode ser obtido no Eclipse clicando com o botão direito sobre o projeto e indo no menu Run As > Maven build..., digitando clean install no campo Goals e selecionando a opção Skip Tests.

Distribuindo seu projeto

Os próximos passos incluiriam disponibilizar o jar para outros desenvolvedores através do goal deploy. Em um deploy, o Maven envia seu jar para um Repositório Remoto. Entretanto, isso exige várias configurações adicionais e as devidas permissões.

Disponibilizar seu projeto no repositório central do Maven exige que seu projeto seja opensource e que você solicite as permissões necessárias. Caso você queira fazer isso, siga os passos disponíveis neste link.

Já dentro de uma empresa geralmente se configura um servidor para conter um repositório com os artefatos da empresa. Existem aplicações gratuitas que podem ser usadas para isso, como o Artifactory ou o Nexus.

As configurações necessárias para o deploy num repositório remoto estão fora do escopo deste artigo, mas existem várias referências disponíveis na web.

Leitura adicional

Usar o Maven para desenvolver projetos pessoais é relativamente fácil e este artigo cobre o necessário para isso. Entretanto, não deixe de estudar o material oficial e, aos poucos, ir entendendo os mecanismos do Maven para usá-lo em projetos maiores:

Considerações finais

Usar o Maven pode ser confuso a princípio, mas traz diversos benefícios.

Em curto prazo ele ajuda você a gerenciar as dependências e organizar seus projetos.

Em médio prazo você poderá ter um controle muito bom de versões e releases, além de um padrão maduro para seus projetos.

Em longo prazo o Maven possibilitará a Integração Contínua de seus projetos. Será necessário um esforço considerável, mas seus projetos serão compilados e testados automaticamente. Com uma quantidade de testes adequada para garantir que as funcionalidades existentes continuam funcionando e as novas vão funcionar, você pode ter versões sendo geradas todos os dias ou a cada commit!

Nesse ponto, cruzamos com conceitos de TDD, Agile e outros. Mas é exatamente este o motivo pelo qual ferramentas de automação como o Maven são importantes.

Espero que o leitor tenha compreendido seu funcionamento básico e possa evoluir daqui em diante para um melhor aproveitamento da automação em benefício da qualidade e da agilidade.

Instalando e configurando o Eclipse Kepler no linux Ubuntu

Continuando a série de artigos sobre configuração de um ambiente de desenvolvimento no linux Ubuntu em uma máquina virtual, chegou a hora do Eclipse.

Você deve ter o JDK instalado no seu Ubuntu. Os passos de instalação do Ubuntu e do Java estão em seus respectivos artigos:

Baixando o Eclipse

Acesse a página de download do Eclipse a partir do seu Ubuntu. Vamos fazer o download da versão Eclipse IDE for Java EE Developers, que já vem com um conjunto maior de plugins do que a versão Standard.

Meu Ubuntu é de 64 bits, portanto vou selecionar a opção Linux 64 Bit, conforme indicado na imagem:

install-eclipse-01

Na próxima tela, clique na seta verde para iniciar o download.

install-eclipse-02

Salve o arquivo no local desejado e aguarde o término do download.

install-eclipse-03

Instalando o Eclipse

Ainda no navegador, clique sobre o nome do arquivo para abri-lo, conforme a imagem abaixo. Ou simplesmente abra o gerenciador de arquivos e navegue até o diretório onde baixou o arquivo e abra-o com um duplo clique. O arquivo deve ter sido baixado no diretório ~/Downloads, que é a pasta padrão de downloads do usuário.

install-eclipse-04

Agora, o arquivo deve estar aberto no Archive Manager, conforme a imagem:

install-eclipse-05

Selecione a pasta eclipse que aparece na listagem do programa e clique no botão Extract da barra de ferramentas.

A caixa de diálogo de extração aparecerá. Navegue até um diretório de sua preferência para descompactar o Eclipse. Se estiver em dúvida ou tiver problemas de permissão, use o diretório Home (~), como na imagem.

install-eclipse-06

Clique no botão Extract para confirmar a extração.

Após a conclusão, o Archive Manager mostratá um diálogo. Clique em Show the Files para abrir o diretório Home. Acesse a pasta eclipse com um clique duplo.

install-eclipse-07

Você deve estar vendo os arquivos do Eclipse, incluindo o executável dele.

Ao invés de abri-lo, vamos criar um atalho para facilitar nas demais utilizações. Clique com o botão direito sobre o executável e selecione a opção Make Link.

install-eclipse-08

Um atalho deve ter sido criado como na imagem abaixo:

install-eclipse-09

Com o atalho selecionado, pressione CTRL+X para recortá-lo.

Minimize todas as janelas, clique com o botão direito no fundo da sua Área de Trabalho e selecione a opção Paste.

install-eclipse-10

Pronto!

Configurando o Eclipse

Antes de iniciar o Eclipse, vamos ajustar alguns parâmetro de memória para melhorar o desempenho geral de nossa IDE.

Primeiro, abra o explorador de arquivos e vá até o diretório onde extraiu os arquivos do Eclipse. No nosso caso é ~/eclipse.

install-eclipse-15

Clique com o botão direito sobre o arquivo eclipse.ini e abra com um editor de texto. Você verá o arquivo com o conteúdo a seguir:

install-eclipse-16

Vamos aumentar um pouco os parâmetros de memória:

install-eclipse-17

O conteúdo do arquivo ficou assim:

-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.gtk.linux.x86_64_1.1.200.v20140116-2212
-product
org.eclipse.epp.package.jee.product
--launcher.defaultAction
openFile
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.6
-XX:MaxPermSize=256m
-Xms512m
-Xmx1G

Feche o editor e não esqueça de salvar o arquivo.

Executando o Eclipse

Clique duas vezes sobre o ícone que criamos na área de trabalho. A splash screen irá aparecer.

install-eclipse-11

Aguarde e logo você será saudado pelo Workspace Launcher para selecionar o Workspace de trabalho.

install-eclipse-12

Clique em OK para criar um novo Workspace no diretório indicado. Aguarde mais um pouco para ver a tela de boas-vindas.

install-eclipse-13

Clique no link Workbench para fechar a tela de abertura e ir à perspectiva de desenvolvimento.

install-eclipse-14

Agora é só usar!

Usando o Eclipse

Caso não esteja acostumado com o Eclipse, não deixe de ler o artigo Instalando, Configurando e Usando o Eclipse Kepler.

Embora o tutorial seja para instalação no Windows, na segunda parte você encontra detalhes sobre como realizar configurações básicas, criar um projeto, executar e depurar um programa, além de informações sobre os componentes principais do Eclipse.

Considerações finais

Com Java e o Eclipse instalados no linux, você já tem um ambiente para iniciar o desenvolvimento de aplicações.

Pretendo em artigos futuros usar este ambiente para ilustrar o desenvolvimento de aplicações Big Data com Hadoop.

Instalando e configurando o JDK 7 no linux Ubuntu

Neste pequeno tutorial você aprenderá a instalar o Java Development Kit 7 no linux Ubuntu. O JDK consiste no conjunto de ferramentas para desenvolvimento em Java.

Caso você não tenha acompanhado, publiquei recentemente em um tutorial sobre como instalar o Ubuntu numa máquina virtual. Esta é uma espécie de continuação da série de artigos que visa a configuração de um ambiente de desenvolvimento no Linux. Com esse ambiente, poderemos brincar livremente bom Big Data. Não perca este e os próximos capítulos! 😉

Instalando o JDK 7

Antes de mais nada vamos verificar se, por acaso, já não temos o JDK instalado. Abra o terminal e digite:

javac

install-jdk-02

Note a mensagem de que o programa não foi encontrado. O próprio Ubuntu nos dá uma dica do comando de instalação. Vamos aceitar a sugestão e instalar o pacote openjdk_7-jdk. Digite o comando:

sudo apt-get install openjdk-7-jdk

O terminal irá solicitar a senha. Digite-a.

install-jdk-03

Depois ele vai pedir a confirmação da operação.

install-jdk-04

Pressione Y para aceitar e aguarde o download e a instalação do pacote.

install-jdk-05

Vamos digitar novamente o comando javac para verificar a instalação.

install-jdk-06

Pronto!

Onde está o Java?

Vamos agora conferir onde o Java foi realmente instalado. Para isso, no terminal, digite o comando abaixo para navegar até o diretório onde geralmente ficam as instalações do Java:

cd /usr/lib/jvm

Agora vamos listar os arquivos no diretório com o comando ls:

install-jdk-08

Em nosso exemplo temos duas entradas: java-1.7.0-openjdk-amd64 e java-7-openjdk-amd64.

Ué!? Mas não instalamos apenas uma versão do Java? Sim! Note que cada entrada tem uma cor diferente. O azul mais claro do primerio item indica que ele é um link simbólico (symbolic link), isto é, um simples um atalho. Vamos confirmar com o comando abaixo:

ln -li

install-jdk-09

Notou a seta que demonstra o link apontando para a pasta original?

Enfim, o Java foi instalado no diretório /usr/lib/jvm/java-7-openjdk-amd64, como podemos ver na imagem a seguir:

install-jdk-10

Algumas configurações adicionais

Embora o Java já funcione apenas com a instalação realizada, vamos configurar as variáveis de ambiente para o caso de algum programa procurar o JAVA_HOME.

Vamos novamente verificar se, por acaso, esta variável já existe usando o comando abaixo:

echo $JAVA_HOME

install-jdk-07

Nada. Então vamos defini-la em nossa sessão com o seguinte comando:

JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64

Em seguida, vamos exportar a variável para os demais programas com o comando export:

export JAVA_HOME

Somente como observação, poderíamos ter feito os dois passos anteriores em apenas um comando da seguinte forma:

export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64

Agora vamos testar se a variável está correta:

echo $JAVA_HOME

install-jdk-11

Tudo certo até agora!

O problema é que o comando export não salva a variável de forma permanente, ou seja, se reiniciarmos o computador (ou máquina virtual), perderemos seu valor. A fim de persisti-la e disponibilizá-la para todos os usuários, vamos criar um script que faz a exportação da variável durante a inicialização do ambiente e a torna disponível para todos os usuários do sistema.

Isso é feito com um Shell script no diretório /etc/profile.d. Todos os scripts neste diretório são executados na inicialização.

Primeiro, vamos até o diretório mencionado:

cd /etc/profile.d

install-jdk-12

Para não complicar muito, vamos usar o editor nano para criar um arquivo com permissão de administrador, então execute o seguinte comando:

sudo nano export_vars.sh

No editor, digite a linha abaixo:

export JAVA_HOME=/usr/lib/jvm/java-7-openjdk-amd64

install-jdk-13

Em seguida, pressione CTRL+X para sair do programa (Exit). Ele vai perguntar se você quer salvar o conteúdo. Pressione Y e depois Enter.

Pronto de novo!

O arquivo export_vars.sh foi criado com sucesso. Podemos conferir o conteúdo com o comando cat, da seguinte forma:

cat export_vars.sh

install-jdk-14

Para mais detalhes sobre variáveis de ambiente veja a documentação do Ubuntu.

Considerações finais

Este tutorial tem a intenção de prepará-lo para tópicos mais avançados.

Entretanto, procurei ser detalhado nos comandos utilizados também para que o leitor possa se ambientar no ambiente linux, ao invés de apenas digitar comandos “às cegas” sem compreender o que está fazendo.

O que é Big Data – e por que você deveria estar desesperadamente interessado nisso

Word Cloud "Big Data"

Estima-se que 2,5 quintilhões de bytes de dados são criados todos os dias, criados em sua maioria por usuários na web de forma desestruturada. em diferentes meios (texto, áudio, vídeo, etc.) e fontes (Facebook, Twitter, Youtube, etc.).

O potencial desses dados é gigantesco, mas os bancos de dados relacionais não são capazes de lidar com algo dessa natureza. Qual a solução?

A história dos dados

A forma como os dados são gerados, processados e armazenados mudou drasticamente ao longo das últimas décadas e mesmo em relação aos últimos anos.

Os primeiros cientistas da computação preocupavam-se em criar estruturas de dados otimizadas para armazenar a maior quantidade de informação, da forma mais eficiente possível. Os bancos de dados relacionais ajudaram bastante nessa tarefa.

Porém, o volume de dados que existe no mundo hoje simplesmente não cabe mais nesse modelo. Com a ascensão da Internet disponível, virtualmente, a todo mundo e com a popularização dos sistemas web, incluindo as Redes Sociais, temos hoje um número inimaginável de informação, que aumenta a cada dia.

Agora pare e imagine o potencial desses dados se devidamente analisados. As pesquisas de mercado tradicionais consideram uma população de algumas centenas ou, no máximo, milhares de pessoas para testar alguma tendência.

E se conseguíssemos analisar os dados comportamentais de milhões ou até bilhões de pessoas? A chance de identificar tendências em grupos mais específicos aumentaria enormemente.

O grande problema é que esse volume monstruoso de dados está distribuído em inúmeros locais e em geral não é estruturado para transportarmos tudo para tabelas.

Pense no Twitter. Milhares de pessoas ao redor do mundo postando comentários, críticas, reclamações em forma de um texto com no máximo 140 caracteres. Se pudéssemos analisar sobre o que as pessoas estão mais comentando hoje e se estão falando bem ou mal, teríamos uma enorme vantagem competitiva, não é mesmo? Poderíamos responder “em tempo real” aos consumidores de acordo com o que eles estão querendo.

Texto, som, vídeo, imagem. Há um enorme potencial em tudo isso em busca de uma informação relevante.

O que é Big Data?

Big Data é um termo cunhado por Budhani em 2008 para descrever qualquer conjunto de dados que seja inviável de manipular por uma ferramenta tradicional em um tempo razoável.

Obviamente isso é um tanto vago, mas já existem algumas definições mais específicas.

Os V’s do Big Data

Ao contrário do que o termo pode levar-nos a pensar, Big Data não é apenas sobre o volume de dados. Em tese, um SGBDR tradicional pode tratar quantidades imensas de dados. Big Data envolve outras características relacionadas com velocidade e variedade.

Procurando definir o que é Big Data, alguns pesquisadores chegaram a um conceito de múltiplos atributos. Isso ficou conhecido como os 3 V’s do Big Data: Volume, Velocidade e Variedade.

Entretanto, o amadurecimento da tecnologia deu origem a uma definição mais completa, com 5 V’s: Volume, Velocidade, Variedade, Valor e Veracidade

Confira na imagem abaixo:

Volume

Big Data envolve uma quantidade de dados que começa na casa dos terabytes e chaga até os petabytes. Note que enquanto escrevo esta informação já pode estar desatualizada!

Velocidade

As informações precisam ser transmitidas, processadas e retornadas em tempo hábil para o negócio. Pense na busca do Google, ela precisa “ler a internet” em alguns milissegundos. Ou você usaria o Google se cada pesquisa levasse horas ou dias?

Variedade

Ao contrário dos sistemas de informação mais tradicionais, Big Data gira em torno de uma grande variedade de dados não estruturados e não normalizados.

Muitas informações podem ser obtidas através de processamento de vídeos do youtube, tweets, arquivos de log e páginas de blog.

Eu sei que você já deve estar cansado de eu usar o Google como exemplo, mas farei novamente. Já viu a legenda automática do YouTube? Houve um bom avanço nos algoritmos de análise de áudio, não é? Já pensou em como o Google ordena os resultados da pesquisa, priorizando o conteúdo mais adequado para os termos inputados?

Trabalhar com SEO nada mais é do que tentar influenciar os algoritmos de Big Data do Google!

Valor

Com o amadurecimento das soluções de Big Data, notou-se que as três características já apresentadas não eram razão suficientes para a utilização desta tecnologia.

É necessário que as informações extraídas da massa de dados proporcionem um benefício tangível.

Por exemplo, se uma empresa consegue identificar através de análises estatísticas certas tendências dos consumidores, ela poderá obter uma grande vantagem em relação aos seus concorrentes.

Veracidade

Desde que alguns institutos passaram a usar dados de redes sociais para verificar tendências, surgiu uma nova versão de ataque digital que procura manipular os resultados.

Vou dar um exemplo. Ouvi recentemente que foram criados bots (robôs) para gerar milhares de tweets e postagens na Internet sobre certo candidato a cargo político. O objetivo é fazer parecer que o candidato tem popularidade, sendo “o candidato mais comentado na internet”.

Uma preocupação que tem faltado nas implementações de Big Data é justamente verificar se os dados são oriundos de fontes confiáveis e se os mesmos são autênticos.

Não vou aprofundar-me nesse assunto, mas continuando com o exemplo, uma boa implementação de contagem de tweets poderia eliminar as mensagens repetidas ou muito parecidas. Isso porque spammers são capaz de gerar frases aleatórias com variações de palavras. Portanto a mesma técnica que filtros de spam utilizam deveria ser aplicada nesse caso.

Por quê Big Data?

Até aqui você já deve ter compreendido alguns conceitos básicos sobre Big Data. Porém, ao mesmo tempo, pode estar se perguntando essa tecnologia não teria seu uso limitado a empresas altamente técnicas como o Google.

Em teoria, sobre tudo o que se pode fazer estatística pode se beneficiar de Big Data.

Vejamos agora algumas aplicações reais de grandes e pequenas empresas.

Vendas

O Walmart, por exemplo, adotou Big Data desde muito cedo. Os dados de dez diferentes sites foram consolidados num cluster Hadoop, migrados a partir de bases Oracle e outras fontes de dados.

Além de diminuir o tempo das buscas o Walmart é capaz de verificar se um amigo seu lhe enviou informações sobre um determinado produto em uma rede social e então lhe mandar um e-mail com uma promoção daquele produto.

Ele também pode lhe indicar produtos de acordo com o perfil dos seus amigos no Facebook, de modo que você possa dar um presente adequado.

Telecom

Algumas empresas de Telecom usam Big Data para traçar perfis dos consumidores, unindo padrões de ligação, envio de mensagem e participação em redes sociais. Eles conseguem então descobrir quais consumidores estão mais propensos a trocar de operadora.

Esportes

No campo dos esportes, há várias iniciativas de monitoramento dos atletas. Isso permitirá a análise do desempenho de um time e de cada indivíduo, possibilitará encontrar tendências, prever resultados e até criar jogos mais reais com as características dos times verdadeiros.

Saúde

Na saúde, os pesquisadores poderão prever com mais precisão problemas de saúde baseando-se em históricos hospitalares.

Hoje já existem estudos sobre a coleta de informações de respiração e batimentos cardíacos de bebês nascidos prematuros e o uso de Big Data para identificar padrões de infecção. Isso possibilitará identificar antecipadamente quando um bebê realmente está com infecção antes dos sintomas mais visíveis aparecerem e, consequentemente, aplicar um tratamento mais efetivo.

Trânsito

No Japão, um aplicativo baseado em Big Data está ajudando a melhorar trânsito de uma cidade ao coletar informações de 12 mil táxis e vários sensores.

O serviço consegue analisar 360 milhões de informações sobre o trânsito instantaneamente para retornar a melhor rota naquele horário para o motorista. Com bancos de dados relacionais, o processamento levava vários minutos.

O que realmente é Big Data?

Parafraseando um outro artigo, “no fundo, no fundo, Big Data não se trata de dados nem de tamanho”. Também não se trata de novidades tecnológicas ou descobertas científicas.

Big Data é uma nova forma de ver o mundo, de usar estatísticas e de tomar decisões de negócio.

Uma metáfora seria como a descoberta do microscópio. Uma vez que se consegue enxergar as coisas numa escala completamente diferente de antes, novas descobertas inevitavelmente irão surgir.

A análise criteriosa de um grande volumes de dados é uma tendência que vai continuar e logo se expandirá para mais e mais esferas da vida humana.

Big Data é apenas mais uma modinha?

Algumas pessoas acham que Big Data é apenas mais uma moda tecnológica.

Como toda moda, existe um ciclo onde a princípio todos estão falando sobre aquilo e tem uma expectativa muito alta, depois começam a perceber que o investimento não está trazendo todos os benefícios imaginados, alguns desistem e outros persistem e começam a usar a tecnologia de forma mais adequada e ao final atingem um grau mais real de aproveitamento.

gartner_hype_cycle

Certamente os termos e as tecnologias podem mudar, mas é verdade também que a ideia principal está aí para ficar.

Big Data não é a mesma coisa que Data Warehouse ou Business Intelligence?

Quem conhece um pouco sobre Data Warehouse ou Business Intelligence pode ter encontrado muitos pontos em comum com o que foi apresentado sobre Big Data. Muitos, inclusive argumento que é a mesma coisa. Será?

Por um lado, podemos dizer que a ideia principal é a mesma, a saber, processar um grande volume de dados para ajudar em decisões de negócios e detectar padrões.

Por outro lado, as tecnologias de BI e DW são mais conservadores no que diz respeito às técnicas, tecnologias e estruturas de dados. O foco delas é processar e consolidar os dados estruturados das empresas em uma base de dados somente-leitura separada dos bancos de dados principais dos sistemas a fim de obter estatísticas relevantes.

Já Big Data tem por objetivo unir fontes heterogêneas, privadas e públicas, geralmente em bases NoSQL ou mesmo em arquivos, com dados modificáveis. Big Data também é usado diretamente “em produção”, provendo informações diretamente para os usuários. Os fundamentos também são diferentes, pois Big Data lida especialmente para processamento distribuído, como veremos em breve num artigo sobre MapReduce.

Quais são as tecnologias relacionadas com Big Data?

Não entrarei em detalhes, pois pretendo escrever outros artigos sobre essas tecnologias. Vou simplesmente citar as mais comuns que envolvem o ecossistema do Hadoop, de longe a solução Big Data mais conhecida e também usada como base para outras soluções comerciais.

Hadoop

É uma implementação opensource do framework MapReduce. O Hadoop é um projeto mantido pelo grupo Apache.

Ele é capaz de coordenar tarefas executadas processamento distribuído e paralelo de grandes conjuntos de dados em qualquer quantidade de nós de um cluster.

O Hadoop é implementado em Java, sendo executado numa JVM. Você pode escrever um programa para executar no Hadoop usando a API disponibilizada em jars ou no Maven, inclusive usando sua IDE predileta.

No entanto, Java não é o limite. O Hadoop nada mais é do que uma base de uma pilha de tecnologias, incluindo linguagens de mais alto nível para fins específicos.

HDFS

O Hadoop Distributed File System é um sistema de arquivos distribuído de alta velocidade usado no Hadoop.

YARN

Trata-se de um framework para agendamento de tarefas e gerenciamento do cluster.

HBase

Um banco de dados NoSQL com suporte a dados estruturadas e tabelas grandes.

Hive

O Hive é um tipo de linguagem SQL (HiveQL, para ser mais exato) próprio para realizar consultas em grandes quantidades de dados distribuídos.

Mahout

Este é um projeto que procura unir Inteligência Artificial com Big Data, falando especificamente de Aprendizado de Máquina.

Pig

Pig é uma linguagem de programação de alto nível que facilita na criação de tarefas e análise de dados distribuídos, com execução em paralelo.

Bem, eu sei que você pode ser especialista em “programação porca”, mas não é disso que se trata o Pig. Tá… todo mundo aqui no Brasil já fez essa piada, mas não pude perder a oportunidade! 😉

ZooKeeper

Para manter todos os bichos do Hadoop em suas jaulas é necessário alguém para administrar tudo.

O ZooKeeper é um serviço centralizado para manter a configuração, dar nomes, prover sincronização e agrupamento de serviços.

Mas quem vai operar tudo isso?

Big Data trouxe também um novo tipo de profissional ao palco: o Cientista de Dados.

Uma breve pesquisa sobre esse termo vai trazer muitos resultados, demonstrando que é uma carreira em alta no momento.

Os cientistas de dados devem unir diversas habilidades:

  1. Ser bom em estatística e matemática para analisar corretamente os dados;
  2. Dominar as bases da Ciência da Computação para implementar devidamente as soluções, que geralmente incluem algum tipo de programação e entendimento da arquitetura distribuída; e
  3. Conhecer o negócio da empresa para gerar benefícios tangíveis com os resultados do seu trabalho.

Considerações finais

Sendo uma moda ou não, as empresas estão investindo pesadamente e obtendo resultados concretos com Big Data. Quanto antes uma empresa obtiver os benefícios em potencial de uma solução Big Data, mais ela terá chances de aumentar sua participação no mercado.

Os profissionais que mais cedo dominarem essa tecnologia também terão mais chances de destacar-se nesse nicho de mercado.

Como em toda mudança, os que chegarem por último terão que se contentar com as sobras.

A maior consideração quanto a isso é não entrar no frenesi de implementar Big Data por um fim em si mesmo, nem criar expectativas irreais sobre as tecnologias.

Torna-se necessário então compreender o cenário atual para avaliar cautelosamente o caminho adequado para um investimento em Big Data.

Além disso, voltando ao exemplo do microscópio, a nova visão de mundo proporcionada pela análise de enormes massas de dados deve ser suplementar e não substituta da visão da realidade dos negócios e da realidade humana.

Apache POI: adicionando segurança em arquivos Excel

poi-original
A biblioteca Apache POI possibilita a leitura e gravação de documentos do Microsoft Office via código Java.

Por esses dias, fiz uma pesquisa sobre como essa biblioteca viabiliza (ou não) trabalharmos com arquivos protegidos do Excel.

Níveis de segurança do Office

Documentos do Excel podem ter diferentes níveis de proteção, a saber:

  • Proteção contra alteração: permite abrir o documento em modo somente-leitura, mas não deixa o usuário alterá-lo sem antes digitar a senha.
  • Proteção contra alteração de um trecho do documento: protege planilhas e células específicas com senha.
  • Proteção contra leitura: não permite abrir o documento sem a senha.

Todos esses modos podem ser usados ao mesmo tempo, inclusive com senhas diferentes.

Se o arquivo tiver proteção contra leitura, o Excel irá solicitar a senha antes de abrir o mesmo. Se houver proteção contra alteração ele irá solicitar a senha para desbloqueio da edição ou permitir acesso somente-leitura ao documento. Se planilhas ou células estiverem protegidas contra alteração, então o usuário deve acessar a função “Desproteger” no Excel e digitar a senha de desbloqueio para cada trecho.

As proteções contra alteração são simples travas no editor, pois o arquivo em si permanece aberto e não há restrições de alteração via programação ou com o uso de editores de terceiros. Podemos dizer que este mecanismo oferece um baixíssimo grau de segurança, que impedirá apenas o usuário mais leigo de efetivamente modificar o documento.

Por outro lado, a proteção contra leitura oferece um grau de segurança maior, pois o documento como um todo é criptografado. Isso significa que nenhum programa ou editor conseguirá sequer ler o documento sem antes descriptografar os bytes do arquivo com a senha original.

Versões dos documentos do Office

Quem acompanha o Office há algum tempo deve ter notado que, a partir da versão 2007, os documentos ganharam novas extensões, com o sufixo x. Por exemplo, doc passou a ser docx, xls mudou para xlsx e assim por diante.

Mas não foi apenas a extensão que mudou. O conteúdo foi completamente reformulado. O formato mais antigo era do tipo binário, enquanto os novos são baseados em XML.

As vantagens do formato XML são inúmeras, a começar pela capacidade de qualquer ferramenta que trabalha com XML conseguir processar o documento ou pelo menos parte dele.

No que se refere à biblioteca Apache POI, isso também nos afeta diretamente, pois há APIs diferentes para trabalhar com os diferentes formatos de documentos.

Como veremos adiante, nem todos os formatos suportam todos os tipos de segurança.

Capacidades do POI

A conclusão dos meus testes foi a seguinte:

Tipo de documento Operação Proteção contra alteração do documento Proteção contra alteração de uma planilha Proteção contra Leitura (criptografia)
XLS
Leitura OK
Criação N/D* OK N/D
XLSX
Leitura OK
Criação N/D OK OK

Na leitura de um arquivo XLS ou XLSX, as proteções contra alteração são ignoradas, então não há o que testar.
N/D = Não disponível, isto é, não há suporte na API.
* Existe um método para proteger o documento, mas ele não surte efeito (bug).

Como pode ser visto, para documentos no formato legado XLS o POI suporta apenas a leitura de arquivos criptografados. Na criação, ele apenas consegue proteger parte do documento contra alteração.

Já para o formato mais novo XLSX, o POI consegue ler e criar arquivos criptografados. Entretanto, na criação do documento, não há um método para proteger o documento todo contra alteração.

Alguns fóruns sugerem uma possibilidade de contornar a falta do recurso de proteger o documento como um todo contra alteração. Para isso, basta criar um arquivo Excel com tal proteção e usá-lo como template para geração de um novo.

Implementação

Criptografia em arquivos XLS

O POI suporta apenas ler arquivos criptografados neste formato, isto é, que estão protegidos contra leitura.

O segredo é a classe Biff8EncryptionKey. Basta definir a senha através do método estático setCurrentUserPassword e depois ler o arquivo normalmente.

Exemplo:

try {
    Biff8EncryptionKey.setCurrentUserPassword(password);
    new HSSFWorkbook(input);
} finally {
    Biff8EncryptionKey.setCurrentUserPassword(null);
}

É bom não esquecer de remover a senha ao final para não atrapalhar futuras leituras.

Mesmo sendo um método estático, não deve haver problemas de concorrências, pois a documentação afirma que a senha é armazenada usando ThreadLocal.

Criptografia em arquivos XLSX

O POI implementa tanto a leitura quanto a criação de documentos criptografados neste formato.

Veja minha implementação para a leitura:

public class XlsxDecryptor {

    public static XSSFWorkbook decrypt(InputStream input, String password)
            throws IOException {

        POIFSFileSystem filesystem = new POIFSFileSystem(input);
        EncryptionInfo info = new EncryptionInfo(filesystem);
        Decryptor d = Decryptor.getInstance(info);

        try {
            if (!d.verifyPassword(password)) {
                throw new RuntimeException("Unable to process: document is encrypted");
            }

            InputStream dataStream = d.getDataStream(filesystem);
            return new XSSFWorkbook(dataStream);
        } catch (GeneralSecurityException ex) {
            throw new RuntimeException("Unable to process encrypted document",
                ex);
        }

    }

    private XlsxDecryptor() {
    }

}

E para criação:

public class XlsxEncryptor {

    public static void encrypt(InputStream input, OutputStream output, 
            String password) throws IOException {

        try {
            POIFSFileSystem fs = new POIFSFileSystem();
            EncryptionInfo info = new EncryptionInfo(fs, EncryptionMode.agile);

            Encryptor enc = info.getEncryptor();
            enc.confirmPassword(password);

            OPCPackage opc = OPCPackage.open(input);
            OutputStream os = enc.getDataStream(fs);
            opc.save(os);
            opc.close();

            fs.writeFilesystem(output);
            output.close();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        } catch (InvalidFormatException e) {
            throw new RuntimeException(e);
        }

    }

}

Proteção de partes de um documento

O POI implementa a proteção de uma planilha de um documento Excel e a proteção de células. Testei apenas a proteção de planilhas.

A implementação é muito simples, basta usar o método protectSheet. Veja o exemplo para o formato XLSX:

XSSFSheet sheet = workbook.createSheet();
sheet.protectSheet("54321");

E agora o equivalente para o formato XLS:

HSSFSheet sheet = workbook.createSheet();
sheet.protectSheet("54321");

Testes e exemplo

Antes de encerrar o artigo, vou acrescentar aqui dois dos testes unitários que implementei de forma que possam servir como exemplo de uso do POI e do meu projeto.

O primeiro cria um novo arquivo XLSX cripgrafado e em seguida lê o mesmo arquivo verificando se o valor foi gravado corretamente:

@Test
public void createXLSXOpenAndModifyProtected() throws IOException {
    System.out.println("createXLSXOpenAndModifyProtected");

    //creates sheet
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet();
    sheet.protectSheet("54321");

    XSSFRow row = sheet.createRow(0);
    XSSFCell cell = row.createCell(0);
    cell.setCellValue("Gravado");

    //saves sheet
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    workbook.write(bos);
    bos.close();
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

    new File("target/.output-file/xlsx").mkdirs();
    WorksheetEncryptor.encrypt(
        bis, 
        new FileOutputStream("target/.output-file/xlsx/OpenAndModifyProtected.xlsx"), 
        DocumentType.XML, 
        "54321");
    bis.close();

    //read again and check
    XSSFWorkbook workbook2 = (XSSFWorkbook) WorksheetDecryptor.decrypt(
        new FileInputStream("target/.output-file/xlsx/OpenProtected.xlsx"),
        DocumentType.XML, 
        "54321");
    Assert.assertEquals("Gravado", workbook2.getSheetAt(0).getRow(0).getCell(0).getStringCellValue());
}

Em seguida, o segundo lê um arquivo XLS criptografado e verifica o valor da célula:

@Test
public void testOpenAndModifyProtectedXLS() throws IOException {
    System.out.println("### testOpenAndModifyProtectedXLS");
    HSSFWorkbook workbook = (HSSFWorkbook) WorksheetDecryptor.decrypt(
        getClass().getResourceAsStream("xls/OpenAndModifyProtected.xls"),
        DocumentType.LEGACY, 
        "12345");
    String conteudoCelula = workbook.getSheetAt(0).getRow(0).getCell(0)
        .getStringCellValue();
    Assert.assertEquals("Teste criptografia", conteudoCelula);
}

Código-fonte

Todo o código, inclusive os testes, está disponível em um projeto no meu GitHub.

Com o Maven você será capaz de executá-lo sem nenhuma configuração adicional. Basta clonar o repositório.

Instalando e configurando o Ubuntu linux numa máquina virtual

virtualbox-ubuntu

Neste tutorial, vamos instalar o Ubuntu, uma das distribuições linux mais populares da atualidade, numa máquina virtual.

Você poderá usar isso para várias finalidades. Aqui no blog, em breve, usaremos em artigos sobre Hadoop e Big Data.

Virtualização

Para quem não está acostumado com virtualização, uma máquina virtual (Virtual Machine, em Inglês) é um ambiente que simula um computador, com sistema operacional próprio, mas que você pode executar dentro do seu sistema atual.

Isso significa que você pode executar um sistema operacional linux dentro do seu Windows e vice-versa. Eu mesmo uso primariamente o Windows 7, mas tenho imagens com XP e diversas distribuições linux.

O sistema operacional principal da máquina é chamado de hospedeiro (host). Os sistemas operacionais usados dentro de máquinas virtuais no sistema hospedeiro são chamadas de sistemas convidados (guests).

Essa técnica tornou-se viável num passado não tão distante quando o hardware atingiu um bom nível de eficiência, inclusive hoje com tecnologia que torna a virtualização quase tão eficiente como um sistema tradicional.

A virtualização traz vários benefícios. O principal é possibilitar a criação da tão famigerada computação em nuvem (cloud computing). Além disso, as empresas que dependem de infraestrutura de TI tanto para desenvolvimento quanto para produção podem usufruir de maior facilidade para a criação de novos ambientes e servidores virtuais, além de flexibilidade para o gerenciamento. Desenvolvedores ou mesmo usuários domésticos como eu podem ter vários servidores com diferentes tecnologias em seu notebook pessoal, inicializados apenas de acordo com a demanda.

Existem ainda sites que disponibilizam ambientes com diversas tecnologias prontos para os administradores usarem em servidores. Um deles é o TurnKey Linux. Baixando imagens de discos virtuais relativamente pequenas, você tem um sistema pronto para uso e somente com o que é necessário para executar a tecnologia escolhida. Enfim, você pode ter um servidor pronto em uma máquina virtual em apenas alguns minutos.

VirtualBox ou VMWare Player?

Os programas gratuitos de virtualização para usuários domésticos mais conhecidos são o VirtualBox da Oracle e o VMWare Player. Ambos são bons produtos, maduros e em constante evolução. Mas com funcionalidades específicas um pouco diferentes, além de vantagens e desvantagens.

Como sou usuário de ambos posso dizer que na prática não há um ganhador absoluto. Depende do uso que fizermos deles. O VMWare, por exemplo, permite copiar e copiar um arquivo do sistema hospedeiro para o convidado e vice-versa. O VirtualBox, por sua vez, traz várias funcionalidades que o VMWare só disponibiliza na versão paga.

Para quem faz questão de uma solução mais completa e possui condições de arcar com as despesas, o melhor seria adquirir uma versão paga do VMWare. Já o usuário doméstico que está começando se dará muito bem com qualquer versão gratuita.

Aqui usaremos o VirtualBox. Mas se alguém optar pelo concorrente não encontrará tanta dificuldade em atingir o mesmo objetivo.

Funcionalidades interessantes do VirtualBox

Existem algumas funcionalidades bem legais quando usamos uma máquina virtual. Irei descrever algumas nas próximas linhas que estão disponíveis no VirtualBox.

Por exemplo, você pode pausar uma máquina virtual a qualquer momento através do menu Máquina > Pausar.

Também é possível salvar snapshots da máquina através do menu Máquina > Criar Snapshot. Sabe o que significa isso? Ao criar um snapshot, você tira uma “fotografia” ou “instantâneo” do sistema naquele momento. Então pode “pintar e bordar”, realizar testes, instalação de programas ou até vírus. Quando cansar da brincadeira, basta restaurar o snapshot e o sistema (disco e memória) voltarão ao estado salvo como se nada tivesse acontecido.

Caso em algum momento você deixe a máquina virtual em tela cheia ou o cursor do mouse seja capturado por ela de forma que você não consiga sair, não se desespere. A tela usada para liberar o mouse e também para algumas teclas de atalho é o CTRL da direita do seu teclado. Este é o padrão e você pode mudá-lo. Essa tecla especial é chamada tecla do hospedeiro, isto é, que permite acessar comandos no sistema hospedeiro. Por exemplo, CTRL+F alterna a máquina virtual entre modo de tela cheia e janela.

Outra funcionalidade interessante, embora deva ser usada com cuidado, é o modo Seamless. Com ele, os programas abertos no sistema dentro da máquina virtual “misturam-se” com a área de trabalho do sistema hospedeiro, dando a impressão de haver apenas um sistema operacional. Veja o seguinte exemplo de um terminal aberto no Ubuntu e exibido em seamless mode:

seamless-mode

Configuração de Hardware

Máquinas mais novas, como o Intel i7, possuem suporte em nível de hardware para virtualização. Entretanto, até algum tempo atrás essas capacidades eram desativadas por padrão. Isso chegava a impedir a virtualização de sistemas operacionais convidados de 64 bits.

Leia o manual da sua placa mãe e do seu processador e verifique se eles possuem suporte nativo para virtualização. Procure por algo como VT-x (Intel) ou AMD-V. Veja um exemplo da BIOS para um processador AMD:

5x3YW

E aqui outro exemplo para um processador Intel:

bios_enablt_vtx1

Lembre-se, sem o suporte nativo, você não será capaz de instalar um sistema operacional de 64 bits como convidado no VirtualBox. Entretanto, se não estou enganado, o VMWare consegue emular via software a virtualização de sistemas 64 bits, mas de qualquer forma o desempenho será sofrível.

Instalando o VirtualBox e as extensões

Acesse a página de downloads e baixe a versão correspondente ao seu sistema operacional.

dowload-virtualbox

Baixe também as extensões para o sistema convidado.

dowload-virtualbox-extension

As extensões trarão várias facilidades, tais como: redimensionamento automático da tela, melhor integração do mouse, compartilhamento de pastas automático entre o sistema hospedeiro e o convidado, uso da USB dentro da máquina virtual e muito mais.

Execute o primeiro arquivo baixado para instalar o VirtualBox. Em geral você não precisa alterar nenhuma configuração, então simplesmente avance até o final da instalação

virtualbox-install

Confirme ainda a instalação de todos os drivers, que serão usados para integrar seus dispositivos como mouse, teclado e rede com a máquina virtual.

Após concluir, execute também o outro arquivo para instalar as extensões do convidado (Guest Additions). O nome deve ser algo como Oracle_VM_VirtualBox_Extension_Pack-4.3.12-93733.vbox-extpack. O programa VirtualBox será aberto. Aceite o contrato para concluir a instalação.

Dando tudo certo, não se esqueça que o atalho adicionado no Menu Iniciar é “Oracle Virtual Box”.

virtualbox-open

Criando uma máquina virtual

Na tela principal do VirtualBox, clique no botão Novo.

virtualbox-main

Na tela de criação, digite “Ubuntu 14”. Note que os demais campos serão preenchidos automaticamente.

configure-vm-01

Clique em Próximo e selecione a quantidade de memória para seu novo ambiente. Aqui vou deixar com 2 Gigabytes (2048 Megabytes), mas uma dica é não ultrapassar 50% da memória total do seu computador.

configure-vm-02

Clique em Próximo. Nesta tela, você poderá criar um novo disco rígido virtual. Um HD virtual é simplesmente um arquivo grande que ficará no seu sistema de arquivos, o qual funcionará como se fosse um HD para o sistema da máquina virtual. A não ser que tenha outros planos, deixe marcada a opção para criar um disco novo.

configure-vm-03

Clique em Criar. Na próxima tela, você poderá escolher o formato do arquivo desse novo disco. Vamos deixar o formato nativo do VirtualBox, o VDI.

configure-vm-04

Clique em Próximo. Nesta tela você pode escolher entre duas opções:

  1. Dinamicamente alocado: nesta opção, o arquivo do disco virtual vai aumentando de tamanho somente quando novos arquivos forem gravados. Isso significa que se você criar um disco de 30 Gigabytes, mas a instalação do SO e os demais arquivos ocuparem apenas 2 Gigabytes, então o arquivo terá apenas 2 Gigabytes. O disco vai aumentando de tamanho na medida do uso até alcançar o limite de 30 Gigabytes.
  2. Tamanho fixo: nesta opção, um disco virtual de 30 Gigabytes vai ocupar todo esse tamanho no seu disco verdadeiro.

Já que economizar espaço nunca é demais, vamos deixar a primeira opção selecionada.

configure-vm-05

Clique em Próximo. Agora vamos selecionar o nome do arquivo e o tamanho do disco virtual.

configure-vm-06

Caso tenha mais de uma partição ou HD no seu computador, você pode mudar o local do arquivo do disco virtual. Em algumas situações já criei máquinas virtuais no meu HD externo. Porém, para este tutorial, vamos apenas deixar tudo como está, pois o padrão é suficiente.

Finalmente, clique em Criar.

Agora você tem um computador virtual para brincar!

virtualbox-virtualmachine-created

Instalando o Ubuntu

Antes de mais nada, acesse a página de downloads da versão desktop do Ubuntu e baixe a versão adequada para o seu computador. Neste tutorial, fiz o download da versão 64 bits, cujo nome do arquivo baixado é ubuntu-14.04-desktop-amd64.iso e possui 964 Megabytes.

ubuntu-download

Com a imagem do disco de instalação do nosso novo sistema operacional, podemos então iniciar a máquina virtual e a instalação.

Na tela principal, selecione a VM (máquina virtual) criada e clique em Iniciar.

Antes da inicialização da VM, o VirtualBox vai saudá-lo com uma tela solicitando o disco de boot. Isso ocorre porque ele verificou que o disco virtual está vazio.

Clique no botão à direita do campo e selecione o arquivo do Ubuntu anteriormente baixado.

vm-boot-disk-select

Clique em Iniciar e aguarde a inicialização da instalação do Ubuntu.

ubuntu-install-01

Você pode selecionar sua língua materna ou deixar em Inglês. Eu prefiro o Inglês porque em TI as traduções acabam por confundir mais que ajudar. Clique em Install Ubuntu ou Instalar Ubuntu, dependendo da sua escolha.

A próxima tela irá informar se o Ubuntu vai executar bem na máquina onde está sendo instalada. Além disso, há opções para já instalar as últimas atualizações e alguns softwares de terceiros. Selecione todas as opções e clique em Continue.

ubuntu-install-02

Agora há opções para formatar ou particionar o disco antes da instalação. Como temos um disco virtual dedicado, simplesmente selecione a primeira opção para formatá-lo e executar uma instalação limpa.

ubuntu-install-03

Clique em Install Now.

Na verdade, a instalação não vai começar ainda. Isso deve ter sido uma grande falha de design. A próxima tela contém a seleção da sua localidade. Digite o nome da capital do seu estado. Coloquei “Sao Paulo”.

ubuntu-install-04

Clique em Continue.

Na próxima tela você pode selecionar o tipo do seu teclado. Teste-o para ver se está ok e clique novamente em Continue.

ubuntu-install-05

Finalmente, digite seus dados de usuário, incluindo a senha, e clique em Continue para iniciar a instalação de verdade.

ubuntu-install-06

Aguarde o processo de instalação.

ubuntu-install-07

Ao final, uma caixa de diálogo vai aparecer informando que o sistema deve ser reiniciado. Clique em Restart Now.

Nota 1: enquanto fazia este tutorial, o Ubuntu travou e não reiniciou corretamente. Então, fui até o menu Máquina > Reinicializar para forçar um reset.

Nota 2: a instalação do Ubuntu ejetou automaticamente o disco de instalação virtual do Ubuntu. Se estiver instalando outro sistema operacional que não faça isso, use o menu Dispositivos > Dispositivos de CD/DVD > Remover disco do drive virtual para não iniciar a instalação do sistema novamente por engano.

Pronto, o sistema está instalado e pronto para uso.

ubuntu-installed

Melhorando a integração entre sistema hospedeiro e convidado

Note que a janela do ubuntu ficou bem pequena, quase inutilizável. Vamos resolver isso!

Lembra que instalamos as “extensões do convidado” (Guest Additions) no VirtualBox? Elas facilitarão o uso da máquina virtual de várias formas, mas falta a parte da instalação no sistema convidado. Isso ocorre para que o VirtualBox consiga “conversar” com o SO que está na máquina virtual.

Para fazer isso, devemos seguir as instruções da documentação do VirtualBox que nos dá alguns comandos.

Vamos abrir o terminal de comandos clicando no primeiro botão à esquerda (equivalente ao “Iniciar” do Windows) e pesquisando na caixa de busca por “terminal”.

search-terminal

Se nada mudou no VirtualBox ou no linux desde que escrevi este tutorial, as instruções do Guest Additions para o Ubuntu consistem nos seguintes comandos:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install dkms

Nota: um usuário comentou que não conseguiu executar o último comando com sucesso, tendo substituído por sudo apt-get install virtualbox-guest-dkms. Isso pode ser necessário se estiverem sendo usadas diferentes configurações ou outras versões do Ubuntu ou ainda outras distribuições linux.

O comando sudo que prefixa os demais não está na documentação, mas é necessário se você não está executando o terminal com privilégios de superusuário (administrador).

O primeiro comando é apt-get update. Ele irá atualizar o índice de pacotes do Ubuntu. Dessa forma ele saberá as últimas versões de todos os seus componentes e programas. Após digitar o comando, o sistema irá solicitar a senha do usuário e então executar a ação.

guest-additions-01

O próximo comando é apt-get upgrade. Ele vai efetivamente instalar todas as atualizações do sistema. Após entrar o comando, o Ubuntu vai solicitar algumas confirmações. Pressione Y (yes) para confirmar a atualização e aguarde.

guest-additions-02

Após a atualização do sistema, executaremos o último comando: apt-get install dkms. Este comando vai instalar o pacote dkms, que possibilita a módulos do kernel serem atualizados independentemente. O Guest Additions precisa disso porque ele é um módulo do Kernel e é atualizado com frequência, caso contrário seria necessário recompilar o Kernel do linux a cada atualização.

guest-additions-03

O comando vai pedir a confirmação da instalação. Pressione Y quando necessário.

Neste momento já cumprimos todos os pré-requisitos para a instalação do Guest Additions. Então vamos à instalação em si.

Acesse o menu Dispositivos > Inserir imagem de CD dos Adicionais para Convidado....

guest-additions-04

Ao acionar o menu, uma imagem de CD do VirtualBox será montada no sistema do Ubuntu e a execução automática (auto run) ocorrerá. Uma mensagem de confirmação será exibida.

guest-additions-05

Clique em Run. A senha será novamente solicitada. Digite-a e aguarde o final da instalação.

guest-additions-06

Finalmente, vamos reiniciar o sistema para ativar o módulo que acabamos de instalar. Clique no botão do sistema no canto superior direito do Ubuntu e selecione a opção Shut Down....

guest-additions-07

Na tela que vai abrir, clique no botão da esquerda para reiniciar.

Após a reinicialização, você poderá, entre outras coisas, redimensionar a janela do VirtualBox como quiser e o Ubuntu irá se ajustar a esse tamanho. Legal, né? Esta é a opção Visualizar > Redimensionar Tela Automaticamente que estava desabilitada anteriormente, mas agora veio ativada por padrão.

Palavras finais

Virtualização é um conceito importantíssimo no mundo de hoje. Desenvolvedores de software não precisam ser especialistas em virtualização, mas devem ter bons conceitos sobre como isso funciona e devem saber usar todos os benefícios a seu favor.

Criar máquinas virtuais não é difícil, basta ter uma base sobre o assunto e saber usar as ferramenta já existentes, que estão cada vez mais intuitivas e poderosas.

Os benefícios da criação de máquinas virtuais são inúmeros, a começar por podermos usufruir de uma variedade de ambientes dentro de um único computador.

Em futuros artigos, pretendo trazer tutoriais envolvendo Hadoop, inclusive com a criação de um cluster, cada um em uma máquina virtual, para processamento Big Data.

Dissecando o padrão de projetos Singleton

singleton O padrão de projetos Singleton consiste em uma forma de garantirmos que teremos uma única instância de uma determinada classe no programa atual.

Por exemplo, em um programa Java para desktop podemos criar um Singleton da classe que gerencia a conexão com o banco de dados.

Este é um dos design patterns mais simples que existem, mas ele possui algumas nuances importantes de se entender do ponto de vista de implementação.

Implementação inicial em Java

Para garantirmos uma única instância de uma classe, a abordagem mais comum é criar um método estático que retorne sempre o mesmo objeto. Exemplo:

public class Singlegon {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
    private Singleton() { }
}

O atributo estático instance armazena o objeto criado para retornar a cada chamada de getInstance().

O trecho private Singleton() { }; é um construtor privado, garantindo que nenhuma outra classe poderá criar inadvertidamente uma instância desta.

Postergando a criação do objeto

Tudo ok, mas nem sempre queremos criar o objeto no modo agressivo (eager), isto é, instanciá-lo logo que a classe é carregada. Em muitas situações, é desejável postergar a criação do objeto até a primeira chamada. Exemplo:

public class Singlegon {
    private static Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

No código acima, o primeiro acesso ao método getInstance() irá disparar a criação do objeto, que será então retornada nas demais chamadas.

Problemas de sincronizção

O problema do código acima é que se houver mais de uma chamada concorrente no primeiro acesso ao método getInstance() ele pode criar duas instâncias de Teste. Duas threads poderiam entrar dentro do if, certo?

A solução mais básica para isso é sincronizar o método:

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

O problema desta abordagem é que todas as chamadas estarão sujeitas a bloqueios, deixando a execução geral do programa mais lenta. Imagine um método assim num servidor de aplicação com vários usuários acessando o sistema! É terrível.

Uma solução melhor seria um bloco synchronized dentro do if, assim:

public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            instance = new Singleton();
        }
    }
    return instance;
}

Isso resolve o problema da sincronização em todos os acessos, mas é uma solução “ingênua”, pois na verdade voltamos ao problema inicial. Como o if não está sincronizado, duas threads diferentes podem entrar no bloco de criação ao mesmo tempo e, mesmo com a sincronização, elas retornarão instâncias diferentes quando instance == null.

Então, a solução mais “pura” para o singleton pattern seria acrescentar uma verificação dupla, assim:

public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        }
    }
    return instance;
}

Com esta última abordagem garantimos que não haverá perda de desempenho por causa de sincronização desnecessária do método inteiro.

Além disso, garantimos uma única instância de Teste, pois mesmo que duas chamadas concorrentes entrem dentro do primeiro if, temos uma nova verificação sincronizada.

No pior caso, se houver duas ou mais chamadas concorrentes no primeiro acesso a getInstance (quando INSTANCE ainda é null), apenas estas primeiras chamadas serão sincronizadas, sendo que após a primeira atribuição de INSTANCE, nenhuma chamada posterior será sincronizada.

Além do Singleton: padrão Registry

Alguns argumentam que o padrão Singleton está depreciado e deve ser abandonado. De certa forma eu concordo, pois em ambientes onde múltiplas threads e múltiplas aplicações executam concorrentemente, ter apenas um objeto quase nunca é desejável.

O padrão de projeto Registry permite armazenarmos uma coleção de objetos, cada um contendo um identificador ou um escopo específicos. É como se limitássemos o escopo do Singleton de “um objeto por programa” para “um objeto por qualquer escopo que quisermos“.

A implementação varia muito, mas podemos encontrar exemplos claros desse padrão em:

  • Threadlocal, que permite armazenar valores para uma thread, ou seja, um Singleton para cada uma.
  • HttpSession, que retorna sempre o mesmo objeto para cada usuário do sistema web, ou seja, um Singleton por usuário.
  • Frameworks de Injeção de Dependências como Spring ou CDI, os quais gerenciam a criação de objetos em diferentes escopos e permitem inclusive usar o padrão Singleton declarativamente.

Não entrarei em detalhes sobre o Registry neste artigo.

Indicações de Leitura

Uma leitura mais completa sobre o assunto está no Head First Design Patterns (Use a Cabeça! Padrões de Projeto). Embora tendo suas falhas, este é um livro muito bom para quem ainda está começando a entender Padrões de Projetos.

Outros detalhes interessantes sobre Singleton, como variações de implementação, podem ser encontrados na Wikipédia (em Inglês).

Uma breve definição do padrão Registry pode se encontra no catálogo de padrões do Martin Fowler.

Is TDD dead?

Test Driven Development is a very popular software development methodology focused (guest what?) on tests.

However, despite of being well known, there’s some criticism on this methodology from prominent professionals and thinkers.

A few days ago, there was a very interesting talk about this issue between Martin Martin Fowler (that elaborated the Dependency Injection definition), Kent Beck (creator of TDD and JUnit framework), and David Heinemeier Hansson (creator of Ruby on Rails).

Let’s review some highlights from that talk in the next topics. But let’s start with a brief review of TDD…

How TDD works

Different from the “traditional” development life-cycle composed by Specification, Implementation and Test, TDD begins testing the interfaces of the system.

At first, all tests will fail. The developer’s goal is to make they succeed. In theory it guarantee the focus on what really should be done and provides a concrete measure of progress.

In general, TDD adopts Unit Tests. Both are different things, but closely related.

Unit Tests

Each Unit Test tests only one function or method of the system. Often they are automated.

It seems too simple, but implies that the routine under test can’t depend on other routines, otherwise the test is subjected to third part failures.

This way, in order to test routines that naturally depend on other classes and methods, resources like databases and files, and other stuff, developers need to create simulations of these elements so the routine works without them during the test.

This is the mock idea, i.e., something that mimics the original dependencies.

For instance, a method that would normally return a value from database could be replaced by a method that returns a fixed value for a particular test. Each language, framework, and tool should provide proper ways for doing that. This is also a manner to assess the quality of these tools!

Benefits of TDD

Writing tests in advance brings various advantages:

  • Helps in the understanding of the requirements, since you have to understand them and also what the system should do in order to write down tests.
  • Provides a clear goal in the development, that is, to make all tests to pass.
  • Increases the visibility of what is or not ready, since the tests are generally more reliable indicators of progress than something “implemented, but not tested”.
  • The automation enables Continuous Integration with some level of guarantee the system won’t break, since all unit tests can be executed over and over as regression tests.

Disadvantages

As a general rule, everything has its advantages and also its negative points. We always have to take care with “evangelists” that will make some technology or methodology looks like it hasn’t problems or has zero impact.

This also applies to TDD. Let’s see some of its reservations:

Increases the effort

It’s necessary to spend much more time creating tests and mocks, not to mention the Continuous Integration infrastructure. Of course it can be considered as an investment in quality instead of a penalty, but in practice not everyone can afford the overhead.

Increases the complexity

The architecture becomes more complex since Dependency Injection and other decoupling techniques are abundantly used everywhere.

Changes in requirements are much more costly, since you need to rewrite the code of the system itself plus all the tests and mocks involved.

If the engineer don’t know well what he’s doing, the system will end with tons of kludges.

Intrusiveness in the main code

In some situations, you’ll have to code in a specific way in order the code can be tested later. The application architecture is then affected by outside unnatural factors.

In short, the design is influenced by tests, what is not desirable since is one more thing to be considered and to grab the architect’s attention.

Testing everything is almost impossible

Many technologies are hard to test independently or even in integration tests.

Think about user interfaces, for example. How many technologies allow us to run it in a decoupled way? There’s tools like Selenium for web apps, but they also brings tons of other limitations and dependencies.

Is TDD dead?

We just saw every benefit comes at a cost. This is the main point about TDD being dead. Is the cost of testing everything and creating mocks for every dependency worthy? Let’s look at some comments about the aforementioned talk.

David Hansson starts arguing that lots of people can’t do TDD because of the nature of their work. He talks about the need of numerous mocks and how it makes coding more difficult.

Kent Beck continues and tell us about situations where the team couldn’t refactor their code because the tests were so coupled to the implementation, with two or three levels of mocks, that any modification affected countless of them. He also asserted TDD fits better certain scenarios, for instance, when requirements are clear enough so you can write them in form of tests directly. On the other hand, there are situations when the developer finds out little by little what he actually should do. This is recurrent in framework development and algorithms for analyzing non-structured data.

Fowler argues the major benefit of TDD is a self-testing code. But this could be achieved in other ways. He also talks about those who say “you aren’t doing unit tests right” because of some dependencies during the test. According to him, the “purity” of the definition is not so important, but the test should work and be useful. Furthermore, there’s alternative definitions of Unit Test.

Conclusions

It’s important to comprehend TDD is not the silver bullet of software development. It brings various potential challenges and problems, mainly if there’s not enough experienced people in the team.

Tests are essential, but for each project we could have different kinds of tests, whose cost-benefit should be evaluated for each individual case.

Tests shouldn’t be the goal in itself, even in TDD. Their true goal is guarantee we’re delivering what the client really needs.

Time and effort invested in tests also should be pondered. It affects directly the quality, but the quality has its price. Who will pay for it?

About unit testing, we shouldn’t pursue only purity. The important is the test tests, that is, it works for that situation.

Finally, a general rule could be: use TDD moderately.


Here is the link of the Hangout with those great guys:

Is TDD dead?

O TDD está morto?

Test Driven Development (Desenvolvimento Orientado a Testes) é uma metodologia de desenvolvimento de software bastante popular, que é centrada (adivinhe) em testes.

No entanto, apesar de muito propagada, há várias críticas a essa metodologia por profissionais e pensadores de destaque.

Por esses dias rolou uma conversa bastante interessante sobre o assunto entre, nada mais nada menos, que Martin Fowler (que elaborou a ideia de Injeção de Dependência), Kent Beck (idealizador do TDD e do framework JUnit) e David Heinemeier Hansson (criador do Ruby on Rails).

Vamos analisar os pontos altos da conversa nos próximos tópicos. Mas antes, uma breve revisão sobre TDD…

Como o TDD funciona

Ao contrário do processo “tradicional” de desenvolvimento que envolve Especificação, Implementação e Testes, o TDD começa pelos testes em cima das interfaces do sistema.

A princípio todos os testes irão falhar. O objetivo do desenvolvedor é fazer com que eles sejam executados com sucesso. Em tese, isso garante o foco no que realmente deve ser feito e fornece uma medida mais real do progresso.

Os testes utilizados no TDD geralmente são Testes Unitários. Ambos os conceitos estão muito relacionados, mas não são sinônimos.

Testes Unitários

Testes Unitários são aqueles onde uma única função ou método do sistema é testada de cada vez. Em geral esses testes são automatizados.

Isso pode parecer simples, mas implica em que a rotina em questão não pode depender de outras rotinas, caso contrário o teste é sujeito a falhas de terceiros.

Dessa maneira, a fim de testar as rotinas que naturalmente possuem dependências de outras classes e métodos, de recursos como bancos de dados ou arquivos e outros, os desenvolvedores criam simulações desses elementos para que a rotina a ser testada funcione, porém não dependa diretamente desses outros elementos durante o teste.

Este é o conceito de mock, isto é, algo que imita o elemento original.

Por exemplo, um método que normalmente retorna um valor do banco de dados poderia ser substituído por um método que retorna um valor fixo num teste em particular. Cada linguagem, framework e ferramenta deve prover formas adequadas para se fazer isso, sendo inclusive uma forma de medir a qualidade da mesma.

Benefícios desta abordagem

Escrever os testes antecipadamente e então desenvolver com base neles traz várias vantagens:

  • Melhora o entendimento dos requisitos, já que para escrever o teste é necessário que o desenvolver entenda logo de início o que o sistema deve fazer.
  • Provê um objetivo claro a ser alcançado durante o desenvolvimento, isto é, fazer os testes executarem sem erros.
  • Aumenta a visibilidade do que está ou não pronto, já que os testes servem como indicadores mais confiáveis do que algo que está implementado e “só falta testar”.
  • A automação dos testes possibilita a Integração Contínua com certa garantia do funcionamento do sistema, já que todos os testes unitários podem ser reexecutados como testes de regressão sempre que necessário.

Desvantagens

Como regra geral, tudo que traz vantagens tem seus pontos negativos. Sempre temos que tomar cuidado com certos “evangelistas” que irão fazer uma tecnologia ou metodologia parecer não ter um lado negativo ou impacto zero.

Isso se aplica também ao TDD. Vejamos algumas considerações sobre ele:

Aumento do esforço

Será necessário gastar muito tempo para criar os testes e todos os mocks, além da infraestrutura de Integração Contínua. É claro que isso pode ser visto como um investimento em qualidade ao invés de uma pena a ser paga, mas na prática nem todos podem se dar a esse luxo.

Aumento da complexidade

A arquitetura acaba sempre mais complexa, usando muita Injeção de Dependências e outras técnicas de desacoplamento.

Mudanças nos requisitos são muito mais custosas, já que além do código do sistema em si, todos os testes e mocks envolvidos deverão ser reescritos.

Se o arquiteto de software não souber o que está fazendo, a arquitetura do sistema pode acabar cheia de gambiarras.

Intrusividade no código principal

Em muitas situações, será necessário programar de uma forma específica para que um certo código possa ser testado.

A arquitetura da aplicação muitas vezes acaba sendo então afetada por elementos e fatores que, de outra forma, não fariam parte de sua natureza.

Enfim, o design da aplicação acaba sendo influenciado pelos testes, o que nem sempre é desejável, já que é mais uma coisa a ser levada em conta e pode desviar o foco do arquiteto.

Testar tudo é quase impossível

Muitas tecnologias são difíceis de testar com testes unitários ou mesmo com testes de integração.

Pense na interface do usuário, por exemplo. Quantas delas possibilitam a execução da interface de forma “desacoplada”? Existem ferramentas como o Selenium para sistemas web, mas com elas também vêm diversas outras limitações e dependências.

O TDD está morto?

Vimos que todo benefício traz um custo associado. É este todo o ponto da discussão sobre o TDD estar morto. O custo de testar tudo e criar os mocks necessários para todos os testes vale a pena? Vejamos alguns comentários a seguir.

David Hansson inicia a argumentação dizendo que muitas pessoas não conseguem trabalhar com TDD porque não faz muito sentido para determinados tipos de trabalho. Ele toca no ponto da necessidade de se ter muitos mocks e que isso acaba tornando a codificação difícil.

Kent Beck aproveita o gancho e relata de casos onde a equipe não conseguia refatorar o código porque os testes estavam tão acoplados com a implementação, com dois ou três níveis de mocks, que qualquer alteração afetava inúmeros deles. Ele também afirma que há cenários onde o TDD se aplica melhor, por exemplo, quando os requisitos do software a ser desenvolvido são claros o suficiente para escrevê-los em testes diretamente. Por outro lado, há situações onde simplesmente o desenvolvedor vai descobrindo aos poucos aquilo que deve fazer. Isso é recorrente na criação de frameworks ou algoritmos que precisam analisar dados não estruturados.

Fowler argumenta ainda que a maior vantagem do TDD é um código que consegue testar a si mesmo. Só isso poderia também ser alcançado de outras formas. Ele também comenta sobre aqueles que dizem que “você não está fazendo teste unitário de verdade” porque existem algumas dependências durante o teste. Segundo ele, o importante não é a pureza de uma definição, mas que o teste funcione e seja útil, até porque há variações da definição de testes unitários.

Conclusões

É importante entender que TDD não é a “bala de prata” do desenvolvimento de software. Ele traz consigo vários desafios e problemas em potencial, principalmente se não há pessoas suficientemente experientes na equipe.

Testar é essencial, mas para cada projeto podem haver diferentes formas de testes, cujo busto-benefício deve ser avaliado para cada caso.

O teste não deve ser um objetivo em si mesmo, ainda que adotemos o TDD. Ele apenas tem o objetivo de garantir que estamos entregando o que o cliente pediu.

O tempo e o esforço que são investidos em testes também devem ser avaliado. Isso afeta diretamente a qualidade, mas a qualidade tem o seu preço. Quem irá pagar o preço da qualidade?

Quanto a testes unitários, não devemos buscar toda a pureza possível. O importante é que ele teste, ou seja, funcione para aquela situação específica.

Concluindo, o conselho em geral é: use TDD com moderação.


Para quem consegue entender Inglês, aqui vai o link do Hangout com essas grandes personalizados:

Is TDD dead?

Distributed transactions and concurrent processing with Atomikos

distributed Atomikos is a piece of software written in Java that, among other things, implements JTA (Java Transaction API) e XA (eXtended Architecture, supports distributed transaction processing).

In general, a transaction is associated to the current thread, so the methods invoked during a request in a JEE server can share the same transaction.

However, an interesting question on StackOverflow raised the possibility of dividing an atomic operation composed by different tasks between threads with a single global transaction.

Well, in order to bypass the default architecture we’ll need invoke Atomikos XA API directly to include the various Data Sources from each thread in the main transaction.

I did a simple example implementing that. The project is available on my GitHub account.

Implementation

Firstly, we have the DataSource and TransactionManager initialization using Atomikos API in class AtomikosDataSource. Here is the relevant excerpt:

// Atomikos implementations
private static UserTransactionManager utm;
private static AtomikosDataSourceBean adsb;

// initialize resources
public static void init() {
    utm = new UserTransactionManager();
    try {
        utm.init();
        adsb = new AtomikosDataSourceBean();
        adsb.setMaxPoolSize(20);
        adsb.setUniqueResourceName("postgres");
        adsb.setXaDataSourceClassName("org.postgresql.xa.PGXADataSource");
        Properties p = new Properties();
        p.setProperty("user", "postgres");
        p.setProperty("password", "0");
        p.setProperty("serverName", "localhost");
        p.setProperty("portNumber", "5432");
        p.setProperty("databaseName", "postgres");
        adsb.setXaProperties(p);
    } catch (SystemException e) {
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}

After that, I implemented the thread named Processamento (Processing) that receives the main transaction instance (Transaction). The interface Callable defines this thread as some kind of task that returns an Integer value. Here is the code:

private static class Processamento implements Callable<Integer> {

    private int id;
    private boolean falhar; //fail or not?
    private Transaction transaction;

    public Processamento(int id, boolean falhar, Transaction transaction) {
        this.falhar = falhar;
        this.transaction = transaction;
        this.id = id;
    }

    public Integer call() throws Exception {
        if (falhar) {
            //fail unexpectedly
            throw new RuntimeException("Falhou inesperadamente!"); 
        }

        //enlist xa connection
        XAConnection xac = AtomikosDataSource.getDS().getXaDataSource().getXAConnection();
        synchronized (transaction) {
            transaction.enlistResource(xac.getXAResource());
        }

        //normal execution, update row with OK
        Connection c = xac.getConnection();
        Statement s = c.createStatement();
        s.executeUpdate("update teste set processado = 'ok' where id = " + id);
        s.close();
        c.close();

        //delist xa connection
        synchronized (transaction) {
            transaction.delistResource(xac.getXAResource(), XAResource.TMSUCCESS);
        }
        return id;
    }

}

Notice, instead of using JTA, I’m using Atomikos XA API directly.

The call to AtomikosDataSource.getDS().getXaDataSource().getXAConnection() gets a XA connection, which is added to the main transaction with the command transaction.enlistResource(xac.getXAResource()). This operation is called enlistment. At the end of the process, there is a delistment.

I synchronized a few commands because I got random NullPointerException errors during tests. I didn’t investigate if it’s a bug or by design, that is, Transaction object is not synchronized by default.

Finally, I implemented a method to create five instances of the thread above and then get the results. If one of them fail, the global transaction is rolled back. This is the code:

public static int processar(boolean falhar) {
    int ok = 0;
    Transaction transaction = null;
    try {

        //start transaction
        AtomikosDataSource.getTM().begin();
        transaction = AtomikosDataSource.getTM().getTransaction();

        //create thread pool
        ExecutorService executor = Executors.newFixedThreadPool(5);
        List<Callable<Integer>> processos = new ArrayList<Callable<Integer>>();

        //create 5 threads, passing the main transaction as argument
        for (int i = 0; i < 5; i++) {
            //if falhar == true, fail the fifth thread
            processos.add(new Processamento(i + 1, i == 4 && falhar, transaction));
        }

        //execute threads and wait
        List<Future<Integer>> futures = executor.invokeAll(processos);

        //count the result; get() will fail if thread threw an exception
        Throwable ex = null;
        for (Future<Integer> future : futures) {
            try {
                int threadId = future.get();
                System.out.println("Thread " + threadId + " sucesso!");
                ok++; 
            } catch (Throwable e) {
                ex = e;
            }
        }

        if (ex != null) {
            throw ex;
        }

        //finish transaction normally
        transaction.commit();

    } catch (Throwable e) {

        e.printStackTrace();
        try {
            //try to rollback
            if (transaction != null) {
                AtomikosDataSource.getTM().rollback();
            }
        } catch (IllegalStateException e1) {
            e1.printStackTrace();
        } catch (SecurityException e1) {
            e1.printStackTrace();
        } catch (SystemException e1) {
            e1.printStackTrace();
        }

    }
    return ok;
}

Notice that some methods have a parameter named falha (fail). It’ll be used to create a scenario where one of the threads will generate an error and force a rollback of all changes made by other threads.

The processar() (process) method returns the number of “successes”, i.e, threads executed without errors, independently if the transaction was committed or rolled back. It’ll be also used in tests.

Tests

I did tests for both success and error scenarios in order to validate the solution.

In the success scenario, each one of the five threads updates a row from TESTE table with the value ok, then the transaction is committed.

In the error scenario, the last thread always throw an exception, forcing the rollback of all operations. Notice that the last thread created it’s not necessarily the last one executed.

The test code is very simple. Look:

public class AtomikosTest {

    @BeforeClass
    public static void init() {
        //create atomikos transaction manager and data source
        AtomikosDataSource.init();

    }
    @Before
    public void reset() {
        //recreate data of TEST table
        AtomikosDAO.resetTable();
    }

    @AfterClass
    public static void shutdown() {
        //close atomikos resources
        AtomikosDataSource.shutdown();
    }

    @Test
    public void sucesso() {
        //process 5 rows in 5 threads
        int okParcial = AtomikosDAO.processar(false);
        //should return 5 successes
        Assert.assertEquals(5, okParcial);
        //confirms in table, count 5 ok's
        Assert.assertEquals(5, AtomikosDAO.countOk());
    }

    @Test
    public void fail() {
        //process 5 rows in 5 threads, one should fail
        int okParcial = AtomikosDAO.processar(true);
        //should return 4 successes
        Assert.assertEquals(4, okParcial);
        //confirms in table, count zero ok's due to rollback
        Assert.assertEquals(0, AtomikosDAO.countOk());
    }

}

Notes about configuration

In this project, I chose PostgreSQL to be the resource to be modified in the distributed transaction.

It was necessary enable the configuration called max_prepared_transactions in the configuration file postgresql.conf with a value greater than the number of participants in the distributed transaction. Otherwise, PostgreSQL won’t be able of participating in distributed transactions.

Final thoughts

Even though there’s a growing interest about NoSQL and even NewSQL, ACID transactions, as available on traditional RDBMS, are very important in many situations. It’s so true, that there are tutorials on how to simulate a transaction with two-phase commit in non-transactional databases like MongoDB.

Furthermore, it’s important to say that each participant of a distributed transaction must support XA protocol. Unfortunately, some database drivers and other data source implementations aren’t. So do your homework and research before start coding.


Este artigo foi baseado na minha resposta no StackOverflow em Português!

Página 4 de 10

Creative Commons O blog State of the Art de Luiz Ricardo é licenciado sob uma Licença Creative Commons. Copie, compartihe e modifique, apenas cite a fonte.