segunda-feira, 6 de maio de 2013

Sobre mim

Olá, meu nome é Wellington e sou um desenvolvedor de software. Comecei nessa área em 1997 com Delphi mas venho trabalhando com Java desde 2001. Gosto bastante de tecnologia mas não me considero um viciado em tecnologia. Sou da geração antiga, adoro um rascunho em uma folha de papel ou em um quadro branco! Sempre tive uma afinidade pelos fundamentos por trás da tecnologia que eu uso e sempre que possível tento entender bem essas bases. Acho que por essa razão eu gosto muito das coisas mais básicas e simples! As soluções simples tendem a ser muito elegantes e eficientes em geral. Isso talvez seja um resquício das aulas de Análise de Algoritmos durante o mestrado. Mas de fato, na minha opinião, a simplicidade é a alma do negócio.

Além da informática, eu gosto muito de música...entenda que quando eu digo “gostar de muito de música” é muito mesmo! Sou contrabaixista (meia boca) nas horas vagas e ainda acredito que um dia conseguirei conciliar a vida de desenvolvedor com a vida de músico amador...quem sabe...estamos trabalhando para isso :-) Mas apesar do contrabaixo ser o instrumento da minha escolha, adoro a combinação de todos os instrumentos para ter algo maior, isso é impressionante. O dia mais louco foi quando eu pude tocar com um saxofonista e um pianista em uma aula de música, a sensação é inexplicável!

Bom, é isso aí! Qualquer coisa não hesitem em entrar em contato.

[]'s,
Wellington.
Visualizar meu perfil completo

terça-feira, 2 de abril de 2013

Gerando um único arquivo Jar com o Maven contendo todas as dependências do projeto

A construção de um projeto Java geralmente envolve o uso de diversas bibliotecas externas que podem ser desenvolvidas por terceiros ou construídas in-house especificamente para um determinado projeto. Essas bibliotecas são normalmente distribuídas como arquivos JARs e disponibilizadas nos ambiente de execução (JVM) utilizando o classpath. Apesar dessa ser uma forma padrão adotada em aplicativos construídos em Java, em certos casos temos a necessidade de gerar um único arquivo JAR que contém tanto as classes do projeto quanto as classes de suas dependências. Esse JAR em questão é conhecido como Uber JAR.

Uma abordagem direta e mais imediata que vem na cabeça para gerar esse Uber JAR é extrair os arquivos de todos JARs que o nosso projeto depende em um único diretório e depois adicioná-los novamente em um único JAR. Mas será que precisamos fazer isso na mão? Bem, se você tem o costume de usar o Maven para compilar e empacotar seus projetos, a resposta é não! O Shade é um plugin do Maven que faz esse trabalho sujo para nós.

Vamos usar um projeto simples de exemplo para demonstrar o funcionamento do Shade. O objetivo desse projeto é simplesmente gerar uma mensagem de saudação no console utilizando o Spring Framework. No final, vamos gerar um Uber JAR executável contendo as classes do projeto e as classes e arquivos de configuração do Spring Framework. Para tanto, vamos começar construindo uma classe HelloWorld, apresentada a seguir, que contém um único método responsável por gerar a saudação.

public class HelloWorld {

    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

O Spring framework será responsável por criar a instância dessa classe. Assim, vamos criar o arquivo de configuração do Spring, que basicamente define um bean chamado helloWorld do tipo da classe HelloWorld. A listagem abaixo mostra como fica o XML de configuração do contexto do Spring.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.2.xsd">

    <bean id="helloWorld" class="br.com.wrpinheiro.helloworldspringshade.HelloWorld" />
</beans>

Por fim, criamos a classe que será responsável por inicializar o contexto do Spring, obter a instância do bean do contexto e chamar o método que gera a saudação. A listagem abaixo apresenta tal classe.

public class Main {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("helloWorldCtx.xml");
        HelloWorld h = ctx.getBean("helloWorld", HelloWorld.class);
        String message = h.sayHello("Shade Plugin");

        System.out.println(message);
    }
}

Agora que temos todos os artefatos necessários para o nosso projeto, vamos construir o pom.xml do Maven para o nosso projeto. O conteúdo desse arquivo aparece na seguinte listagem.

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>br.com.wrpinheiro</groupId>
  <artifactId>helloworld-spring-shade</artifactId>
  <packaging>jar</packaging>
  <version>1.0</version>
  <name>helloworld-spring-shade</name>
  <url>http://maven.apache.org</url>

  <dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>3.2.0.RELEASE</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.0</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                  <resource>META-INF/spring.handlers</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                  <resource>META-INF/spring.schemas</resource>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <mainClass>br.com.wrpinheiro.helloworldspringshade.Main</mainClass>
                </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>

