[Pesquisar este blog]

quinta-feira, 12 de janeiro de 2017

JavaScript na JVM

O uso do JavaScript tem crescido consistentemente nestes últimos anos, tanto que a linguagem não apenas figura, mas aparece bem posicionada nos mais conhecidos rankings de utilização de linguagens de programação (TIOBE Index, GitHub PYPL, RedMonk, IEEE Spectrum, etc).


Não é preciso justificar porque o JavaScript é importante. Introduzido em 1995 por Brendan Eich, sob o nome original Mocha, acabou sendo renomeado para JavaScript para aproveitar o sucesso inicial do Java, lançado no mesmo ano. Sua característica mais marcante é trazer interatividade a páginas HTML, sendo executada pelo browser, ou seja, operando do lado do cliente.

Com o passar do tempo e apesar das críticas de muitos programadores, o JavaScript evoluiu e hoje permite o controle sofisticado do conteúdo de páginas web, permitindo a troca de estilos, a ocultação/exibição de elementos, a realização de validações e outras operações mais complexas.

A partir de 2005, com o advento do AJAX (Asynchronous JavaScript and XML), ocorre seu renascimento do JavaScript, e surgem frameworks importantes como Prototype, JQuery, Dojo e Mootools, assim como esforço consistente para sua padronização (ECMAScript 3.1 a 5.1). Hoje o JavaSript é uma linguagem de programação amplamente utilizada.

Nashorn

O Nashorn é uma implementação de um mecanismo (ou motor) de execução para linguagem de programação JavaScript integrado a plataforma Java, disponível a partir da versão 8, compatível com a especificação ECMAScript 262 v5.1, que se beneficia da enorme quantidade de ferramentas e bibliotecas disponíveis para JavaScript, possibilitando sua integração com a plataforma Java e, portanto, conjugando as vantagens destes dois mundos.

Até a versão 7 do Java, o mecanismo de execução padrão do JavaScript disponível no Java era o Rhino, baseado num projeto de mesmo nome voltado para os navegadores Mozilla. Apenas como curiosidade, a palavra nashorn significa rinoceronte em alemão e também denominou um tanque de guerra conhecido como destruidor de tanques.

O mecanismo de execução Nashorn é acessível por meio da API javax.script e da ferramenta de linha de comando jjs, suportando a execução de scripts escritos em JavaScript embutidos em programas Java, incluindo aplicações JavaFX.

A sintaxe da linguagem JavaScript é bastante semelhante a do Java e o acesso a elementos Java a partir do JavaScript, como arrays e objetos, é praticamente idêntico, de modo que o Java tem acesso a valores e elementos do JavaScript e vice-versa.

Programa Java executa script JavaScript

O exemplo que segue (classe Nashorn01) mostra um programa de console Java que utiliza o Nashorn para executar um comando JavaScript.

import javax.script.*;

public class Nashorn01 {
    public static void main(String[] arg) {
        // Obtem acesso ao mecanismo de execucao JS Nashorn
        final ScriptEngine nashorn =
               new ScriptEngineManager().getEngineByName("nashorn");
        try {
            // Script JS pode ser hardcoded ou
            // obtido por outros meios (p.e., arquivo)
            String script = "print('Hello World!')";
            // Execucao do script JS
            nashorn.eval(script);
        } catch (final ScriptException se) {
            System.out.println(se);
        }
    }
}

Na classe Nashorn01, uma instância de ScriptEngineManager é criada para que, por meio de seu método getEngineByName(String) e o nome "nashorn", se possa obter uma instância do mecanismo de execução correspondente ao Nashorn. Com o objeto ScriptEngine obtido é possível executar-se diretivas JavaScript individuais ou scripts inteiros com uso do método eval(String). É necessário monitorar tal avaliação com um try/catch.

Retorno de valor do script JavaScript para Java

Neste outro exemplo (classe Nashhor02), o script é lido de um arquivo cujo nome é fornecido como argumento para o programa. O mecanismo de execução Nashorn é obtido da mesma maneira, mas sua utilização é um pouco diferente.

import java.io.*;
import javax.script.*;

