Java 9 na prática: Jigsaw

Há muito tempo se diz sobre modularizar a plataforma Java. É um plano que começou desde antes do Java 7, foi uma possibilidade no Java 8 e por fim, para permitir mais tempo de desenvolvimento, revisão e testes, foi movido para o Java 9.

O projeto Jigsaw, como foi chamado, é composto por uma série de JEPs. Algumas delas inclusive já disponíveis no Java 8, como os conhecidos Compact Profiles. A idéia por trás do projeto não é só criar um sistema de módulos, que poderemos usar em nossos projetos, mas também aplicá-lo em toda a plataforma e JDK em busca de melhor organização e desempenho.

we propose to design and implement a standard module system for the Java SE Platform and to apply that system to the Platform itself, and to the JDK. The module system should be powerful enough to modularize the JDK and other large legacy code bases, yet still be approachable by all developers.

Neste novo post da serie Java 9 prático não só veremos que o projeto já existe e está integrado aos últimos builds, como também vamos explorar e implementar nosso próprio projeto modular. Tudo ao estilo hands on, claro — preparem seus compiladores!

Preparando o seu ambiente para o Java 9 modular

O Jigsaw foi integrado ao build do JDK 9 desde a versão ea+111, portanto tudo que você precisa para usá-lo é baixar uma versão igual ou superior a ela. Eu recomendo bastante que, mesmo que você já tenha uma versão relativamente atual, baixe sempre a última antes de qualquer teste. Você pode fazer o download aqui. A versão que estou usando neste post é do build 134 (mais atual de agora).

Criando um hello world tradicional

No lugar de já começar criando o projeto modular com Jigsaw, vamos criar um projeto da forma tradicional e depois migrá-lo, assim a diferença entre as duas abordagens deve ficar bastante clara. Nos exemplos do post vou fazer tudo pela linha de comando e ir compartilhando os snippets, mas para acompanhar, você pode e deve usar o gerenciador de arquivos do seu sistema operacional e qualquer editor de sua preferência.

Os passos são simples, começando pela criação dos diretórios do projeto e de seus pacotes. O projeto se chamará hello-jigsaw e as classes Java devem ficar no pacote br.com.caelum.jigsaw. Podemos fazer tudo com o comando:

mkdir -p hello-jigsaw/src/br/com/caelum/jigsaw

Claro que, se você preferir, pode criar usando o gerenciador de arquivos do seu sistema operacional favorito. O importante é que, no final, a estrutura de diretórios fique assim:

hello-jigsaw
└── src
     └── br
         └── com
             └── caelum
                 └── jigsaw

Tudo ok. Então agora podemos criar a classe JigSaw.java, dentro de src/br/com/caelum/jigsaw:

package br.com.caelum.jigsaw;

class JigSaw {

	public static void main (String... args) {
		System.out.println("Olá Java 9 modular!");
	}
}

Aqui tem um vídeo com todos os passos desse processo, caso queira dar uma olhada. E também vou deixar um link para fazer download ou navegar pelos arquivos com esse estado do post.

Compilando e executando, ainda da forma atual

Para compilar o projeto podemos usar o comando javac e pronto. Nada de especial aqui.

javac src/br/com/caelum/jigsaw/JigSaw.java 

Feito isso, caso não exista nenhum erro, podemos executar com:

java -cp src/ br.com.caelum.jigsaw.JigSaw

Repare que, como já criamos dentro de um pacote e da pasta source, foi preciso passar o parâmetro -cp (classpath) apontando para ela e também usar o nome completo da classe, que inclui o pacote. Talvez você não esteja tão acostumado com isso, afinal a maioria de nós não compila classes na linha de comando no dia a dia, mas não tem nenhuma novidade por enquanto.

Ao executar, o output, conforme esperado, será a mensagem: Olá Java 9 modular!.

Você pode ver aqui o vídeo do processo de compilação e execução.

Agora finalmente vamos para a parte divertida, que será migrar o projeto para que seja modular!

Migrando para um projeto modular

A migração não é nada complicada. Você precisa basicamente criar uma nova pasta com o nome do seu módulo, que por convenção, é o pacote base do seu projeto. Você pode pensar no nome do módulo como algo parecido com o groupId de um projeto do Maven. É aberto, você pode escrever qualquer coisa, mas idealmente deve seguir uma convenção.

Em nosso caso o módulo será chamado de br.com.caelum.jigsaw:

mkdir src/br.com.caelum.jigsaw