Esse arquivo deixa explicito na seção <dependencies> que o projeto depende do Spring Context. Na seção <build> é informado que o plugin Shade deve ser usado na fase de empacotamento (package) do projeto, ou seja, durante a geração do JAR do projeto deverá ser executado o Shade para copiar para o JAR gerado as classes que o projeto depende. Note que o Uber JAR gerado conterá todas as classes de todas as dependências resolvidas transitivamente, ou seja, as classes do Spring Context, as dependências do Spring Context, as dependências das dependências, e assim por diante.

Note ainda que nesse pom usamos os Resources Transformers do Shade na seção <configuration>. Os Resource Transformers são usado para agregar diversos recursos em um único arquivo. Por exemplo, no caso do Spring Framework, cada JAR contém um conjundo de arquivos XSD utilizados para validar os XMLs e dentro do META-INF de cada JAR existem dois arquivos chamados spring.schemas e spring.handlers que auxiliam na tarefa de "descobrir" onde estão os arquivos XSD. Como estamos juntando vários arquivos JARs do Spring e em cada um desses arquivos existe o spring.schemas e o spring.handlers, será necessário unir o conteúdo desses arquivos em um único arquivo que ficará no META-INF do Uber Jar. Essa junção dos arquivos pode ser feita pelo Shade, utilizando as seguintes definições:

    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
      <resource>META-INF/spring.handlers</resource>
    </transformer>
    <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
      <resource>META-INF/spring.schemas</resource>
    </transformer>

Além disso, como o JAR gerado será um executável vamos incluir também no MANIFEST-MF qual é a classe executável do projeto, incluindo as seguintes linhas no pom.xml:

    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
      <mainClass>meu.projeto.helloworld.Main</mainClass>
    </transformer>

Nosso projeto está pronto para ser compilado e testado, conforme mostrado a seguir:

$ mvn package

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building helloworld-spring-shade 1.0
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:resources (default-resources) @ helloworld-spring-shade ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 1 resource
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ helloworld-spring-shade ---
[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent!
[INFO] Compiling 2 source files to /opt/workspaces-eclipse/helloworld-spring-shade/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.4.3:testResources (default-testResources) @ helloworld-spring-shade ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] skip non existing resourceDirectory /opt/workspaces-eclipse/helloworld-spring-shade/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ helloworld-spring-shade ---
[INFO] No sources to compile
[INFO] 
[INFO] --- maven-surefire-plugin:2.7.2:test (default-test) @ helloworld-spring-shade ---
[INFO] No tests to run.
[INFO] Surefire report directory: /opt/workspaces-eclipse/helloworld-spring-shade/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
There are no tests to run.

Results :

Tests run: 0, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-jar-plugin:2.3.1:jar (default-jar) @ helloworld-spring-shade ---
[INFO] Building jar: /opt/workspaces-eclipse/helloworld-spring-shade/target/helloworld-spring-shade-1.0.jar
[INFO] 
[INFO] --- maven-shade-plugin:2.0:shade (default) @ helloworld-spring-shade ---
[INFO] Including org.springframework:spring-context:jar:3.2.0.RELEASE in the shaded jar.
[INFO] Including org.springframework:spring-core:jar:3.2.0.RELEASE in the shaded jar.
[INFO] Including commons-logging:commons-logging:jar:1.1.1 in the shaded jar.
[INFO] Including org.springframework:spring-aop:jar:3.2.0.RELEASE in the shaded jar.
[INFO] Including aopalliance:aopalliance:jar:1.0 in the shaded jar.
[INFO] Including org.springframework:spring-expression:jar:3.2.0.RELEASE in the shaded jar.
[INFO] Including org.springframework:spring-beans:jar:3.2.0.RELEASE in the shaded jar.
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing /opt/workspaces-eclipse/helloworld-spring-shade/target/helloworld-spring-shade-1.0.jar with /opt/workspaces-eclipse/helloworld-spring-shade/target/helloworld-spring-shade-1.0-shaded.jar
[INFO] Dependency-reduced POM written at: /opt/workspaces-eclipse/helloworld-spring-shade/dependency-reduced-pom.xml
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.326s
[INFO] Finished at: Tue Apr 02 11:35:45 BRT 2013
[INFO] Final Memory: 9M/239M

$ cd target

$ java -jar helloworld-spring-shade-1.0.jar
02/04/2013 11:36:26 org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@296672d6: startup date [Tue Apr 02 11:36:26 BRT 2013]; root of context hierarchy
02/04/2013 11:36:26 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [helloWorldCtx.xml]
02/04/2013 11:36:26 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@70453807: defining beans [helloWorld]; root of factory hierarchy
Hello, Shade Plugin!

Observe que no diretório target do projeto existem dois arquivos JAR: (i) helloworld-spring-shade-1.0.jar, que é o Uber JAR e (ii) original-helloworld-spring-shade-1.0.jar, que é o arquivo JAR original sem as dependências.

O código fonte do projeto de demonstração está disponível no Github, nesse link.