public class Nashorn02 {
    public static void main(String[] arg) {
        // Script obtido do arquivo indicado como argumento
        StringBuffer script = new StringBuffer();       
        try (BufferedReader br =
                 new BufferedReader(new FileReader(arg[0]))) {
            String line = null;
            while ( (line=br.readLine()) != null) {
                script.append(line);
                script.append("\n");
            }
        } catch (Exception exc) {
            System.out.println("Erro: " + exc);
            System.exit(-1);
        }
        try {
            // Obtem acesso ao mec. execucao JS Nashorn
            final ScriptEngine nashorn =
             = new ScriptEngineManager().getEngineByName("nashorn");
            // Execucao do script JS
            Object returnValue = nashorn.eval(script.toString());
            System.out.println("Java: " + returnValue);
        } catch (final ScriptException se) {
            System.out.println(se);
        }
    }
}

O resultado do método eval(String) é atribuído a uma variável Java do tipo Object, de modo que é possível transferir um resultado do script Javascript para o programa Java. Basta para isso que a variável cujo valor deseja-se retornar seja indicada como uma diretiva ao final do script, como no script simples que segue, onde a variável f tem seu valor retornado para o programa Java.

// teste01.js
var c = 33;
print('JavaScript: ' + c);
var f = c*9.0/5.0 + 32.0;
print('JavaScript: ' + f);
f; // retorno de valor

O valor retornado pode ser convertido em outros tipos por meio de operações de coerção, no caso, como o resultado é do tipo double, poderia ser aplicado:
double resultValue = (Double) nashorn.eval(script.toString);

Passagem de valores entre programa Java e script JavaScript

Também é possível passar valores do programa Java para o script Javascript com o uso do método put(String, Object) disponível na classe ScriptEngine. que define uma variável com o valor indicado (ou global variable binding). O exemplo que segue transfere dois valores do programa Java para o script, que soma tais valores, retornando tal resultado para o programa, usando o método get(String).

import javax.script.*;

public class Nashorn03 {
    public static void main(String[] arg) {
        // Obtem acesso ao mecanismo de execucao JS Nashorn
        final ScriptEngine nashorn =
               new ScriptEngineManager().getEngineByName("nashorn");
        // Script JS hardcoded
        String script =
               "var c = a + 2*b;\nprint('JavaScript: ' + c);";
        try {
            // Transferência de valores: programa -> script
            nashorn.put("a", 123);
            nashorn.put("b", 0.456);
            // Execucao do script JS
            nashorn.eval(script);
            // Transferência dos resultados: script -> programa
            double result = (Double) nashorn.get("c");
            System.out.println("Java: " + result);
        } catch (final ScriptException se) {
            System.out.println(se);
        }
    }
}

Script JavaScript que utiliza classes Java

Outra possibilidade interessante decorrente da integração do mecanismo de execução Nashorn na JVM é o compartilhamento da API Java por parte dos script JavaScript. Um objeto Java pode ser instanciado de duas formas:
  • Instanciação direta:var lista = new java.util.ArrayList();
  • Instanciação indireta:var AL = java.util.ArrayList;
    var lista = new AL;



As duas formas produzem o mesmo efeito. O que muda é a quantidade de código digitado: a instanciação direta é mais verborrágica, portanto, mais longa, no entanto, clara e direta; a instanciação indireta permite instanciar objetos com menos código, embora possa ser menos clara conforme os apelidos usados para as classes efetivamente usadas.

O uso dos objetos é como no Java, tal como mostrado no exemplo que segue, onde um script JavaScript utilizar uma tabela de hashing, um array elástico e um buffer para String para manipular uma lista de linguagens de programação e seus inventores.

// teste02.js
var HM = java.util.HashMap; // instanciação indireta
var map = new HM();
var list = new java.util.ArrayList(); // instanciação diretas
var buffer = new java.lang.StringBuffer("|");

map.put("JavaScript","Brendan Eich");
map.put("Java","James Gosling");
map.put("C#","Anders Hejlsberg");
map.put("Pascal","Niklaus Wirth");

for (var key in map) { print('key: ' + key); list.add(key); }
for each (var value in map) {
    print('value: ' + value);
    buffer.append(value); buffer.append("|");
}
for each (var key in list)
    print('{ ' + key + ', ' + map.get(key) + ' }');

print(buffer.toString());
list // retorno de objeto


Este script, salvo sob o nome de teste02.js, é executado com uso da classe/exemplo Nashorn02, dada anteriormente.

Console JavaScript


Junto com o JDK 8 existe um console JavaScript (uma ferramenta de linha de comando) denominada jjs que provê acesso direto ao mecanismo de execução Nashorn, comportando-se como um REPL (Read-Evaluate-Print-Loop), ou seja, um console que lê comandos JavaScript, executando-os e imprimindo os resultados no próprio console, interativa e repetidamente, o que é muito conveniente para testar comandos, executar pequenas rotinas e testes, incluindo experimentar elementos Java e JavaScript sem a necessidade de escrever programas completos.

