Jshell e Read-Eval-Print Loop (REPL)

Atualmente, estou me preparando para o exame OCP 1Z0-813  e venho aqui falar  um pouco mais sobre um recurso que está sendo muito útil para meus estudos: o Jshell do JDK9!


O Jshell é uma proposta de implementar uma ferramenta de programação interativa, conhecida como Read-Eval-Print Loop (REPL), que continuamente lê a entrada do usuário, avalia, e escreve resultado ou a descrição da mudança de estado causada pela entrada. Linguagens como Python, Ruby, Javascript possuem REPLs que permitem a escrita de pequenos programas e até mesmo uma introdução rápida na linguagem.

Estas características ajudaram na popularidade destas linguagens no ensino de linguagem de programação e até algoritmos. Sendo assim, na próxima versão do Java teremos o REPL pelo Jshell para, quem sabe, motivar a utilização do Java no ensino.

Exemplo do Jshell

Um dos itens de avaliação da OCP é a manipulação dos atributos de arquivos pela API do NIO2. No uso da nova API somos testados sobre coisas do tipo:

  • Qual o tipo do arquivo? Um link? Arquivo regular? um diretório?
  • Qual a visibilidade e acessibilidade do arquivo? Ele é oculto? É executável?
  • Qual o usuário que possui privilégios no arquivo?

Estas e outras perguntas são expostas em métodos da API Files incluída ainda no Java 7 por meio de vários métodos helpers. Caso deseje entender um pouco das razões para criação desta API e sua estruturação, aqui e aqui tem um link interessante.

Voltando ao JShell, o primeiro passo é iniciá-lo é pela execução do comando jshell:

Ao tentar executar o método Paths.get, na linha 6, nos deparamos com um erro de “cannot find symbol“! Um erro que indica que a classe não é reconhecida em tempo de compilação. Isso ocorre devido o pacote java.nio.file não ser importado por padrão no JShell.

Para importar um pacote podemos fazer da mesma forma de código Java pela instrução import. O bacana do jshell que ao apertar TAB um autocomplete é apresentado! Os pacotes importados podem ser visualizados pelo comando /imports

Após importar o pacote, podemos utilizar as classes presentes no java.nio.file apenas chamando a instrução desejada;

Na linha 02-03 é apresentado a saída (Print) do resultado da avaliação (Eval) da nossa entrada (Read). Como o método Paths.get retorna uma instância de Path, o shell armazena o valor do resultado automaticamente em uma variável $N onde N é incrementado a cada execução. Podemos utilizar o objeto $2 para chamar qualquer método de Path ou passar ele como parâmetro para outros métodos, por exemplo:

Na linha 01, após digitar TAB, os métodos disponíveis na variável $2 são apresentados em autocomplete. Na linha 07, invocamos o método getFileName da interface Path. Por fim, na linha 11, testamos se o caminho do arquivo é um diretório pela chamada do método Files.isDirectory. Neste último caso, armazenamos o retorno da chamada em uma variável mais amigável.

Conclusão

O Jshell é uma ótima ferramenta para testes rápidos de API e com certeza irá trazer muitos benefícios aos desenvolvedores Java! Este artigo apresentou o básico da nova ferramenta e, caso você tenha gostado e deseja conhecer outras funcionalidades na prática, realize o download do acesso antecipado ao JDK 9 e bons estudos!

Aplicação prática do Padrão Proxy

A ideia do padrão Proxy é simples,  dado um objeto do nosso sistema com um método foo, ao realizar uma chamada pojo.foo(), o método foo do POJO é acessado diretamente

Para aplicar o padrão precisamos inserir um objeto intermediário, que será do mesmo tipo do objeto pojo – implementando as mesmas interfaces ou herdando da mesma classe do pojo – que irá receber todas as chamadas aos métodos e realizar as devidas verificações e processamentos.

Para criar este objeto intermediário, devemos implementar uma classe com a devida equivalência de tipos ou, podemos criar dinamicamente uma classe que implemente as devidas interfaces/super classe.

Aplicação Prática do Proxy

Na linguagem Java,  é possível criar os chamados proxies dinâmicos por meio da API de Proxy. Porém, esta forma apresenta algumas restrições, pois permite apenas a criação de Proxy de interfaces. Ou seja, nossos objetos devem obrigatoriamente implementar uma interface comum  (Que é criada dinamicamente pelas interfaces informadas em tempo de execução).

Um cenário onde o padrão é aplicado é em um Pool de Conexões.  Ao obter uma conexão de uma fonte de dados (java.sql.DataSurce), a aplicação realiza suas leituras/escritas no banco de dados e, quando não é mais necessária,  fecha a conexão pela chamada ao método close da interface java.sql.Connection. A imagem abaixo apresenta o ciclo de vida de uma conexão em um pool.

Sendo assim, para controlar esta devolução da conexão ao pool, e não fechar a conexão de fato, é possível utilizar o padrão proxy. O exemplo de código apresentado a seguir é baseado no projeto flexy-pool.

A linha 0 1-6 temos a criação do Proxy dinâmico por meio do método estático java.lang.reflect.Proxy#newProxyInstance. O primeiro argumento é o classloader onde o proxy criado será carregado, normalmente é passado o mesmo classloader do objeto this. Já o segundo, é a lista de interfaces que serão implementadas dinamicamente pela instância do proxy. Estas interfaces  definem a tipagem do objeto criado, ou seja,  quais métodos serão possíveis invocar e, quais tipos ele pode ser atribuído. Em nosso exemplo, a interface javax.sql.Connection.

O último parâmetro é uma instância da interface InvocationHandler, implementada pela classe ConnectionInvocationHandler em nosso exemplo. Cada instância de Proxy deve possuir um Invocation Handler associado. Quando qualquer método é chamado em uma instância do Proxy,  a chamada é encaminhada para o método invoke do Invocation Handler, que recebe informações de qual método do foi chamado e quais foram os parâmetros fornecidos. Com o uso desta classe, podemos intermediar a comunicação com o objeto encapsulado.

A implementação do método apresentado faz com que, quando um método close for chamado pela aplicação, a conexão seja devolvida ao pool. A lógica que implementa este algoritmo é delegada para uma instância de ConnectionCallback, uma interface própria do flexy-pool. Quando qualquer outro método da interface Connection for invocado, o invocation handler irá delegar a chamada para o objeto encapsulado, que ocorre  na linha 23, via API de reflexão.

Considerações finais

É possível aplicar proxies em muitos outros cenários, para citar algumas aplicações reais, temos o módulo spring-aop, do Spring framework, que utiliza como base para criação dos Aspectos. Um exemplo bacana do uso de proxies no Spring framework é utilizado pelo projeto Spring Data para transformar nossas interfaces de Repositório – “magicamente” – em algo funcional. Por falar em “data”, o Hibernate utiliza para implementar a recuperação de informações Lazy-loading. Outro exemplo é encontrado nos frameworks de testes, tais como Mockito, utilizam proxies para criação de objetos Mocks.

Por fim, caso seja desejado criar proxies de classes, podemos fazê-lo utilizando bibliotecas de terceiros, como a cglib, javassist e byte buddy que são responsáveis por outras atividades além da criação de proxies, em especial, manipulação de bytecode.