Por enquanto é isso aí galera! Bons builds.

Abraços,
Wellington.

quinta-feira, 10 de janeiro de 2013

Criando um sistema multi-módulos com o Maven


Nota: Uma versão atualizada e melhorada desse post está disponível no meu novo blog, com o título Criando uma aplicação multimódulo com Maven. Não deixe de conferir!!!


Imagine que você está construindo um sistema grande, composto de diversos projetos no seu IDE favorito. Cada um desses projetos tem a sua configuração Maven específica para fazer a compilação e geração do seu Jar, War, Ear ou algo similar. Apesar dessa abordagem ser satisfatória ela pode ter algumas desvantagens, tais como:

1) Cada projeto tem uma configuração totalmente separada e, caso exista algo em comum entre esses projetos, essa configuração deverá ser replicada;
2) Quando o sistema com todos os seus projetos tiverem que ser compilados, será necessário compilar cada um dos projetos considerando a dependência entre eles. E essa dependência geralmente está na cabeça de algumas pessoas que nunca estão disponíveis no momento.

Uma forma de resolver essas questões consiste em reogarnizar os projetos na estrutura multi-módulo do Maven. Nessa abordagem, cada projeto é definido como um módulo e é criado um projeto, que chamaremos de projeto agregador, utilizado agrupar todos os módulos. O projeto agregador contém um pom.xml com declarações comuns, tais como dependências e plugins, que são herdadas por cada módulo.

Para exemplificar como é essa organização multi-módulos utilizaremos um sistema fictício chamado SistemaX. Esse sistema contém dois módulos criativamente chamados de Módulo A e Módulo B. Só para complicar um pouquinho, vamos supor que o Módulo A tem uma dependência para o JUnit e o Módulo B depende do Módulo A. A estrutura de diretórios deve ficar da seguinte forma:

sistemaX/
├── moduloA
│   └── pom.xml
├── moduloB
│   └── pom.xml
└── pom.xml
O diretório sistemaX é onde está definido o projeto agregador e os diretórios dos módulos A e B são, respectivamente, moduloA e moduloB. Observe que os módulos devem ficar dentro do diretório do projeto agregador. Cada módulo continua com o seu pom.xml específico porém, agora eles devem herdar as definições feitas no pom do projeto agregador. Vamos analisar cada um dos arquivos pom.xml e explicar como são feitas as configurações.

sistemaX/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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>meu.projeto.multimodulo</groupId>
  <artifactId>sistemaX</artifactId>
  <packaging>pom</packaging>
  <version>1.0</version>

  <modules>
    <module>moduloA</module>
    <module>moduloB</module>
  </modules>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.1</version>
        <scope>test</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
</project>
No pom.xml do projeto agregador são definidas as características que basicamente todo pom.xml define, tais como: groupId, artifactId, packaging e version. Um detalhe aqui é que o packaging está definido como pom. Isso indica que esse projeto não gera um artefato tipo jar ou war, e sim que ele contém definições que serão herdadas por outros projetos. A tag <modules> é usada para definir os módulos que fazem parte do projeto, nesse caso, moduloA e moduloB. Além da definição dos módulos, também são declaradas algumas dependências com a tag <dependencyManagement>. Apesar do projeto agregador em sí não precisar dessas dependências, ele pode ser usado para manter um ponto central de gerenciamento dessas definições ao invés de fazer separado por módulo.

sistemaX/moduloA/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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>meu.projeto.multimodulo</groupId>
    <artifactId>sistemaX</artifactId>
    <version>1.0</version>
  </parent>

  <artifactId>moduloA</artifactId>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
    </dependency>
  </dependencies>
</project>


No pom.xml do Módulo A é utilizada a tag <parent> para informar que as definições feitas no pom.xml do projeto agregador são utilizadas nesse módulo. Observe que aqui não é mais necessário definir um groupId e version para o módulo pois essas informações são obtidas a partir do pom do projeto agregador. No caso desse módulo a tag <packaging> é definida como jar pois o módulo vai gerar um jar específico, o mesmo vale o módulo B, como veremos a seguir. Como esse módulo depende do JUnit, é criada uma entrada especificando essa dependência, mas repare que não é mencionada qual a versão do JUnit será usadao pois isso é obtido na definição da dependência no pom do projeto agregador.

sistemaX/moduloB/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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>meu.projeto.multimodulo</groupId>
    <artifactId>sistemaX</artifactId>
    <version>1.0</version>
  </parent>

  <artifactId>moduloB</artifactId>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>meu.projeto.multimodulo</groupId>
      <artifactId>moduloA</artifactId>
      <version>1.0</version>
    </dependency>
  </dependencies>
</project>



