[Pesquisar este blog]

segunda-feira, 8 de janeiro de 2018

Java 9::Jigsaw - a nova modularidade da plataforma (Parte III)

O Jigsaw trouxe um novo e interessante sistema de modularização para a versão 9 do Java, o qual soluciona muitos problemas das versões anteriores, além de proporcionar novas facilidades na construção de aplicações.
A parte III deste post traz um exemplo simples que mostra o essencial da construção de aplicações modularizadas, incluindo o uso do jlink. para a criação de imagens executáveis (runtime images), isto é, máquinas virtuais Java customizadas. A parte I abordou o conceito de modularidade e a especificação do novo artefato module trazida pelo Jigsaw; enquanto a parte II discute a implementação do Jigsaw na plataforma Java.
Ao longo deste post, use ';' como separador de diretório no Microsoft Windows e ':' no Unix/Linux. Note que %JAVA_HOME%/jmods é o diretório, no Windows que contém o módulo java.base.jmod (e os demais módulos padrão do JDK). Verifique sua instalação em caso de dúvidas.

Aplicação Simples (módulo único)

Uma aplicação simples é constituída por classes distribuídas em um pacote (ou mais) encapsulados em um único módulo. Além disso, estas classes não serão utilizadas por outras aplicações, ou seja, não serão exportadas para outros módulo, ou, em outros termos, observáveis por outros módulos.

Criaremos uma aplicação no estilo clássico do "Hello World" para começar.

1.
Crie um diretório Jigsaw em um lugar conveniente de seu sistema de arquivos. Entre neste diretório.
md Jigsaw
cd Jigsaw

2.
Crie um subdiretório para o código fonte (src), com subdiretórios para cada pacote e subpacote necessários, usando o seguinte esquema: src\<nome.pacote>\<subdir1>[\<subdir2>...].
Neste exemplo será necessário apenas um pacote (cujo nome longo é apenas ilustrativo).
md src\br.gov.sp.fatec.saudacoes\br\gov\sp\fatec\saudacoes

3.
Neste subdiretório crie o arquivo Main.java com o conteúdo que segue. Este é o código da aplicação simples que estamos construindo.
package br.gov.sp.fatec.saudacoes;

public class Main {
   public static void main(String[] args) {
      System.out.println("Olá Jigsaw!");
   }
}

4.
Navegue para o subdiretório que denomina o pacote (src\br.gov.sp.fatec.saudacoes):
cd ..\..\..\..\..

5.
Neste subdiretório crie o arquivo module-info.java, que é o descritor do módulo br.gov.sp.fatec.saudacoes, com o conteúdo que segue.
module br.gov.sp.fatec.saudacoes {
}

6.
Navegue para o diretório inicial (Jigsaw):
cd ..\..

7.
Crie um subdiretório para os módulos compilados (modules).
md modules\br.gov.sp.fatec.saudacoes


8.
Compile os arquivos de cada subpacote (apenas um neste exemplo).
(O comando abaixo foi separado em várias linhas para melhorar a legibilidade, mas deve ser fornecido em uma única linha!)
javac -d modules\br.gov.sp.fatec.saudacoes
      src\br.gov.sp.fatec.saudacoes\br\gov\sp\fatec\saudacoes\Main.java

A opção -d é seguida do diretório onde o código binário das classes deve ser armazenado, respeitando a estrutura de pacotes. O último argumento é o nome do arquivo que será compilado.

9.
Compile o arquivo de informação de cada módulo (apenas um neste exemplo).
(O comando abaixo foi separado em várias linhas para melhorar a legibilidade, mas deve ser fornecido em uma única linha!)
javac -d modules\br.gov.sp.fatec.saudacoes
      src\br.gov.sp.fatec.saudacoes\module-info.java

10.
É possível usar a JVM para listar os módulos disponíveis.
java --module-path modules --list-modules
A opção --module-path é seguida de uma lista de diretórios que contém os módulos não integrantes do JDK/JRE. A opção --list-modules determina a ação a ser tomada pela JVM.

11.
E também usar a JVM para obter informação de módulos observáveis.
(O comando abaixo deve ser fornecido em uma única linha!)
java --module-path modules
     --describe-module br.gov.sp.fatec.saudacoes

Aqui a opção --module-path é seguida de uma lista de diretórios que contém os módulos não integrantes do JDK/JRE. Já a opção --describe-module deve ser seguida do nome do módulo que deve ser descrito.
O resultado deve ser como segue:
br.gov.sp.fatec.saudacoes file:///C:/Users/Jandl/workspace/Jigsaw/modules/br.gov.sp.fatec.saudacoes/
requires java.base mandated
contains br.gov.sp.fatec.saudacoes

