Java 6 ei linguaggi di scripting (JSR 223)

JUG – Ancona Italy
Java 6 e i linguaggi di
scripting (JSR 223)
Andrea Del Bene
Jug Marche
[email protected]
Java:piattaforma e linguaggio
●
●
Piattaforma Java
“Insieme di prodotti e specifiche per sviluppare e distribuire
applicazioni multipiattaforma...”
Source: http://en.wikipedia.org/wiki/Java_(Sun)
Linguaggio Java
“Componente di base della piattaforma Java...deriva la sua sintassi da C
e C++ ma con un modello ad oggetti semplificato”
Source: http://en.wikipedia.org/wiki/Java_(programming_language)
Il successo planetario della piattaforma Java ha reso estrema-mente
popolare anche il suo linguaggio
Altri linguaggi di successo
Anche se Java rimane il linguaggio più diffuso nel corso degli
ultimi anni altri linguaggi hanno acquistato grande popolarità e si
sono ritagliati degli ambiti applicativi d'eccellenza (es: PHP per la
programmazione Web)
Source: http://www.tiobe.com/
Verso una JVM “poliglotta”
In risposta al successo di questi linguaggi antagonisti la comunità
Java ha iniziato a lavorare ad un'infrastruttura standard per poter
eseguire da Java codice scritto in linguaggi diversi. L'intento era
quello di fornire allo sviluppatore il meglio del mondo Java e delle
altre tecnologie.
Il risultato di questi sforzi è stato la redazione della specifica
ufficiale JSR 223 Scripting for the Java Platform, avente lo scopo
di fornire un'infrastruttura di riferimento per l'implementazione di
classi capaci di eseguire sulla JVM script scritti in un determinato
linguaggio.
Origine della JSR 223
L'esigenza di integrazione tra JVM e script è nata nel contesto delle
applicazioni web dove l'uso di diversi linguaggi interpretati è molto
diffuso
Un primo tentativo di fornire interfacce e classi comuni ai motori
Java di scripting è stato fatto dal progetto Bean Scripting
Framework(BSF), inizialmente ideato da IBM e ora parte
dell'Apache Jakarta project.
I linguaggi supportati da BSF sono: Ruby, Groovy, JavaScript,
Python, Tcl, ...
La specifica JSR 223 è stata influenzata fortemente dal lavoro fatto
per questo framework.
Organizzazione della JSR 223
La JSR 223 si divide in due sottoparti:
●
Java Language Bindings
è il meccanismo che consente agli script di creare oggetti Java o
di interagire con istanze già esistenti sulla JVM. Considera quindi
l'integrazione tra Java e linguaggio di scripting dal punto di vista
dello script
Scripting
●
Java
General Scripting API
descrive le classi e le interfacce che permettono ad un script
engine di essere usato in un programma Java. Viene presa in
considerazione l'integrazione tra linguaggi dal punto di vista di
Java.
Java
Scripting
Java Language Bindings
Questa parte della specifica è rivolta ai progettisti di linguaggi che
devono fare in modo che il linguaggio di scripting supporti le
operazioni basilari sugli oggetti:
1)Creazione di istanze sulla JVM
2)Invocazione dei metodi di istanza e statici e dei costruttori
3)Overloading dei metodi
4)Conversione nei tipi Java degli argomenti dei metodi
5)Conversione del valore di ritorno di un metodo nel tipo corrispondente
del linguaggio di script
6)Accesso alle proprietà di un oggetto.
La specifica dice semplicemente cosa deve essere garantito non
come. Sul come si lascia ampio spazio al progettista...
I linguaggi che supportano l'OOP riescono a lavorare con gli oggetti
con la loro sintassi base. In caso contrario occorre espandere la
sintassi. Esempio:
Creazione oggetti in Groovy e Prolog
●
Creazione di una stringa e chiamata toUpperCase in Groovy
text = 'ciao';
text = text.toUpperCase();
La stringa è tata creata usando la normale sintassi Groovy
●
Creazione di una stringa e chiamata toUpperCase in Prolog
usando le librerie Java tuProlog (NON conformi jsr 223)
java_object(’java.lang.String’,[],Text).
Text <- toUpperCase returns Value.
Per usare gli oggetti è stata ampliata la sintassi Prolog. Le
keywords <- e returns non fanno parte della sintassi originale!
General Scripting API
Questa parte della specifica interessa maggiormente il
programmatore Java “comune” che vuole usare un motore di script
nel proprio codice.
Le General Scripting Api sono vere proprie classi ed interfaccie
standard presenti nei package Java che devono essere importate per
usare un motore di script
Come vedremo questa parte della specifica oltre ad indicare cosa
usare per lo scripting descrive anche come usare le classi e le
interfacce fornite.
La seguente presentazione tratterà principalmente le General
Scripting API
L'interfaccia ScriptEngine
L'entità centrale della JSR 223 è l'interfaccia ScriptEngine. Tutti i
motori di script sono implementazioni di questa interfaccia.
I metodi principali dell'interfaccia sono overloding del metodo eval
che lancia l'esecuzione di un dato script e che restituisci come
risultato un generico Object.
Il metodo eval più semplice accetta come unico parametro la stringa
contenente lo script da eseguire. Le entità Bindings e Context le
esamineremo a breve.
L'interfaccia Compilable
Il motore di script può implementare l'interfaccia facoltativa
Compilable. Con essa gli script oltre che essere eseguiti
direttamente possono essere “compilati” producendone una
rappresentazione intermedia (un'istanza di CompiledScript) e
permette una sua riesecuzione.
Esiste un'ulteriore interfaccia facoltativa chiamata Invocable che
consente di compilare gli script e solo singole procedure o metodi in
essi definiti.
NOTA:ricordiamoci che l'espressione “compilare gli script” è un po'
forviante, non viene prodotto bytecode o codice intermedio!
Uso di uno script engine
Gli script engine JSR 223 non vengono creati direttamente dal codice
client ma vengono istanziati tramite pattern Factory. Ogni script engine
ha associata un'implementazione dell'interfaccia ScriptEngineFactory.
Per trovare la factory associata ad un dato script engine si ricorre alla
classe ScriptEngineManager, usando il meccanismo chiamato Script
Engine Discovery Mechanism.
Il meccanismo di discovery sfrutta il servizio Service Provider dei file
Jar. Deve essere presente anche un file di testo alla posizione METAINF/services/javax.script.ScriptEngineFactory. Il file di testo deve
contenere una riga per ogni classe che implementa ScriptEngineFactory.
#list of ScriptEngineFactory's in this package
com.sun.script.javascript.RhinoScriptEngineFactory #javascript
com.sun.script.beanshell.BeanShellEngineFactory #BeanShell
Esempio base di utilizzo di uno motore
di script
Esempio
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class CalcMain {
public static void main(String[] args) throws
Exception {
ScriptEngineManager factory = new
ScriptEngineManager();
ScriptEngine engine =
factory.getEngineByName("groovy");
// visualizza la somma dei numeri da 1 a 10
System.out.println(engine.eval("(1..10).sum()"));
}
Diagramma di sequenza
1
2
3
Uso con interfaccia Compilable
Esempio
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class CalcMain {
public static void main(String[] args) throws Exception
{ ScriptEngineManager factory = new ScriptEngineManager();
Compilable engine = (Compilable)factory.getEngineByName("groovy");
// basic example
CompiledScript compiledScript = engine.compile("(1..10).sum()");
System.out.println(compiledScript.eval());
}
}
Diagramma di sequenza
1
2
3
4
Bindings e scope
Lo stato delle Scripting API è mantenuto in set di chiavi/valori chiamati
scope. Ai valori degli scope si accede tramite l'interfaccia Bindings che
estende a sua volta l'interfaccia standard java.util.Map<String,
Object>.
Gli script engine possono accedere ad almeno due scope: l'engine scope
e il global scope. L'accesso all'insieme di scope visibili avviene tramite
un'implementazione dell'interfaccia ScriptContext.
Engine scope: contiene il mapping tra variabili di script e il loro valore.
Tramite questo oggetto Map possiamo quindi recuperare il valore delle
variabili di uno script usando come chiave il loro nome. Ogni istanza di
script engine ha il suo engine scope, e la variabili in esso contenute
sono visibili solo all'istanza di appartenenza.
Global scope: contiene contiene di solito dati dell'applicazione host che
sta usando lo script engine. Lo scope è condiviso da tutte le istanze di
engine create dal medesimo ScriptEngineManager.
ScriptEngine, ScriptContext e
Bindings
L'implementazione di ScriptContext è in relazione uno a uno con la
classe che implementa l'engine scope poichè l'engine scope è esclusivo
per ogni script engine. Al contrario il global scope è condiviso da tutte
le istanze di script engine create dalla stessa classe manager
Esempio di engine scope
package groovyExample;
import
import
import
import
import
import
javax.script.Compilable;
javax.script.CompiledScript;
javax.script.ScriptEngine;
javax.script.ScriptEngineManager;
javax.script.ScriptException;
javax.script.SimpleBindings;
public class GroovyScopeExample {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("groovy");
String fact = "text = 'ciao';";
engine.eval(fact);
//visualizza ciao
System.out.println(engine.get("text"));
}
}
Esempio di global scope
package alice.JSR223Examples;
import
import
import
import
import
javax.script.Bindings;
javax.script.ScriptContext;
javax.script.ScriptEngine;
javax.script.ScriptEngineManager;
javax.script.ScriptException;
public class GroovyScopeExample {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("groovy");
String fact = "text = 'ciao';";
engine.eval(fact);
Bindings bindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("name", "Mario");
fact = "text + ' ' + name";
System.out.println(engine.eval(fact));
}
}
Engine e global insieme...
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("groovy");
String fact = "text = 'ciao';";
engine.eval(fact);
Bindings bindings =
engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("name", "Mario");
fact = "text + ' ' + name";
System.out.println(engine.eval(fact));
NOTA: se uno script crea variabili globali durante l'esecuzione queste
vengono viste anche dagli script successivamente eseguiti (vedi text)!
JSR 223 in Java 6
A partire dalla versione 6.0 di Java ( Java SE 6 Dolphin) java integra il
motore di scripting JavaScript Rhino conforme alla specifica JSR 223.
ScriptEngineManager mgr = new
ScriptEngineManager();
ScriptEngine jsEngine =
mgr.getEngineByName("JavaScript");
try {
jsEngine.eval("print('Hello, world!')");
} catch (ScriptException ex) {
ex.printStackTrace();
}
Sito di riferimento
Esiste anche un sito di riferimento all'indirizzo
https://scripting.dev.java.net/ voluto da Sun e che raccoglie tutte
relative agli engine compatibili con la JSR 223.
Ogni motore è scaricabile come Jar da inserire nel proprio
CLASSPATH
Domande...?
JUG – Ancona Italy
Grazie !
Andrea Del Bene
JUG Marche - www.jugancona.it