Note que o pom.xml do Módulo B é muito parecido com o do Módulo A, exceto pelo fato dele definir uma dependência para o Módulo A mas não para o JUnit. Nesse caso, se existir alguma classe que faça uso do JUnit no Módulo B, será gerado um erro de compilação pois a dependência não foi declarada para esse módulo, mesmo que ela tenha sido definida no pom do projeto agregador.

Pronto, feitas essas alterações basta executar o maven a partir do diretório sistemaX que ele se encarregará de compilar os projetos dependentes e gerar os respectivos artefatos que forem necessário.

Um último detalhe. Estão lembrados que o Módulo B depende do Módulo A? Assim, o Módulo A tem que ser compilado antes do Módulo B. E se no pom.xml do projeto agregador invertermos a ordem de declaração dos módulos, deixando da seguinte forma:

  <modules>
    <module>moduloB</module>
    <module>moduloA</module>
  </modules>


Bem, isso não afeta em nada! O Maven é esperto o suficiente para decifrar a dependência entre os módulos e fazer a compilação na ordem correta.

É isso ai!

Bons builds para todos.

quarta-feira, 9 de março de 2011

Testando as novas funcionalidades do JDK 7 - Parte 2

Esse post é a segunda e última parte dos posts sobre os novos recursos do Java 7. Na minha opinião, os recursos apresentados nesse post são mais interessantes do que aqueles apresentados no post anterior porque causam mudanças mais significativas na linguagem.

Caso você tenha interesse no primeiro post, ele pode ser encontrado aqui!
O código fonte dos programas utilizados como exemplo nos dois posts estão disponíveis no Githut em: https://github.com/wrpinheiro/diversos.

Boa leitura.

4. Inferência na Criação de Objetos de Tipos Parametrizados (Generics)

O suporte para tipos parametrizados (generics), incluído no Java 5,  é certamente um aliado do desenvolvedor que facilita tanto a codificação quando a depuração. Com essa abordagem é possível informar tipos como parâmetros para classes e interfaces, fornecendo informações adicionais para o  compilador detectar erros em tempo de compilação que antes eram detectados somente em tempo de execução.

Apesar das vantagens do Generics, com ele a linguagem também ganhou uma verbosidade extra! Veja um exemplo na classe InferenciaGenerics abaixo:

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class InferenciaGenerics {

  public static void main(String[] args) {
    Map<Integer, Set<Integer>> mapOfIntegers =
      new HashMap<Integer, Set<Integer>>();

    Integer aKey = 10;
    Set<Integer> aSet = new HashSet<Integer>();

    mapOfIntegers.put(aKey, aSet);
  }
}

No Java 7 é possível reduzir obter uma sintaxe mais enxuta para a crição dos objetos nas linhas 10 e 13 através da omissão dos tipos com uma notação apelidada de diamond:

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class InferenciaGenerics {

  public static void main(String[] args) {
    Map<Integer, Set<Integer>> mapOfIntegers =
      new HashMap<>();

    Integer aKey = 10;    
    Set<Integer> aSet  = new HashSet<>();

    mapOfIntegers.put(aKey, aSet);
  }
}

Com essa sintaxe os parâmetros de tipos usados na construção dos objetos são inferidos a partir dos parâmetros definidos na referência para esse objeto, ou seja, em uma declaração do tipo: Tipo<t1, t2, ..., tk> referencia = new Tipo<>(), o compilador entenderá que new Tipo<>() deverá ser substituído por new Tipo<t1, t2, ..., tk>().

Além disso, também é possível postergar a criação do objeto do tipo HashSet na linha 13, fazendo-o na linha 15 da seguinte forma:
mapOfIntegers.put(aKey, aSet = new HashSet<>());

nesse caso, os parâmetros do tipo são inferidos a partir daqueles usados na declaração da variável aSet. Note que a seguinte sentença não compila:
mapOfIntegers.put(aKey, new HashSet<>());

5. Simplificação na Invocação de Métodos Varargs

Em Java, Arrays e tipos genéricos não combinam assim tão bem!! Observe o seguinte exemplo:

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class TesteVarargs {
  public static void main(String[] args) {
    Set<Integer> aSet = new HashSet<integer>();

    List<Set<Integer>> listOfSets = Arrays.asList(aSet);
  }
}


Quando o programa acima é compilado com o Java versão <= 1.6 através do comando: javac -Xlint:unchecked Varargs.java, o compilador gera o seguinte warning:

TesteVarargs.java:10: warning: [unchecked] unchecked generic array 
creation of type java.util.Set<java.lang.integer>[] for varargs parameter List<Set<Integer>> listOfSets = Arrays.asList(aSet); ^ 1 warning

Mas repare que não há nada de errado ou estranho com esse programa. As declarações, tipos e os parâmetros estão corretos. Mas porque o compilador emitiu esse warning? Na verdade, o que acontece é que o compilador antes de gerar o bytecode faz uma conversão no programa que literalmente modifica a linha 10 que fica assim:

List<Set<Integer>> listOfSets = Arrays.asList(new Set[]{aSet});