Para acioná-la basta digitar 'jjs' (considerando que a variável de ambiente path está corretamente configurada para incluir o diretório <java_install>/bin). Considere as várias sequências que seguem de comandos que podem ser digitados no jjs. A primeira define variáveis e exibe o resultado de uma expressão:

jjs> var a = 1.5;
jjs> var b = -3;
jjs> print (2*a - 0.5*b);
4.5

Agora a definição e a exibição de um array simples:

jjs> var array = [];
jjs> array[0] = 'Peter';
Peter
jjs> array[1] = 2017;
2017
jjs> array
Peter,2017
jjs> array.length  
2
jjs> for(var i=0; i<array.length; i++) print(array[i]);
Peter
2017
jjs> for each (var v in array) print(v);
Peter
2017

Para encerrar o jjs deve ser fornecido quit().
Objetos Java podem ser instanciados e utilizados, como segue:

jjs> var list = new java.util.ArrayList();
jjs> list.add('Peter');
true
jjs> list.add('Jandl');
true
jjs> list
[Peter, Jandl]
jjs> list.size();
2
jjs> var i = 10;
jjs> for each (var v in list) { print(i + ':' + v); i = i + 10; }
10:Peter
20:Jandl
30

Características avançadas do Java, como expressões lambda e streams também podem ser usadas (com alguns cuidados). Considerando o ArrayList list definido acima, poderia ser escrito:

jjs> list .stream() .filter(function(s) s.startsWith('J')) .forEach(function(s) print(s));
Jandl
null

Neste fragmento devem ser observados alguns aspectos:
  • onde é possível usar uma expressão lambdas no Java, emprega-se uma função JavaScript; de maneira que o lambda Java
    "s -> s.startsWith('J')"
    torna-se
    "function(s) s.startsWith('J')";
  • o valor 'Jandl' é o valor resultante da exibição dos elementos filtrados do stream obtido da coleção list;
  • como o método forEach(Consumer) é terminal, seu valor de retorno é void, assim sua avaliação pelo jjs retorna null.


O modo interativo do jjs é bastante útil para experiências e testes diversos. Mas não é conveniente quando desejamos executar (ou repetir) uma sequência conhecida (ou longa) de comandos. Um conjunto de comandos JavaScript, com ou sem uso de elementos do Java, pode ser salvo num arquivo de script, o qual pode ser executado pelo jjs em seu modo de interpretação (ou batch). Para executar o script teste02.js basta escrever:

> jjs –scripting teste02.js

Conclusões

Dada a importância atual do JavaScript, sua utilização integrada à plataforma Java é bastante interessante. Assim o Nashorn é bastante útil ao permitir que programas Java utilizem funcionalidades JavaScript; que programas JavaScript compartilhem elementos do Java; que o desenvolvedor disponha de um console JavaScript para testes diversos, execução de tarefas rápidas, prototipação, teste de características do Java e construção de scripts usando Java e JavaScript combinados.
Atualmente o Nashorn (Java 8) suporta completamente o padrão ECMAScript 5.1, além de algumas extensões. Pretende-se que com o Java 9 o Nashorn seja atualizado para suportar o ECMAScript 6. Tudo muito útil e conveniente.

Para Saber Mais


quinta-feira, 5 de janeiro de 2017

Além do Java na JVM

Já se foi o tempo em que a Java Virtual Machine - JVM – era dedicada exclusivamente ao Java. Como a especificação da máquina virtual é pública, muitos desenvolvedores criaram linguagens de programação compiladas em bytecode, o formato próprio da JVM, permitindo que seus programas sejam executados em uma JVM.


Essa estratégia:
  • permite que tais linguagens sejam tão portáteis quanto o Java, pois os programas construídos com estas linguagens podem ser executados em qualquer plataforma onde exista uma 
  • possibilita que estas linguagens tenham características que o próprio Java não possui, servindo como um ambiente de pesquisa; e
  • constitui uma alternativa para que linguagens existentes sejam portadas para a JVM, aumentando as possibilidades de sua utilização.
Em algumas versões a linguagem de programação Java recebeu melhorias substanciais, como, por exemplo, os genéricos na versão 5; ou as expressões lambda, as referências para métodos e a API Stream da versão 8. Mas além disso não ser uma constante cada versão, seria impossível satisfazer todos os grupos, opiniões e necessidades da comunidade. Daí que este uso alternativo da JVM é tanto atraente como conveniente.