Ok. O próximo passo será mover todos os arquivos do projeto, de dentro da pasta src, para dentro deste novo diretório do módulo. Você pode fazer isso com o comando mv do terminal ou simplesmente mover a pasta br pelo seu gerenciador de arquivos com tudo que tem dentro para o diretório do módulo. Na linha de comando seria assim:

mv src/br/ src/br.com.caelum.jigsaw/

Agora falta criar um arquivo chamado module-info.java, também dentro deste diretório do módulo — tudo ficará nele a partir de agora. Esse arquivo será o responsável pela declaração do módulo e futuramente as suas dependências. A configuração é mínima:

module br.com.caelum.jigsaw {
     // não precisa escrever nada aqui, pelo menos por enquanto
}

Tudo pronto, no final de todos os passos nosso projeto deve ficar assim:

hello-jigsaw
└── src
    └── br.com.caelum.jigsaw
        ├── br
        │   └── com
        │       └── caelum
        │           └── jigsaw
        │               └── JigSaw.java
        └── module-info.java

Vou deixar aqui os links do post nesta fase também, caso queira acompanhar:

[video, download do código, navegar pelo projeto]

Nosso projeto agora é modular! Nada complicado, não é mesmo? Vamos ver agora como compilar e executar um projeto com esse novo formato.

Compilando um projeto modular

O comando javac para um projeto modular é um pouco diferente. Ele terá a seguinte estrutura:

javac -d <1> <2> <3>

Onde <1> é o diretório onde ficará o código compilado, <2> é o caminho para o seu arquivo module-info.java, e <3> é o caminho completo para o seu arquivo .java.

O comando completo para compilar esse nosso projeto ficará assim:

javac -d mods/br.com.caelum.jigsaw 
    src/br.com.caelum.jigsaw/module-info.java
    src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java 

Execute e, se nenhum erro de compilação for exibido, você verá que a nova pasta mods foi criada com todo o código compilado. Inclusive os metadados do module-info. O projeto ficou assim:

hello-jigsaw
└── mods
│   └── br.com.caelum.jigsaw
│       ├── br
│       │   └── com
│       │       └── caelum
│       │           └── jigsaw
│       │               └── JigSaw.class
│       └── module-info.class
└── src
    └── br.com.caelum.jigsaw
        ├── br
        │   └── com
        │       └── caelum
        │           └── jigsaw
        │               └── JigSaw.java
        └── module-info.java

Agora temos o diretório src, com o código fonte, e o diretório mods, com o código compilado.

Executando um projeto modular

Para executar você usará o comando java, como sempre, mas agora com esse formato:

java –module-path <1> -m <2>

Onde <1> é o nome do diretório onde está o código compilado do nosso módulo, ou seja, a pasta /mods. E <2> o nome completo (full qualified name) da classe que será executada. Um detalhe importante é que, agora que estamos trabalhando de forma modular, o nome da classe recebe também o nome do módulo como prefixo, logo antes do nome do pacote.

O comando para executar a classe JigSaw ficará assim:

java –module-path mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw

E a saída, assim como esperado, será o print: Olá Java 9 modular!

Aqui vão os links de [video, download do código, navegar pelo projeto]

Adicionando um alert do Swing

Por enquanto usamos apenas um System.out no projeto, apenas recursos do pacote padrão java.lang. Vamos experimentar usar um importe de alguma outra API do próprio Java pra ver o que acontece? Uma forma simples seria usando um alert do Swing para imprimir a mensagem:

package br.com.caelum.jigsaw;

import javax.swing.JOptionPane;

class JigSaw {

	public static void main (String... args) {
		JOptionPane.showMessageDialog(null,"Olá Java 9 modular!");
	}
}

Maravilha, agora basta compilar utilizando o mesmo comando de antes e ops…

src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java:3: 
error: package javax.swing does not exist
import javax.swing.JOptionPane;
                  ^
src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java:8: 
error: cannot find symbol
		JOptionPane.showMessageDialog(null,"Olá Java 9 modular!");
		^
  symbol:   variable JOptionPane
  location: class JigSaw
2 errors

Você pode ver aqui um video com esses passos e o erro gerado.

O que houve? A mensagem diz que o pacote javax.swing não existe! Claro que o Swing não foi removido no Java 9… mas a partir do momento em que compilamos nosso projeto modular, o compilador só usará os módulos que declararmos como dependência no arquivo module-info.java. Até então não tinhamos nenhuma.