Note que nessa conversão o Set usado para construir o array não contém mais a informação de tipo (em Java não é permitido a criação de um array usando generics) o que provoca esse warning.  Generalizando, esse warning é gerado sempre que é feita a chamada para um método com uma sintaxe do tipo

<T> Tipo<T> metodo(T... params)

sendo T e Tipo dois tipos que podem ser parametrizadas e na ocasião da chamada do método em questão as instâncias representadas pelo Array params também estão com tipos parametrizados. No exemplo apresentado T é Set<Integer> enquanto que Tipo<T> é List<Set<Integer>>.

Uma forma de evitar esse warning é incluindo a anotação @SuppressWarnings("unchecked") no método ou na linha imediatamente antes da linha no qual o compilador indicou o Warning. Apesar de resolver o problema, isso é um tanto inconveniente porque poluí o nosso código visto que a chamada desse método não pode gerar nenhum problema referente à tipagem (heap pollution seria o termo correto aqui).

Note que no exemplo apresentado não há nada que possamos fazer em nosso código para evitar esse warning a não ser adicionar a anotação de SuppressWarning. Assim, seria interessante que em casos desse tipo o compilador pudesse entender que essa chamada não pode gerar problema. Para resolver essa situação foi criado no Java 7 a anotação 

@SafeVarargs

A função dessa nova anotação é informar ao compilador que a operação de conversão forçada de Arrays com tipos parametrizados é segura, ou seja, não acontecerá o heap pollution. Quando usada na declaração de um método com a sintaxe citada, essa anotação desonera as classes que chamam esses métodos da necessidade de utilizarem o SuppressWarnings.

É importante observar que agora a responsabilidade de informar o compilador para não gerar o Warning é da classe que fornece a implementação e não da classe cliente que a usa. Dessa forma, a partir do Java 6 é possível remover todos os SuppressWarnings em casos onde esse Warning era gerado.

6. Gerenciamento Automático de Recursos & Multi-catch

Duas novidades que realmente vão facilitar a vida ao escrever programas em Java são o Gerenciamento Automático de Recursos e o Multi-catch. Utilizarei um exemplo para mostrar essas novas funcionalidades.

Suponha um programa que seja responsável por ler um arquivo texto contendo nomes de classes, um nome por linha incluindo o pacote e, para cada linha será obtida a classe através do comando Class.forName e criada uma instância dessa classe através do método newInstance da classe Class também. Suponha que os tratamentos de erros apropriados estejam feitos.

Para fazer a leitura do arquivo será utilizado um FileReader e um BufferedFileReader, mas para esse exemplo, eu criei uma classe que especializa o BufferedReader com o objetivo de simplesmente mostrar quando o método close() é chamado. Essa classe é chamada (criativamente) de MyBufferedReader e o seu código é apresentado a seguir:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;

public class MyBufferedReader extends BufferedReader {
  public MyBufferedReader(Reader r) {
    super(r);
  }

  public void close() throws IOException {
    System.out.println("fechando o BufferedStream");
    super.close();
  }
}

A seguir apresentamos a classe ResourceManagementMultiCatch que é responsável por solucionar o problema.

import java.io.FileReader;

public class ResourceManagementMultiCatch {
  private static void instantiate(String className) throws Exception {
    try {
      Class<?> clazz = Class.forName(className);
      Object o = clazz.newInstance();
      System.out.println(o.getClass().getName());

    } catch (ClassNotFoundException e) {
      System.err.println("Classe nao encontrada: " + className);
      throw e;
    } catch (InstantiationException e) {
      System.out.println("Nao foi possivel instanciar a classe: " + className);
      throw e;
    } catch (IllegalAccessException e) {
      System.out.println("Nao foi possivel instanciar a classe: " + className);
      throw e;
    }
  }

  public static void main(String[] args) throws Exception {
    MyBufferedReader br = new MyBufferedReader(new FileReader("classes.txt"));
    try {  
      String line;
      while ((line = br.readLine()) != null)
        instantiate(line);
    } finally {
      br.close();
    }
  }
}


O método main é responsável basicamente por ler o arquivo e o conteúdo de cada linha é passado para o método instantiate que tentará carregar a classe e criar uma instância.

Suponha que que o arquivos classes.txt contém somente a linha: java.util.ArrayList. Ao executarmos o programa acima é obtida a seguinte saída:

java.util.ArrayList
fechando o BufferedStream

Caso o arquivo classes.txt contivesse o nome de uma classe não existente no Java, como por exemplo: java.util.ArrayList1, obteríamos a seguinte saída:

Classe nao encontrada: java.util.ArrayList1
fechando o BufferedStream
Exception in thread "main" java.lang.ClassNotFoundException: java.util.ArrayList1
 at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
 at java.security.AccessController.doPrivileged(Native Method)
 at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
 at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
 at java.lang.Class.forName0(Native Method)
 at java.lang.Class.forName(Class.java:169)
 at ResourceManagement.instantiate(ResourceManagement.java:6)
 at ResourceManagement.main(ResourceManagement.java:27)