Assim, surgiram nestes últimos anos várias linguagens de programação novas voltadas para a JVM, como as conhecidas Scala e Groovy, mas existem outras como Xtend, Ceylon, Kotlin, X10 e Fantom.

Da mesma maneira, linguagens consagradas como o Python e Lisp estrearam na JVM respectivamente com Jython e Closure. O JRuby é um outro exemplo de linguagem portada para a JVM.

Neste post falaremos um pouco sobre o Scala e o Groovy.

Scala

Desenvolvida por Martin Odersky, a partir de 2001, como evolução de seus projetos de linguagens de programação envolvendo os paradigmas da orientação a objetos e funcional. Scala é considerada um linguagem multiparadigma de propósito geral. Seu nome é uma abreviação de scalable language.
É similar ao Java, com sintaxe básica bastante semelhante, compartilhando sua API. Mas possui diversas características próprias, que fazem seu código bastante distinto tem relação ao Java. Seus programas podem ser executados na JVM e podem ser integrados a outras aplicações Java. Um exemplo do tradicional programa Hello World com Scala:

object HelloWorld {
    def main(args: Array[String]): Unit = {
        println("Hello, world!")
    }
}

Aqui object define um singleton (um objeto único) denominado HelloWorld, no qual def declara um método denominado main que recebe um array de tipo String como argumento e tem Unit como valor de retorno. O código do método apenas efetua a impressão de uma string.

Neste exemplo coexistem elementos quase idênticos ao Java, junto de outros bastante diferentes.

Do ponto de vista de programação Scala é puramente orientada a objetos, não possuindo tipos primitivos. Também é funcional, tanto que todos os seus operadores são funções implícitas.

Scala possui um design muito interessante, pois a combinação de seu sistema de tipos, o fato de seus métodos poderem ser usados como operadores e parâmetros, além da construção de closures permite ampliar sua sintaxe substancialmente.

A linguagem de programação Scala é utilizada por grande companhias, como Twitter, Linkedin, Netflix, FourSquare, Tumblr, AirBnB e outras.

Para saber mais sobre o Scala, veja os links no final deste post.


Groovy

Desenvolvida por Guillaume Laforge, a partir de 2003, como uma alternativa ao Java. É um linguagem de programação dinâmica, orientada a objetos e funcional, que combina características do Smalltalk, Python, Ruby, entre outras. Atualmente é mantida como um projeto da Apache Software Foundation, considerada um linguagem multiparadigma de propósito geral. Seu nome é uma abreviação de scalable language.
Possui sintaxe similar ao Java, com a qual compartilha a API e pode se integrar facilmente. Mas possui várias características inexistentes no Java, como tipagem estática e dinâmica; sintaxe nativa para arranjos, listas, arrays associativos e expressões regulares, além de closures e sobrecarga de operadores. Programas Groovy são compilados dinamicamente para bytecodes Java, podendo então ser executados na JVM e, com isto, integrados a outras aplicações Java. Um exemplo do tradicional programa Hello World com Groovy:

class HelloWorld {
    static void main(String[] args) {
        println "Hello, World"
    }
}

Aqui class define uma classe denominada HelloWorld, na qual existe a declaração de um método estático denominado main que recebe um array de tipo String como argumento e não tem valor de retorno (void). O código deste método usa println para efetuar a impressão de uma string.

Neste exemplo todos os elementos são idênticos ao Java. Mas o Groovy oferece muitas outras possibilidades.

Do ponto de vista de programação Groovy é uma linguagem opcionalmente (fracamente) tipada, dotada de orientada a objetos e programação funcional. Permite a compilação estática, metaprogramação em tempo de compilação e execução, inferência de tipos e capacidade de scripting.

Devido a proximidade sintática com Java, é de fácil aprendizado, mas suas capacidades diferenciadas a tornam muito flexível, customizável e escalável. Sua integração transparente com Java e a quantidade de bibliotecas, ferramentas e frameworks de seu ecossistema a tornam muito interessante.

A linguagem de programação Groovy é utilizada pelas equipes de desenvolvimento de grande companhias, como IBM, Cisco, Sony, Target, MTv, Nestle, Oracle, SAS e outras.

Para saber mais sobre o Groovy, veja os links no final deste post.

Para Saber Mais