Observe que o módulo br.gov.sp.fatec.saudacoes requer (obrigatoriamente) o módulo java.base, apesar de seu descritor não explicitar tal necessidade.

12.
Para executar a aplicação simples, organizada como um módulo, usamos o comando que segue.
(O comando abaixo deve ser fornecido em uma única linha!)
java --module-path modules
     -m br.gov.sp.fatec.saudacoes/br.gov.sp.fatec.saudacoes.Main

A opção --module-path é seguida de uma lista de diretórios que contém os módulos não integrantes do JDK/JRE; enquando a opção -m deve ser seguida pelo nome do módulo e da classe que deve ser executada, mas sempre usando o separador '/'.
O resultado deve ser como segue:
Olá Jigsaw!
A listagem dos módulos disponíveis, conforme o passo 10, deve ser semelhante à figura que segue.
O resultado da aplicação simples construída, executada como indicado no passo 12, deve ser como na próxima figura.

Este exemplo mostrou como construir uma aplicação simples, constituída de um único módulo, com o novo sistema de modularização da plataforma Java.

Aplicação e Módulo-Recurso

Uma situação bastante mais típica é uma aplicação constituída de vários módulos. Nestes casos, sempre existe um módulo principal e outros módulos que se comportam como recursos do módulo principal.

Nesta parte do exemplo, criaremos uma aplicação, ainda no estilo clássico do "Hello World", mas que utiliza uma classe auxiliar de um pacote residente em um outro módulo, ou seja, a aplicação utiliza um módulo adicional como recurso.

1.
Navegue para o diretório inicial (Jigsaw).

2.
Crie novos subdiretórios para o novo pacote jandl.tecnopode.
md src\jandl.tecnopode\jandl\tecnopode

3.
Navegue para o subdiretório src\jandl.tecnopode\jandl\tecnopode.
cd src\jandl.tecnopode\jandl\tecnopode

4.
Neste subdiretório crie o arquivo Saudacoes.java com o conteúdo que segue.
package jandl.tecnopode;

public class Saudacoes {
   private String message;

   public Saudacoes() {
      this("Ola Jigsaw!");
   }

   public Saudacoes(String msg) {
      setMessage(msg);
   }

   public String getMessage() {
      return message;
   }

   public void setMessage(String msg) {
      message = msg;
   }

   @Override
   public String toString() {
      return getMessage();
   }
}

4.
Navegue para o subdiretório que denomina o pacote (src\jandl.tecnopode):
cd ..\..

5.
Neste subdiretório crie o arquivo module-info.java, que é o descritor do módulo jandl.tecnopode, com o conteúdo que segue.
module jandl.tecnopode {
   exports jandl.tecnopode;
}

6.
Navegue para o diretório inicial (Jigsaw).
cd ..\..

7.
Compile os arquivos do novo pacote e do descritor do módulo.
(Forneça os comandos que seguem numa única linha!)
javac -d modules\jandl.tecnopode
      src\jandl.tecnopode\jandl\tecnopode\Saudacoes.java
javac -d modules\jandl.tecnopode
      src\jandl.tecnopode\module-info.java

É possível compilar tudo junto:
javac -d modules\jandl.tecnopode
      src\jandl.tecnopode\jandl\tecnopode\Saudacoes.java
      src\jandl.tecnopode\module-info.java

8.
Verifique se o novo módulo é observável pela JVM com:
java --module-path modules --describe-module jandl.tecnopode

9.
Navegue até o diretório do subpacote src\br.gov.sp.fatec.saudacoes.
cd src\br.gov.sp.fatec.saudacoes\br\gov\sp\fatec\saudacoes

10.
Neste subdiretório crie o arquivo Main2.java, que corresponde a nova aplicação, com o conteúdo que segue.
package br.gov.sp.fatec.saudacoes;
import jandl.tecnopode.Saudacoes;

public class Main2 {
   public static void main(String[] args) {
      Saudacoes sds = new Saudacoes("Ola novamente Jigsaw!");
      System.out.println(sds);
   }
}

Observe que a classe Saudacoes, do novo pacote jandl.tecnopode, é importada como usualmente feito.

11.
Navegue para o diretório inicial (Jigsaw).
cd ..\..\..\..\..\..\..

12.
Compile a nova aplicação (Main2.java).
(O comando abaixo deve ser fornecido em uma única linha!)
javac --module-path modules
      -d modules/br.gov.sp.fatec.saudacoes
      src/br.gov.sp.fatec.saudacoes/br/gov/sp/fatec/saudacoes/Main2.java