Observe no método main o uso do finally que chama o método close() do MyBufferedReader com o objetivo de liberar os recursos alocados para usar o arquivo classes.txt. Note também que, tanto executando o programa com sucesso quanto em situação de erro a mensagem: "fechando o BufferedStream" aparece, ou seja, o finally é executado e os recursos são liberados em ambos os casos.

Essa abordagem no uso do finally é muito comum quando trabalhamos com conexões e statements de BD, IO em geral, manipulação de arquivos de mídia, etc. Esses são os casos onde é necessário que o nosso programa adquira recursos do SO e esses recursos devem ser liberados posteriormente.

Para tirar esse trabalho do desenvolvedor, o Java 7 traz o gerenciamento automático de recursos. Observe o código a seguir que implementa esse recurso no método main:

public static void main(String[] args) throws Exception {
    try(MyBufferedReader br = new MyBufferedReader(new FileReader("classes.txt"))) {
      String line;
      while ((line = br.readLine()) != null)
        instantiate(line);
    }
  }
}

Com essa mudança, a declaração e inicialização do MyBufferedReader é feita no próprio try, definindo que a instância terá seu método close chamado automaticamente, de forma semelhante aquela obtida quando usamos o finally. Isso certamente evita confusões e erros na hora de liberar os recursos.

Mas como o Java sabe quais classes podem ser usadas para fazer esse tratamento? A resposta é que agora existe a interface java.lang.AutoCloseable com um único método: void close() throws Exception. Assim, qualquer classe que implementa essa interface pode ser passada para o try e o método close será chamado. Em decorrência dessa chamada implícita do close, esse try deve fazer parte de outro try contendo um catch para Exception ou o método deve lançar Exception.

Continuando com o nosso exemplo, outra novidade é o multi-catch no qual agora o catch pode tratar múltiplas exceções. Dessa forma, quando temos vários catches fazendo um mesmo tratamento para exceções diferentes, podemos juntar tudo em um único catch. No programa de exemplo, podemos aplicar esse recurso e o método instantiate fica assim:

private static void instantiate(String className) throws Exception {
  try {
    Class<?> clazz = Class.forName(className);
    Object o = clazz.newInstance();
    System.out.println(o.getClass().getName());

  } catch (ClassNotFoundException e) {
    System.err.println("Classe nao encontrada: " + className);
    throw e;
  } catch (InstantiationException | IllegalAccessException e) {
    System.out.println("Nao foi possivel instanciar a classe: " + className);
    throw e;
  }
}

Como estávamos fazendo o mesmo tratamento para as exceções InstantiationException e IllegalAccessException, então juntamos essas duas exceções num catch só. Repare que as exceções são separadas por um caractere pipe '|'. A forma como é feito o tratamento usando essa sintaxe é a mesma que a anterior.

Conclusão

Nesse post foram apresentados mais 3 recursos da linguagem Java 7, que são: Inferência na Criação de Objetos de Tipos Parametrizados (Generics), Simplificação na Invocação de Métodos Varargs e o Gerenciamento Automático de Recursos & Multi-catch. Essas alterações são maiores em relações àquelas apresentadas no primeiro post e realmente fazem diferença na linguagem.

Em especial, gerenciamente automático de recursos deve ajudar muito a resolver problemas referentes ao gerenciamento de recursos e a inferência de tipos e multi-catch ajudam a diminuir a verbosidade do Java.

Isso aí pessoal! Até a próxima.

Links Úteis

quarta-feira, 2 de fevereiro de 2011

Testando as novas funcionalidades do JDK 7 - Parte 1

No dia 18/01 foi lançada a primeira release do JDK 7 contendo todas as funcionalidades planejadas para essa versão. Como eu fiquei muito curioso, tive que instalar essa versão e fuçar um pouco só para dar uma descontraída!! Vale citar que essa não é uma versão final, portanto, se você pretende instalar para desenvolver ou mesmo usá-la em ambiente de produção, esqueça....algo sinistro pode acontecer (ok, se mesmo assim você fez isso, conta o resultado pra gente :-)  ).

As modificações dessa versão 7 do JDK em relação à versão anterior foram divididas nas seguintes categorias: Máquina Virtual, Linguagem, Core, Internacionalização, I/O e Rede, Segurança e Criptografia, JDBC, Camada Cliente, Gerenciamento (JMX e MBeans).  Como existem muitas modificações, eu optei por tratar nesse post somente as alterações na linguagem (sintaxe e semântica) e, devido ao meu tempo escasso, resolvi mostrar as novas funcionalidades em dois posts, sendo que o segundo post eu ainda não tenho planejado quando ficará pronto, mas espero que seja em breve então, stay tuned!!!