O JRE modular

A primeira coisa que eu fiz ao baixar o build do Java 9 com Jigsaw foi abrir o diretório onde ele está instalado e ver o que mudou. Se você fizer isso aí na sua casa, vai perceber que no lugar da pasta jre/lib, onde ficavam todos os .jars da JRE, você encontrará um diretório jmods com todos os módulos. E não são poucos.

The original goal of this Project was to design and implement a module system focused narrowly upon the goal of modularizing the JDK, and to apply that system to the JDK itself. We expected the resulting module system to be useful to developers for their own code, and it would have been fully supported for that purpose, but it was not at the outset intended to be an official part of the Java SE Platform Specification.

O pensamento foi: se vamos modularizar o próprio JDK e esse sistema de módulos pode ser útil aos desenvolvedores, por que não torná-lo parte oficial do Java SE? E assim foi feito, todo o código foi reorganizado em módulos. Você pode listar todos eles executando o seguinte comando:

java --list-modules

Você pode ver todos na imagem deste link. Um detalhe que você deve ter percebido é que, assim como não era preciso importar o java.lang para usar as classes System, String e outras, você também não precisa declarar nenhum módulo. Essas classes estão presentes no módulo padrão, java.base. Outro detalhe importante é que você não é obrigado a usar Java modular, claro. Você só precisa declarar de quais módulos a sua aplicação depende se ela for modular… caso exista o arquivo module-info.

Declarando a dependência entre módulos

As classes do Swing, como o JOptionPane que usamos, estão em um módulo chamado java.desktop. Para ensinar ao compilador que precisamos desse módulo em nosso projeto, basta declará-lo no arquivo module-info.java. A mudança é simples, o código ficará assim:

module br.com.caelum.jigsaw {
	requires java.desktop;
}

E pronto! Você não precisa de nenhuma configuração adicional, basta usar o mesmo comando para compilar e agora ele saberá de onde vem o pacote javax.swing.

javac -d mods/br.com.caelum.jigsaw 
    src/br.com.caelum.jigsaw/module-info.java
    src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java

Zero erros. Agora executamos o código e o alert será exibido:

java –module-path mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw

Aqui vão os links de [video, download do código, navegar pelo projeto]

É claro que, assim como você não precisa executar os comandos javac e java manualmente na linha de comando hoje, passando o classpath e caminho para as dependências do projeto, você também não vai precisar fazer isso com o projeto modular. As IDEs vão adicionar suporte para que isso fique transparente e para que você não se preocupe tanto com os detalhes.

O post foi um overview rápido sobre um assunto bastante abrangente, então, sem dúvida alguma novos posts entrando em detalhes que não foram discutidos aqui virão em breve. Não deixe de deixar suas perguntas e sugestões de assuntos aqui como comentário, são elas que estão guiando os posts da serie Java 9.

E aí, o que achou do Jigsaw? Tem outros assuntos que gostaria de ler aqui no blog sobre Java 9?