src\br.gov.sp.fatec.saudacoes\br\gov\sp\fatec\saudacoes\Main2.java:2: error: pac
kage jandl.tecnopode is not visible
import jandl.tecnopode.Saudacoes;
            ^
  (package jandl.tecnopode is declared in module jandl.tecnopode, but module br.
gov.sp.fatec.saudacoes does not read it)
1 error

O erro obtido se deve ao fato do módulo da aplicação não ler/importar o módulo recurso.

13.
Navegue para o subdiretório inicial do pacote br.gov.sp.fatec.saudacoes.
cd src\br.gov.sp.fatec.saudacoes

14.
Edite o descritor de módulo module-info.java como segue, para indicar a importação do módulo que contém os recursos necessários.
module br.gov.sp.fatec.saudacoes {
   requires jandl.tecnopode;
}

15.
Navegue para o diretório inicial (Jigsaw).
cd ..\..\

16.
Compile novamente a nova aplicação (Main2.java) e também o descritor de seu módulo.
(O comando abaixo deve ser fornecido em uma única linha!)
javac --module-path modules
      -d modules/br.gov.sp.fatec.saudacoes
      src/br.gov.sp.fatec.saudacoes/br/gov/sp/fatec/saudacoes/Main2.java
      src/br.gov.sp.fatec.saudacoes/module-info.java

17.
Execute a nova aplicação.
(O comando abaixo deve ser fornecido em uma única linha!)
java --module-path modules
      -m br.gov.sp.fatec.saudacoes/br.gov.sp.fatec.saudacoes.Main2

O resultado da nova aplicação é como o que segue.
Ola novamente Jigsaw!

Este segundo exemplo mostrou como construir uma aplicação ainda simples, embora mais realista, pois é constituída de um módulo que utiliza recursos de outro módulo. 

Na sequência serão exploradas outras possibilidade que exibem algumas das vantagens do novo sistema de modularização.

Empacotamento dos módulos

Até agora, a compilação dos módulos construídos gerou uma árvore de subdiretórios contendo o código binário das classes distribuídos em seus respectivos subdiretórios. Em nosso projeto o subdiretório Jigsaw\src contém a árvore do código fonte dos tipos pertencentes a cada um dos módulos criados, enquanto Jigsaw\modules contém a árvore do código binário dos respectivos tipos. Claramente esta estrutura é pouco adequada para distribuição do código das aplicações.

A ferramenta de linha de comando jar, que existe desde a primeira versão do Java, foi atualizada para possibilitar a criação de módulos, ou seja, permite empacotar os tipos e descritor de um módulo na forma de um arquivo JAR.

Convém criar um novo subdiretório no projeto para conter os módulos empacotados a partir do diretório inicial (Jigsaw).
md library

Para criar um módulo, use a ferramenta jar como segue:
(O comando abaixo deve ser fornecido em uma única linha!)
jar --create
    --file=library/jandl.tecnopode@1.0.jar
    --module-version=1.0
    -C modules/jandl.tecnopode .

A opção --create é autoexplicativa; --file determina o caminho e o nome desejado para o módulo que será criado; --module-version especifica a versão do módulo; -C indica o diretório onde existe a árvore do código binário do módulo.

jar --create
    --file=library/br.gov.sp.fatec.saudacoes@1.0.jar
    --main-class=br.gov.sp.fatec.saudacoes.Main2
    -C modules/br.gov.sp.fatec.saudacoes .

Aqui deve ser observado o uso da opção --main-class seguida do nome da classe principal do módulo (aquela que é executada por padrão).

A figura que segue ilustra o resultado do empacotamento dos módulo br.gov.sp.fatec.saudacoes e jandl.tecnopode.


Com o empacotamento, a distribuição de uma aplicação se resume ao envio dos módulos necessários, que podem ser usados em qualquer outra instalação do Java 9. Para executar a aplicação, basta fornecer:
java -p library -m br.gov.sp.fatec.saudacoes

A opção -p indica uma lista de diretórios que contém os módulos necessários; enquanto -m especifica o módulo que (classe principal) terá o início executado, como mostra a figura que segue.


A ferramenta jar também pode ser usada para descrever um módulo empacotado, como exemplificado nos comandos que seguem.

jar --describe-module --file=library/jandl.tecnopode@1.0.jar
jar --describe-module --file=library/br.gov.sp.fatec.saudacoes@1.0.jar


Criação de Imagem Executável da Aplicação

Uma novidade muito interessante do Java 9 é a possibilidade de criação de imagens executáveis (runtime images) de aplicações. Uma imagem executável contém uma JVM, os módulos específicos de uma aplicação e todas as suas dependências, inclusive as transitivas (decorrentes da execução), permitindo que a aplicação seja executada em qualquer ambiente compatível, independente destes possuírem ou não instalações compatíveis do Java.