Bem, chega de blah blah blah e vamos ao que interessa.

1. String na Seleção do switch

Uma alteração interessante e muito útil é que agora podem ser utilizadas Strings em comandos switch. Observe o programa a seguir,  que recebe parâmetros de configuração através da linha de comando e executa ações específicas para cada parâmetro.

public class StringsInSwitch {

  public static void main(String[] args) {
    for (String param : args) {
      switch(param) {
        case "-test": 
          System.out.println("O programa esta sendo executado em modo teste");
          break;
        case "-silent":
          System.out.println("O programa envia poucas informacoes na saida padrao");
          break;
        case "-verbose":
          System.out.println("O programa envia muitas informacoes na saida padrao");
          break;
        default:
          System.out.printf("Parametro %s nao reconhecido\n", param);
          System.exit(-1);
      }
    }
  }
}

Note que no switch da linha 5 é utilizada uma variável do tipo String para selecionar entre os vários casos de teste de condição. Apesar dessa ser uma alteração simples, isso evita aquele monte de ifs encadeados ou então a necessidade de procurar soluções mirabolantes para contornar situações desse tipo.

2. Underscore como separador de dígitos

Não sei se isso pode parecer normal para muitos, mas para mim realmente não é!! A partir do JDK 7 é possível utilizar o caractere "_"  (underscore, underline, grifo, sublinhado, etc, etc, etc) como separador de dígitos em literais  numéricos. Segundo a proposta (leia mais sobre o subprojeto Coin através do link na seção de Links Úteis no final do post), a idéia é criar um mecanismo que possa facilitar a leitura por seres humanos de um número com vários dígitos. Dessa forma, ao invés de escrevermos um número assim: 123456789, podemos escrever 123_456_789. Basicamente, o compilador vai ignorar o underscore e interpretar  o número como se esse caractere não existisse. Até aí, você talvez tenha gostado dessa idéia, mas o problema é que podemos escrever esse mesmo número nos seguintes formatos:

  • 1_2_3_4_5_6_7_8_9
  • 123__45__678__9
  • 12_3456_78_9
  • 12___________________345678______9
Além disso, isso também vale para números de ponto flutuante (double e float), tais como:
  • 12__3.45___6D
Note que o underscore poder ser utilizado livremente em qualquer posição de literais numéricos, exceto na primeira e na última. O programa a seguir apresenta um exemplo de uso do underscore como separador:

public class UnderscoreAsSeparator {

  public static void main(String[] args) {
    int x = 10_000;
    int y = 3_333;
    long l = 123_123_123_123L;
    double f = 123_456.3_4_1;

    System.out.printf("O valor de x e: %d\n", x);
    System.out.printf("O valor de y e: %d\n", y);
    System.out.printf("O valor de l e: %d\n", l);
    System.out.printf("O valor de f e: %.4f\n", f);
    System.out.printf("O valor de x+y e: %d\n", x+y);
  }
}

Ao executar esse programa, a seguinte saída é obtida:

O valor de x e': 10000
O valor de y e': 3333
O valor de l e': 123123123123
O valor de f e': 123456,3410
O valor de x+y e': 13333

3. Literais Binários

Até a versão 6 do Java era possível trabalhar com literais inteiros definidos como octais, decimais e hexadecimais. A partir da versão 7 também é possível trabalhar com literais binários. Para a grande maioria das aplicações comerciais isso pode até não fazer muito sentido, mas em sistemas nos quais são frequentes as operações binárias envolvendo bits e shitfs, isso pode ser uma mão na roda! A sintaxe dessa declaração é muito simples, basta iniciar o literal com 0b (número zero seguido da letra 'b') seguido por uma sequência de 0s e 1s. Por exemplo:

  • 0b1100
  • 0b01
  • 0b1101

Repare que os dígitos são alinhados à direita na área de memória, ou seja,  0b11 é igual a 3.

O código a seguir simula um interpretador de sinais obtidos a partir da leitura do status de uma válvula e imprime o status atual. Os status são formados a partir de padrões de bits, isto é, quando a válvula está fechada o valor binário é 01, quando a válvula está aberta o valor binário é 10 e quando a válvula está em alarme o valor é binário é 11. Para simular os status foi construído o método readStatus que devolve aleatóriamente um inteiro com os valores possíveis de status da válvula. No método main é executado um laço que lê o status atual até que ele seja nulo (NONE) e imprime uma mensagem informando o status atual. Cada status é representado por uma constante que depois será utilizada para indexar um array de mensagens.

public class BitLiteral {
  private static final int NONE   = 0b00;
  private static final int CLOSED = 0b01;
  private static final int OPEN   = 0b10;
  private static final int ALARM  = 0b11;

  private static final String[] MESSAGES = {"NONE", "CLOSED", "OPEN", "ALARM"};

