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