A JVM provida numa imagem executável contém apenas os módulos necessários para a aplicação, ou seja, aqueles não utilizados não são inclusos, reduzindo seu tamanho, constituindo um ambiente customizado e independente, pois não requer a presença de um JRE ou JDK para sua execução.

Apenas a arquitetura do sistema deve ser compatível, ou seja, as imagens executáveis construídas para uma arquitetura específica de SO só podem ser utilizadas em tais SOs, tal como ocorre com programas executáveis gerados para o Microsoft Windows, que não são compatíveis com SO Linux e outros.

Ainda assim, a distribuição de uma imagem executável pode simplificar muito seu uso, pois seus usuário não precisam se preocupar com os pré-requisitos de instalação necessários.

O jlink é a nova ferramenta de linha de comando capaz de criar imagens executáveis, como no exemplo de uso que segue.
(O comando abaixo deve ser fornecido em uma única linha!)
jlink --module-path %JAVA_HOME%\jmods;library
      --add-modules br.gov.sp.fatec.saudacoes
      --output jigsawDemoApp

A opção --module-path é seguida pela lista de diretórios de módulos necessários (no caso o subdiretório jmods na instalação local do Java 9 e o subdiretório library do nosso projeto). A opção --add-modules indica o módulo da aplicação que será adicionado na imagem. A opção --output indica o subdiretório onde a imagem obtida será gerada. Note que as dependências do módulo br.gov.sp.fatec.saudacoes (ou seja, os módulos java.base e jandl.tecnopode) são automaticamente identificadas e adicionadas à imagem.

A imagem gerada terá a seguinte estrutura:
jigsawDemoApp
├───bin
│   └───server
├───conf
│   └───security
│       └───policy
│           ├───limited
│           └───unlimited
├───include
│   └───win32
├───legal
│   └───java.base
└───lib
    ├───security
    └───server

Em sua raiz existe um arquivo release que contém a versão do Java e os módulos inclusos na imagem executável. No diretório lib encontramos o arquivo modules com apenas 21.8MB, bem menor que o correspondente da JVM completa da versão 9.

Para executar a aplicação encapsulada na imagem executável, basta fornecer o comando que segue:
jigsawDemoApp\bin\java -m br.gov.sp.fatec.saudacoes/br.gov.sp.fatec.saudacoes.Main2

Nele utiliza-se a JVM da própria imagem, seguida da opção -m e da combinação do nome do módulo (br.gov.sp.fatec.saudacoes), do separador '/' e do nome da classe principal  da aplicação (br.gov.sp.fatec.saudacoes.Main2).

Também é possível solicitar que a ferramenta jlink gere um pequeno script de inicialização, mais fácil de ser usado. Para criar a imagem executável e também gerar tal script, o comando adequado é:
jlink --module-path %JAVA_HOME%\jmods;library
      --add-modules br.gov.sp.fatec.saudacoes
      --output jigsawDemoApp
      --launcher launch=br.gov.sp.fatec.saudacoes/br.gov.sp.fatec.saudacoes.Main2

Observe a adição da opção --launcher launch=<modulo>/>classePrincipal>.

A ativação da aplicação agora pode ser feita de maneira bem mais simples com:
jigsawDemoApp\bin\launch

Conclusões

Os exemplos deste post, embora muito simples, mostram como utilizar de maneira proveitosa o novo sistema de modularização do Java. Em sua primeira parte mostra como criar módulos (modules). Na segunda parte mostra como construir aplicações constituídas de vários módulos.

Além disso, estes exemplo permitem perceber como a distribuição de aplicações é simplificada tanto pelo uso de módulos, como pela criação de imagens executáveis (runtime images). Módulos podem ser distribuídos diretamente, permitindo o reuso em diferentes aplicações, funcionando como componentes. No entanto o uso dos módulos requerem um ambiente de execução Java  previamente instalado. Já a distribuição de imagens executáveis é muito conveniente, pois não requerem ambientes de execução pré-instalados, pois já contém uma JVM própria. Além disso, as imagens executáveis permitem a customização das JVM para aplicações específicas, o que é algo bem interessante.

Isto tudo permite afirmar que o aguardado projeto Jigsaw e seu novo sistema de modularização é, de fato, uma valiosa adição na plataforma Java!


Para Saber Mais


Um comentário:

Fredy Ramos disse...

MUUUUUUUUUUUUUUUUUUUUUUITO BOM. Foi direto ao ponto. Unico artigo que ensinou de forma clara. muito obrigado.