  public static int readStatus() {
    return (int)(Math.random()*0b100);
  }

  public static void main(String[] args) {
    System.out.printf("The notable statuses are: %s(%d) %s(%d), %s(%d), %s(%d)\n\n",
      MESSAGES[NONE], NONE, MESSAGES[CLOSED], CLOSED,
      MESSAGES[OPEN], OPEN, MESSAGES[ALARM], ALARM);

    int st = readStatus();
    while (st != NONE) {
        System.out.printf("Valve state is: %s\n", MESSAGES[st]);
        st = readStatus();
    }
  } 
}

Observe que no método main são utilizadas as constantes de status (inicializadas através da notação binária) para acessar as posições do vetor contendo as mensagens. No final das contas, o mesmo raciocínio utilizado para literais octais, hexa e decimais também é aplicado aos binários.

Só para complementar essa discussão sobre literias binários, também é possível utilizar o underscore como separador de dígitos:

private static final int NONE   = 0b0___0;
private static final int CLOSED = 0b0_1;
private static final int OPEN   = 0b1___0;
private static final int ALARM  = 0b1_1;

Conclusão e Cenas do Próximo Capítulo

Nesse post foram apresentadas as seguintes novas funcionalidades do JDK 7: String na seleção do switch, underscore para separar dígitos em literais e literais binárias. Entre esse itens citados, o que realmente deve agradar mais os desenvolvedores é  a possibilidade de utilizar strings na seleção do comando switch. De qualquer forma, vale a pena ficar atento dessa outras mudanças pois um dia nós poderemos encontrar algo assim em algum código perdido por aí!

No próximo post eu vou comentar sobre a inferência na instanciação de tipos genéricos, as novas formas de tratamento de exceção e também sobre as mudanças em varargs e também outras modificações bacanas que ficaram para o JDK 8.

Isso aí! Agora eu vou ouvir Judas Priest.....até a próxima!

Links Uteis


    O Projeto COIN é na verdade um subprojeto do OpenJDK que tem como objetivo definir mudanças na linguagem Java que serão incorporadas no JDK - nesse caso, os itens apresentados nesse post foram apresentados todos pelo projeto COIN.


quinta-feira, 30 de dezembro de 2010

Sobre 2010

Esse ano de 2010 foi bem agitado para mim! Vários acontecimentos tornaram esse ano especial e ao mesmo tempo "tenso".

O final do meu curso de mestrado foi certamente um marco! Afinal, eu estava nesse curso desde 2006 e o final foi realmente complicado. Felizmente, deu tudo certo e a minha defesa aconteceu em Maio, sendo a versão final da dissertação entregue em Junho.

Junto da minha esposa, vendemos nosso apartamento, compramos outro e reformamos uma casa para a qual nos mudamos em Março. A reforma começou em 2009, mas como mudamos somente no primeiro trimestre de 2010, foi nessa época que percebemos os problemas...toda reforma é embolada! Bem, ainda teremos outros problemas para resolver na casa em 2011, mas enfim, faz parte. Com a reforma da casa, eu acabei me endividando e tive que vender o carro. Apesar disso, passarei o ano novo quase sem dívidas!

No trabalho, a mesma correria tradicional de quem trabalha com TI. Apesar da agitação, foi muito gratificante poder ver alguns grandes obstáculos sendo ultrapassados. Na verdade, eu acho que o número de produtos entregues é a métrica que tem mais impacto na satisfação de quem desenvolve sistemas. Claro que também é muito bom fazer algo que ajuda no trabalho de outras pessoas, mas creio que isso seja mais uma característica de satisfação pessoal do que algo global.

Com o final do mestrado voltei a ter uma vida social. Ok, nem sou tão sociável assim, mas agora eu tenho tempo para ficar com a minha esposa, curtir a minha casa e meus cachorros. Além disso, agora também posso me dedicar um pouco mais à banda (por falar nisso, precisamos arranjar um nome para a banda) e ainda tenho Happy Hours!!!!

O grande aprendizado desse ano de 2010 foi realmente o valor dos amigos! Mais do que nunca eu precisei e pude contar com a ajuda deles. Assim, gostaria de deixar aqui a minha gratidão para todos os meus amigos (que prefiro não mencionar nomes para não esquecer ninguém) e também pedir desculpas por todos os meus momentos de ausência, que não foram poucos. Certamente, não poderia deixar de mencionar a minha esposa, que também é minha amiga, e uma pessoa que vem me apoiando em todas as decisões loucas que venho tomando durante esses anos que estamos casados.

Esse ano de 2011 também promete! Espero conseguir conciliar melhor o meu trabalho com a minha vida social e voltar a fazer algumas coisas que ficaram para trás, como viajar e realmente curtir minha vida. Também vou fazer um esforço para escrever alguns artigos interessantes nesse, até então, pobre blog :-)

Um ótimo 2011 e muito sucesso para todos.