20 Comentários

  1. Icaro 15/09/2016 at 16:56 #

    Senti falta da resposta à pergunta: como voce compararia um projeto baseado nesta modularizaçao, a um projeto baseado em modulos maven (por exemplo)? quais vantagens e desvantagens…

  2. Rodrigo Turini 16/09/2016 at 11:26 #

    Oi Icaro, legal o ponto que você levantou!

    A grande vantagem do modular em Java é que você só vai usar o que precisa da JRE — já que agora ela também é modular e você precisa declarar quais módulos usa em seu projeto!

    Em outras palavras, terá uma JRE pequena só com o que você usa.

    A vantagem disso é uma melhora de desempenho considerável, principalmente na inicialização.

  3. Ricardo Alcantara 19/09/2016 at 10:39 #

    Rodrigo Turini, ótimo hands on. Não sei se na época que você fez esse artigo era diferente mas tive problema construindo o seu exemplo, deu erro quando fui executar:

    java -modulepath mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw

    No site do proprio java descobri que o module path estava errado, o correto é:

    java –module-path mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw

  4. Ricardo Alcantara 19/09/2016 at 10:43 #

    Esqueci de dizer no próprio help do java: java –help, está com erro também:

    -modulepath …
    Uma lista separada por : de diretórios, cada um
    sendo um diretório de módulos.

    Ou eu fiz alguma coisa errado?
    Baixeo o jdk-9-ea+136_linux-x86_bin.tar.gz

  5. Rodrigo Turini 19/09/2016 at 11:08 #

    Oi Ricardo

    Tem razão, isso mudou no build 136! Já atualizei pra `–module-path`.

    Obrigado pelo aviso, assim o post sempre fica atualizado (;

  6. Hugo 05/10/2016 at 15:11 #

    Desculpe a ignorância, mas o propósito do jigsaw é parecido com osgi?

  7. Rodrigo Turini 05/10/2016 at 18:23 #

    Oi Hugo

    É isso mesmo, o JigSaw é uma solução nativa da linguagem para modularização.

    A grande diferença entre ambos é que o JigSaw é usado pra modularizar o próprio JRE. Quando você for criar um projeto modular, com ele, pode escolher apenas os “pedacinhos” da JRE que precisar, diferente da abordagem atual.

    É muito provável que em um futuro próximo as duas soluções possam ser utilizadas em conjunto. OSGI existe a anos e é muito bem consolidado.

  8. Rogerio J. Gentil 06/10/2016 at 11:54 #

    Excelente artigo introdutório. Mas fiquei com uma dúvida em relação a modularização: o projeto Jigsaw vai modularizar o Java e possibilitar que os projetos se tornem menores (mais eficientes em relação ao uso da JRE). Isso possibilitará que os projetos desenvolvidos em Java 9 sejam módulos de um projeto maior?

  9. Rodrigo Turini 06/10/2016 at 15:43 #

    oi Rogério

    Será uma possibilidade sim. Você pode, no lugar de ter 1 único projeto com todo o código, ter vários projetinhos pequenos (módulos) que se integram em um projeto maior.

  10. Junior Sousa 20/10/2016 at 10:24 #

    Olá Rodrigo,

    Você sabe dizer se o Java 9 também terá suporte para modularização de aplicações Web? Ou seja, se vai existir a possibilidade de modularizar um war em outros sub Wars?

  11. Dênis 07/11/2016 at 10:34 #

    Olá Rodrigo,

    Interessante a idéia de modularizar, porém achei meio burocrático, essa ideia de ter q criar uma outra estrutura para q isso funcione. Na minha opinião a solução deveria ser mais convention over configuration. Quem sabe em versões futuras melhorem isso até com utilização de annotations para facilitar nossa vida. O que vc achou?

  12. Rodrigo Turini 07/11/2016 at 20:54 #

    Oi Dênis

    Fazendo assim, manual, realmente parece bastante burocrático!

    mas no fim das contas as IDEs vão fazer o trabalho pesado. Tenho certeza que, pouco tempo depois de sair o JigSaw –ou talvez antes–, as principais do mercado já vão suportar isso e facilitar o trabalho.

    Claro que, com o tempo, novos recursos devem aparecer e como você disse muitas convenções podem ser adotadas, etc, para que tudo fique ainda mais simples (;

  13. marcosalex 28/11/2016 at 23:18 #

    O Netbeans já tem um suporte beta que deixa tudo transparente. Sei que não é a IDE mais popular, mas pra testar a facilidade de programar com módulos ele é ideal pra avaliar.

  14. Tiago 15/12/2016 at 11:54 #

    Java swing não vai ter mais?

  15. Rodrigo Turini 15/12/2016 at 12:40 #

    oi Marco

    boa dica sobre o NetBeans! Como ele é da própria Oracle, costuma estar a frente nessas atualizações de novas features.

    O Intellij também já tem um suporte bem interessante.

  16. Rodrigo Turini 15/12/2016 at 12:42 #

    Oi Tiago

    O Java swing continuará existindo sim! Ele está no módulo *java.desktop*

  17. marcus 10/03/2017 at 18:33 #

    “O pensamento foi: se vamos criar um sistema de módulos na plataforma Java, por que não modularizar o próprio JDK?”

    Pelo texto em inglês, parece que foi o contrário: já que iam modularizar o próprio JDK e esse sistema de módulos seria útil aos desenvolvedores, por que não torná-lo parte oficial do do Java SE?

  18. Rodrigo Turini 13/03/2017 at 10:02 #

    Oi Marcus

    muito bem observado! já atualizei no texto com as suas palavras.

    obrigado pelo aviso

  19. marcus 13/03/2017 at 10:45 #

    Legal, obrigado pela dedicação para atualizar o post!

  20. Raphael 07/04/2017 at 12:29 #

    Caramba… deve ter dado um puta trabalho modularizar a jdk

Deixe uma resposta