Obsah
1. Využití sdílených objektů mezi aplikací v Javě a skripty
3. První demonstrační příklad: použití metod ScriptEngineManager.put() a ScriptEngineManager.get()
4. Druhý demonstrační příklad: výpis hodnot předaných do skriptovacího enginu
5. Rozdíly mezi datovými typy použitými v Javě a JavaScriptu
6. Třetí demonstrační příklad: typy hodnot předaných do JavaScriptu
7. Porovnání datových typů používaných v Javě a JavaScriptu
8. Čtvrtý demonstrační příklad: modifikace hodnot programem v JavaScriptu
1. Využití sdílených objektů mezi aplikací v Javě a skripty
Aplikační programové rozhraní pro podporu skriptovacích jazyků ve virtuálním stroji Javy (JVM), které bylo standardizované v rámci JSR 223, obsahuje i metody použitelné pro vytvoření pojmenovaných objektů sdílených mezi aplikací napsanou v Javě a skriptem vytvořeným v libovolném (podporovaném) skriptovacím jazyce. V předchozí části tohoto seriálu jsme si ukázali, jakým způsobem lze funkci naprogramované v JavaScriptu (tj. jednom z podporovaných skriptovacích jazyků) předat seznam libovolných hodnot a jak může tato funkce naopak vrátit programu v Javě nějakou hodnotu, která je automaticky zkonvertována na takový datový typ (ať již primitivní nebo objektový), jenž je možné v javovských aplikacích jednoduše zpracovat (konverzi je však nutné provést programově). Skripty taktéž mohou volat viditelné metody javovských objektů (předaných formou parametrů) a přistupovat k jejich viditelným (public) atributům, což je taktéž jedna z možností, jak zajistit vzájemnou komunikaci mezi programem v Javě a skripty spouštěnými v rámci tohoto programu.
Ovšem žádná z výše uvedených možností nemusí vyhovovat ve všech případech, protože například předávání všech potřebných hodnot přes parametry javascriptových funkcí volaných z Javy by vedla k tomu, že by počet parametrů mnohdy zbytečně narůstal. Z tohoto důvodu jsou, jako alternativa k předávání parametrů, v JSR 223 popsány dvě metody nazvané put() a get(), které jsou implementovány každou instancí třídy ScriptEngineManager (podobné metody lze použít i v rámci jednotlivých skriptovacích enginů, kde se však může lišit oblast jejich viditelnosti). Pomocí metody put() se do pojmenovaného sdíleného objektu může uložit libovolná hodnota primitivního datového typu Javy (převedená na „obalový“ typ) nebo libovolný objekt. S využitím metody get() se naopak přečte pojmenovaný sdílený objekt (zde je většinou nutné použít přetypování vrácené hodnoty). Pojmenování sdíleného objektu musí navíc vycházet ze zvyklostí používaných ve skriptovacích jazycích, což například znamená, že jméno sum-x by mohlo být použito v jazyce Clojure (varianta LISPu), ale již nikoli v JavaScriptu.
2. Základní vlastnosti metod ScriptEngineManager.put() a ScriptEngineManager.get(), přístup ke sdíleným objektům ze skriptů
Metoda ScriptEngineManager.put() jako svůj první parametr očekává řetězec představující jméno proměnné, pod kterým je možné ke sdílenému objektu přistupovat ve skriptech, což jsme si již ostatně řekli v předchozí kapitole. Druhým parametrem je obecný typ Object, což mj. znamená, že se v tomto parametru může skriptovacímu enginu předat instance libovolné třídy, tj. objektový typ. Pokud je předávána hodnota primitivního datového typu (může se jednat o pravdivostní hodnotu, celé číslo libovolného rozsahu, znak, popř. o reálné číslo libovolného rozsahu a přesnosti), je díky autoboxingu zavedeného v JDK 5 automaticky převedena na příslušný „obalový“ objekt. Namísto reference na existující objekt lze předat i null. V tomto případě k vytvoření sdíleného objektu sice skutečně dojde, o čemž se přesvědčíme v dalších demonstračních příkladech, ale hodnota bude ve skriptu (konkrétně v JavaScriptu) bude nastavena na null (nikoli však Undefined).
V případě, že nedojde ke změně hodnoty některého sdíleného objektu ve skriptu, není jeho typ žádným způsobem skriptovacím enginem upravován, což lze jednoduše zjistit získáním jména třídy příslušného objektu získaného metodou ScriptEngineManager.get(), nebo s využitím operátoru instanceof. Ovšem skript obecně může do sdílených objektů – které se z pohledu skriptu chovají jako běžné globální proměnné – zapsat jakoukoli hodnotu, protože ve většině skriptovacích jazyků se typy vážou přímo k hodnotám, nikoli k proměnným nesoucím tyto hodnoty. To například znamená, že ve sdíleném objektu, kterému byl původně v Javě přiřazen typ java.lang.Integer může být po spuštění skriptu uložen řetězec, seznam, instance třídy java.util.Date atd. S tímto rozdílem v chování Javy a JavaScriptu (popř. dalších dynamicky typovaných programovacích jazyků) jsme se již setkali v předchozí části tohoto seriálu.
3. První demonstrační příklad: použití metod ScriptEngineManager.put() a ScriptEngineManager.get()
V dnešním prvním demonstračním příkladu jsou ukázány všechny vlastnosti metod ScriptEngineManager.put() a ScriptEngineManager.get() popsané v předchozí kapitole. Nejprve jsou s využitím metody put() vytvořeny sdílené objekty (zde se v některých případech uplatní autoboxing), posléze jsou tyto objekty získány zpět pomocí metody get() s tím, že je zjištěn i typ objektu, který zůstane nezměněn, protože sdílené objekty nejsou skriptem modifikovány (žádný skript ostatně ani není spuštěn):
import java.util.Date; import java.util.List; import java.util.ArrayList; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.Invocable; /** * Ukazka volani metod ScriptEngineManager.put() a ScriptEngineManager.get(), * ktere mohou byt pouzity pro predavani hodnot mezi skripty a Javovskym * programem. * * @author Pavel Tisnovsky */ public class ScriptingTest11 { private ScriptEngineManager scriptEngineManager; private ScriptEngine scriptEngine; private Invocable invocable; /** * Inicializace skriptovaciho engine v konstruktoru. */ protected ScriptingTest11() { this.scriptEngineManager = new ScriptEngineManager(); this.scriptEngine = this.scriptEngineManager.getEngineByName("JavaScript"); this.invocable = (Invocable)this.scriptEngine; } /** * Vytvoreni nekolika globalnich promennych v ramci skriptovaciho enginu a * jejich zpetne precteni s vyhodnocenim jejich hodnoty a taktez typu. */ private void testScriptEngineManagerGetMethod() { System.out.println("testScriptEngineManagerGetMethod:"); // ulozeni retezcove hodnoty a hodnoty null this.scriptEngineManager.put("foo2", "bar"); this.scriptEngineManager.put("foo3", null); // booleovske hodnoty this.scriptEngineManager.put("pravda", true); this.scriptEngineManager.put("nepravda", false); // ruzne typy celociselnych hodnot this.scriptEngineManager.put("cele_cislo1", (byte)42); this.scriptEngineManager.put("cele_cislo2", (short)42); this.scriptEngineManager.put("cele_cislo3", 42); this.scriptEngineManager.put("cele_cislo4", 42L); // ruzne typy realnych hodnot this.scriptEngineManager.put("realne_cislo1", 3.14f); this.scriptEngineManager.put("realne_cislo2", 3.14); // dalsi typy objektu this.scriptEngineManager.put("datum", new Date()); this.scriptEngineManager.put("seznam", new ArrayList()); // vypis typu a hodnot vlastnosti (i vlastnosti neexistujicich) for (String propertyName : new String[] { "foo1", "foo2", "foo3", "pravda", "nepravda", "cele_cislo1", "cele_cislo2", "cele_cislo3", "cele_cislo4", "realne_cislo1", "realne_cislo2", "datum", "seznam"}) { printPropertyTypeAndValue(propertyName); } } /** * Vypis typu a hodnoty vybrane promenne. */ private void printPropertyTypeAndValue(String propertyName) { String propertyType = "void"; // tato metoda muze vratit null namisto skutecne reference Object propertyValue = this.scriptEngineManager.get(propertyName); // zjistovat tridu (typ) ma smysl pouze pro existujici objekty if (propertyValue != null) { propertyType = propertyValue.getClass().getName(); } System.out.format("property name: %-14s type: %-20s value: %s\n", propertyName, propertyType, propertyValue); } /** * Inicializace a spusteni testu. */ protected void runTests() { testScriptEngineManagerGetMethod(); } /** * @param args nepouzito. */ public static void main(String[] args) { new ScriptingTest11().runTests(); } }
Po spuštění prvního demonstračního příkladu se na standardní výstup vypíše následující tabulka. Povšimněte si toho, jak se původní hodnoty některého primitivního datového typu (byte, short, int, float, double, boolean) automaticky již při ukládání nahradily obalujícím objektovým typem (java.lang.Byte, …):
testScriptEngineManagerGetMethod: property name: foo1 type: void value: null property name: foo2 type: java.lang.String value: bar property name: foo3 type: void value: null property name: pravda type: java.lang.Boolean value: true property name: nepravda type: java.lang.Boolean value: false property name: cele_cislo1 type: java.lang.Byte value: 42 property name: cele_cislo2 type: java.lang.Short value: 42 property name: cele_cislo3 type: java.lang.Integer value: 42 property name: cele_cislo4 type: java.lang.Long value: 42 property name: realne_cislo1 type: java.lang.Float value: 3.14 property name: realne_cislo2 type: java.lang.Double value: 3.14 property name: datum type: java.util.Date value: Tue Apr 26 20:56:08 CEST 2011 property name: seznam type: java.util.ArrayList value: []
4. Druhý demonstrační příklad: výpis hodnot předaných do skriptovacího enginu
Ve druhém demonstračním příkladu se nejprve pomocí metody ScriptEngineManager.put() pojmenují a vytvoří sdílené objekty, jejichž hodnoty jsou následně přečteny a vypsány na standardní vystup v javascriptové funkci nazvané showValues(). Povšimněte si, že tato funkce může ke sdíleným objektům přistupovat naprosto stejným způsobem, jako ke globálním proměnným – z pohledu skriptu se totiž skutečně jedná o globální proměnné, které jsou ve skripovacím enginu uloženy v samostatné sdílené oblasti. Přesněji řečeno tyto globální proměnné nejsou uloženy přímo ve skriptovacím enginu, ale jsou navíc sdíleny mezi více skriptovacími enginy, což může ale taktéž nemusí být požadovaná vlastnost (pokud by bylo žádoucí, aby byly sdílené objekty pro každý skriptovací engine uložené samostatně, je možné použít rozhraní Bindings popsané příště:
import java.util.Date; import java.util.List; import java.util.ArrayList; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.Invocable; /** * Ukazka vyuziti hodnot ulozenych metodou ScriptEngineManager.put() * ve skriptech. * * @author Pavel Tisnovsky */ public class ScriptingTest12 { private ScriptEngineManager scriptEngineManager; private ScriptEngine scriptEngine; private Invocable invocable; /** * Inicializace skriptovaciho engine v konstruktoru. */ protected ScriptingTest12() { this.scriptEngineManager = new ScriptEngineManager(); this.scriptEngine = this.scriptEngineManager.getEngineByName("JavaScript"); this.invocable = (Invocable)this.scriptEngine; } /** * Vytvoreni nekolika globalnich promennych v ramci skriptu. */ private void putValues() { // ulozeni retezcove hodnoty a hodnoty null this.scriptEngineManager.put("foo2", "bar"); this.scriptEngineManager.put("foo3", null); // booleovske hodnoty this.scriptEngineManager.put("pravda", true); this.scriptEngineManager.put("nepravda", false); // ruzne typy celociselnych hodnot this.scriptEngineManager.put("cele_cislo1", (byte)42); this.scriptEngineManager.put("cele_cislo2", (short)42); this.scriptEngineManager.put("cele_cislo3", 42); this.scriptEngineManager.put("cele_cislo4", 42L); // ruzne typy realnych hodnot this.scriptEngineManager.put("realne_cislo1", 3.14f); this.scriptEngineManager.put("realne_cislo2", 3.14); // dalsi typy objektu this.scriptEngineManager.put("datum", new Date()); this.scriptEngineManager.put("seznam", new ArrayList()); } /** * Metoda, ktera vykona prislusny program v JavaScriptu, jehoz * funkce mohou byt spousteny v dalsich castech testu. */ private void evaluateScript() throws ScriptException { final String script = "function showValues() {" + " println('inside showValues() function:');" + " println('variable foo2 = ' + foo2);" + " println('variable foo3 = ' + foo3);" + " println('variable pravda = ' + pravda);" + " println('variable nepravda = ' + nepravda);" + " println('variable cele_cislo1 = ' + cele_cislo1);" + " println('variable cele_cislo2 = ' + cele_cislo2);" + " println('variable cele_cislo3 = ' + cele_cislo3);" + " println('variable cele_cislo4 = ' + cele_cislo4);" + " println('variable realne_cislo1 = ' + realne_cislo1);" + " println('variable realne_cislo2 = ' + realne_cislo2);" + " println('variable datum = ' + datum);" + " println('variable seznam = ' + seznam);" + "}"; this.scriptEngine.eval(script); } /** * Vypis hodnot promennych pomoci skriptu. */ private void printValues() throws ScriptException, NoSuchMethodException { this.invocable.invokeFunction("showValues"); System.out.println(); } /** * Inicializace a spusteni testu. */ protected void runTests() { try { putValues(); evaluateScript(); printValues(); } catch (ScriptException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } /** * @param args nepouzito. */ public static void main(String[] args) { new ScriptingTest12().runTests(); } }
Na výstupu vytvořeného druhým demonstračním příkladem je zajímavý především řádek s proměnnou foo3, která sice skutečně existuje (jinak by došlo k vyhození výjimky při pokusu o její výpis), ovšem její hodnota je null:
inside showValues() function: variable foo2 = bar variable foo3 = null variable pravda = true variable nepravda = false variable cele_cislo1 = 42 variable cele_cislo2 = 42 variable cele_cislo3 = 42 variable cele_cislo4 = 42 variable realne_cislo1 = 3.140000104904175 variable realne_cislo2 = 3.14 variable datum = Tue Apr 26 21:20:32 CEST 2011 variable seznam = []
5. Rozdíly mezi datovými typy použitými v Javě a JavaScriptu
Při sdílení objektů mezi aplikací naprogramovanou v Javě a skriptem napsaným v některém podporovaném skriptovacím jazyce, je občas nutné, aby skriptovací engine sdílené objekty konvertoval takovým způsobem, aby k nim měly bezproblémový přístup obě komunikující strany, tj. jak silně a současně i staticky typovaná Java, tak i dynamicky typovaný skriptovací jazyk. V případě použití JavaScriptu tedy dochází ke konverzi mezi objektovými typy Javy (může se jednat o instanci jakékoli třídy) a jedním z pěti datových typů, s nimiž pracuje JavaScript:
Datový typ | Význam |
---|---|
boolean | pravdivostní hodnota |
number | reálné číslo v dvojité přesnosti |
string | řetězec |
object | objektový typ |
array | pole s volitelnou velikostí |
null | odpovídá null v Javě |
function | funkce je v JS plnohodnotným typem, dynamicky generovaný objekty sun.org.mozilla.javascript.gen.XXX v Javě |
Nejjednodušší je situace při práci s pravdivostními hodnotami, ale problém může nastat již u numerických hodnot, protože v JavaScriptu jsou tyto hodnoty reprezentovány jako reálná čísla v dvojité přesnosti, tedy céčkovským či javovským typem double (http://www.hunlock.com/blogs/The_Complete_Javascript_Number_Reference). Při konverzích tedy může dojít ke změně nejnižších cifer u hodnot, které byly v Javě uloženy v proměnných typu long, protože jak typ long, tak i typ double používá pro reprezentaci hodnot osm bajtů, přičemž některé bity jsou u typu double vyhrazeny pro uložení exponentu. Mnohem důležitější je ale konverze dalších typů objektů v Javě na obecný javascriptový typ object – pokud programátor ve skriptu pouze využívá viditelné atributy a metody předaných objektů, nedochází k žádným problémům (to jsme si ostatně ukázali již minule), ovšem ve chvíli přidání nového atributu či metody, což je v JavaScriptu zcela legální postup, by se již zpětná konverze na odpovídající objekt v Javě nemohla provést.
6. Třetí demonstrační příklad: typy hodnot předaných do JavaScriptu
Ve třetím demonstračním příkladu je ukázáno, jak lze přímo z programu vytvořeného v JavaScriptu zjistit a vypsat typ sdílených objektů. Pokud si porovnáme výpis prvního a tohoto příkladu, snadno zjistíme, jaké typové konverze jsou v čase běhu aplikace prováděny při předávání hodnot mezi Javou a skriptovacím enginem. Taktéž si všimněte, že zdrojový kód skriptu, který je reprezentován řetězcem, je možné v některých případech relativně snadno generovat s využitím programových smyček a operací s řetězci nebo instancemi tříd StringBuilder a StringBuffer:
import java.util.Date; import java.util.List; import java.util.ArrayList; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.Invocable; /** * Typy dat predavanych mezi programem napsanym v Jave a JavaScriptu * se z pochopitelnych duvodu lisi. V nasledujicim programu je volana * JavaScriptova funkce, ktera vyhodnoti a nasledne vypise typy * globalnich promennych predanych pomoci metody ScriptEngineManager.put(). * * @author Pavel Tisnovsky */ public class ScriptingTest13 { private ScriptEngineManager scriptEngineManager; private ScriptEngine scriptEngine; private Invocable invocable; /** * Inicializace skriptovaciho engine v konstruktoru. */ protected ScriptingTest13() { this.scriptEngineManager = new ScriptEngineManager(); this.scriptEngine = this.scriptEngineManager.getEngineByName("JavaScript"); this.invocable = (Invocable)this.scriptEngine; } /** * Vytvoreni nekolika globalnich promennych v ramci skriptu. */ private void putValues() { // ulozeni retezcove hodnoty a hodnoty null this.scriptEngineManager.put("foo2", "bar"); this.scriptEngineManager.put("foo3", null); // booleovske hodnoty this.scriptEngineManager.put("pravda", true); this.scriptEngineManager.put("nepravda", false); // ruzne typy celociselnych hodnot this.scriptEngineManager.put("cele_cislo1", (byte)42); this.scriptEngineManager.put("cele_cislo2", (short)42); this.scriptEngineManager.put("cele_cislo3", 42); this.scriptEngineManager.put("cele_cislo4", 42L); // ruzne typy realnych hodnot this.scriptEngineManager.put("realne_cislo1", 3.14f); this.scriptEngineManager.put("realne_cislo2", 3.14); // dalsi typy objektu this.scriptEngineManager.put("datum", new Date()); this.scriptEngineManager.put("seznam", new ArrayList()); } /** * Metoda, ktera vykona prislusny program v JavaScriptu, jehoz * funkce mohou byt spousteny v dalsich castech testu. */ private void evaluateScript() throws ScriptException { String[] variables = new String[] { "foo2", "foo3", "pravda", "nepravda", "cele_cislo1", "cele_cislo2", "cele_cislo3", "cele_cislo4", "realne_cislo1", "realne_cislo2", "datum", "seznam" }; // mala ukazka generovani skriptu pomoci zakladni // manipulace s retezci v Jave StringBuilder script = new StringBuilder(); script.append("function showValues() {\n"); for (String variable : variables) { script.append(" println('variable " + variable + " = ' + " + variable + ");\n"); } script.append("}\n\n"); script.append("function showTypes() {\n"); for (String variable : variables) { script.append(" println('type of variable " + variable + " = ' + typeof " + variable + ");\n"); } script.append("}\n"); System.out.println("Generated script:\n" + script.toString()); this.scriptEngine.eval(script.toString()); } /** * Vypis hodnot promennych pomoci skriptu. */ private void printValues() throws ScriptException, NoSuchMethodException { this.invocable.invokeFunction("showValues"); System.out.println(); } /** * Vypis typu promennych pomoci skriptu. */ private void showTypes() throws ScriptException, NoSuchMethodException { this.invocable.invokeFunction("showTypes"); System.out.println(); } /** * Inicializace a spusteni testu. */ protected void runTests() { try { putValues(); evaluateScript(); printValues(); showTypes(); } catch (ScriptException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } /** * @param args nepouzito. */ public static void main(String[] args) { new ScriptingTest13().runTests(); } }
Tento příklad po svém spuštění nejdříve vypíše skutečnou řetězcovou reprezentaci skriptu, jehož zdrojový kód obsahuje dvojici funkcí pojmenovaných showValues() a showTypes():
Generated script: function showValues() { println('variable foo2 = ' + foo2); println('variable foo3 = ' + foo3); println('variable pravda = ' + pravda); println('variable nepravda = ' + nepravda); println('variable cele_cislo1 = ' + cele_cislo1); println('variable cele_cislo2 = ' + cele_cislo2); println('variable cele_cislo3 = ' + cele_cislo3); println('variable cele_cislo4 = ' + cele_cislo4); println('variable realne_cislo1 = ' + realne_cislo1); println('variable realne_cislo2 = ' + realne_cislo2); println('variable datum = ' + datum); println('variable seznam = ' + seznam); } function showTypes() { println('type of variable foo2 = ' + typeof foo2); println('type of variable foo3 = ' + typeof foo3); println('type of variable pravda = ' + typeof pravda); println('type of variable nepravda = ' + typeof nepravda); println('type of variable cele_cislo1 = ' + typeof cele_cislo1); println('type of variable cele_cislo2 = ' + typeof cele_cislo2); println('type of variable cele_cislo3 = ' + typeof cele_cislo3); println('type of variable cele_cislo4 = ' + typeof cele_cislo4); println('type of variable realne_cislo1 = ' + typeof realne_cislo1); println('type of variable realne_cislo2 = ' + typeof realne_cislo2); println('type of variable datum = ' + typeof datum); println('type of variable seznam = ' + typeof seznam); }
Posléze se vypíšou hodnoty globálních proměnných:
variable foo2 = bar variable foo3 = null variable pravda = true variable nepravda = false variable cele_cislo1 = 42 variable cele_cislo2 = 42 variable cele_cislo3 = 42 variable cele_cislo4 = 42 variable realne_cislo1 = 3.140000104904175 variable realne_cislo2 = 3.14 variable datum = Tue Apr 26 21:31:47 CEST 2011 variable seznam = []
A na závěr jejich typy tak, jak byly zkonvertovány skriptovacím enginem:
type of variable foo2 = string type of variable foo3 = object type of variable pravda = boolean type of variable nepravda = boolean type of variable cele_cislo1 = number type of variable cele_cislo2 = number type of variable cele_cislo3 = number type of variable cele_cislo4 = number type of variable realne_cislo1 = number type of variable realne_cislo2 = number type of variable datum = object type of variable seznam = object
7. Porovnání datových typů používaných v Javě a JavaScriptu
Zkusme nyní zkombinovat výstup prvního a třetího demonstračního příkladu, abychom získali přehled o typových konverzích prováděných skriptovacím enginem:
Sdílený objekt | Typ v Javě | Typ v JavaScriptu |
---|---|---|
foo2 | java.lang.String | string |
foo3 | void | object |
pravda | java.lang.Boolean | boolean |
nepravda | java.lang.Boolean | boolean |
cele_cislo1 | java.lang.Byte | number |
cele_cislo2 | java.lang.Short | number |
cele_cislo3 | java.lang.Integer | number |
cele_cislo4 | java.lang.Long | number |
realne_cislo1 | java.lang.Float | number |
realne_cislo2 | java.lang.Double | number |
datum | java.util.Date | object |
seznam | java.util.ArrayList | object |
8. Čtvrtý demonstrační příklad: modifikace hodnot programem v JavaScriptu
Po odzkoušení předchozích dvou demonstračních příkladů by se mohlo zdát, že jakákoli globální proměnná vytvořená ve skriptu se automaticky stane součástí sdílených objektů. Ve skutečnosti tomu tak však není – množina sdílených objektů se nemění ani v případě, že je ve skriptu vytvořena nová globální proměnná; hodnotu ani typ této proměnné nebude možné pomocí metody ScriptEngineManager.get() přečíst. Možnosti skriptu ovlivnit chování programu napsaného v Javě jsou tedy částečně omezeny, což opět odpovídá filozofii běhu skriptu v „sandboxu“. V dnešním posledním demonstračním příkladu je toto chování ukázáno na případu globální proměnné foo0, která je sice skriptem vytvořena, ale aplikace napsaná v Javě k ní nemůže přistupovat. Navíc je v příkladu ukázáno, že je možné přistupovat k viditelným metodám předávaných objektů (zde seznamu) i to, jak je provedena konverze typů položek seznamu vytvořených přímo ve skriptu (nikoli v Javě). Jako malý úkol si můžete vyzkoušet, co se stane, pokud se některému sdílenému objektu přiřadí funkce:
import java.util.Date; import java.util.List; import java.util.ArrayList; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import javax.script.Invocable; /** * Ukazka, jak muze skript modifikovat globalni promenne. * * @author Pavel Tisnovsky */ public class ScriptingTest14 { private ScriptEngineManager scriptEngineManager; private ScriptEngine scriptEngine; private Invocable invocable; /** * Inicializace skriptovaciho engine v konstruktoru. */ protected ScriptingTest14() { this.scriptEngineManager = new ScriptEngineManager(); this.scriptEngine = this.scriptEngineManager.getEngineByName("JavaScript"); this.invocable = (Invocable)this.scriptEngine; } /** * Vytvoreni nekolika globalnich promennych v ramci skriptu. */ private void putValues() { // ulozeni retezcove hodnoty a hodnoty null this.scriptEngineManager.put("foo2", "bar"); this.scriptEngineManager.put("foo3", null); // booleovske hodnoty this.scriptEngineManager.put("pravda", true); this.scriptEngineManager.put("nepravda", false); // ruzne typy celociselnych hodnot this.scriptEngineManager.put("cele_cislo1", (byte)42); this.scriptEngineManager.put("cele_cislo2", (short)42); this.scriptEngineManager.put("cele_cislo3", 42); this.scriptEngineManager.put("cele_cislo4", 42L); // ruzne typy realnych hodnot this.scriptEngineManager.put("realne_cislo1", 3.14f); this.scriptEngineManager.put("realne_cislo2", 3.14); // dalsi typy objektu this.scriptEngineManager.put("datum", new Date()); this.scriptEngineManager.put("seznam", new ArrayList()); } /** * Vypis typu a hodnot vlastnosti (i vlastnosti neexistujicich). */ private void showTypes() { for (String propertyName : new String[] { "foo0", "foo1", "foo2", "foo3", "pravda", "nepravda", "cele_cislo1", "cele_cislo2", "cele_cislo3", "realne_cislo1", "realne_cislo2", "datum", "seznam"}) { printPropertyTypeAndValue(propertyName); } } /** * Vypis typu a hodnoty vybrane promenne. */ private void printPropertyTypeAndValue(String propertyName) { String propertyType = "void"; Object propertyValue = this.scriptEngineManager.get(propertyName); // zjistovat tridu (typ) ma smysl pouze pro existujici objekty if (propertyValue != null) { propertyType = propertyValue.getClass().getName(); } System.out.format("property name: %-14s type: %-20s value: %s\n", propertyName, propertyType, propertyValue); } /** * Vypis hodnot a typu vsech prvku ulozenych v seznamu. */ private void showListValues() { List list = (List)this.scriptEngineManager.get("seznam"); int i = 0; for (Object item : list) { System.out.format("List item #%d has type %s and value %s\n", i++, item.getClass().getName(), item.toString()); } } /** * Metoda, ktera vykona prislusny program v JavaScriptu, jehoz * funkce mohou byt spousteny v dalsich castech testu. */ private void evaluateScript() throws ScriptException { final String script = "function modifyValues() {" + " foo0 = 'xyzzy';" + // bude tato promenna viditelna? " foo2 = 'xyzzy';" + " foo3 = 'not void';" + " pravda = !pravda;" + " cele_cislo1++;" + " cele_cislo2 *= cele_cislo3 + 1;" + " datum = new java.util.Date(0);" + // pocatek epochy! // pridani dalsich polozek do seznamu je snadne, nebot // se seznam chova jako bezny objekty typu List v Jave. " seznam.add('prvni');" + " seznam.add(false);" + " seznam.add(cele_cislo1);" + " seznam.add(0);" + " seznam.add(datum);" + " seznam.addAll(seznam);" + "}"; this.scriptEngine.eval(script); } /** * Vyvolani JavaScriptove funkce pro modifikaci globalnich promennych. */ private void modifyValues() throws ScriptException, NoSuchMethodException { this.invocable.invokeFunction("modifyValues"); } /** * Inicializace a spusteni testu. */ protected void runTests() { try { putValues(); evaluateScript(); modifyValues(); showTypes(); showListValues(); } catch (ScriptException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } /** * @param args nepouzito. */ public static void main(String[] args) { new ScriptingTest14().runTests(); } }
Výstup výše uvedeného demonstračního příkladu je opět rozdělený na dvě části. V první části se vypíšou nové hodnoty přiřazené sdíleným objektům ve skriptu:
property name: foo0 type: void value: null property name: foo1 type: void value: null property name: foo2 type: java.lang.String value: xyzzy property name: foo3 type: java.lang.String value: not void property name: pravda type: java.lang.Boolean value: false property name: nepravda type: java.lang.Boolean value: false property name: cele_cislo1 type: java.lang.Double value: 43.0 property name: cele_cislo2 type: java.lang.Double value: 1806.0 property name: cele_cislo3 type: java.lang.Integer value: 42 property name: realne_cislo1 type: java.lang.Float value: 3.14 property name: realne_cislo2 type: java.lang.Double value: 3.14 property name: datum type: java.util.Date value: Thu Jan 01 01:00:00 CET 1970 property name: seznam type: java.util.ArrayList value: [prvni, false, 43.0, 0.0, Thu Jan 01 01:00:00 CET 1970, prvni, false, 43.0, 0.0, Thu Jan 01 01:00:00 CET 1970]
Následně je vypsána hodnota i datový typ všech položek umístěných ve sdíleném objektu se jménem „seznam“ (jedná se o instanci třídy ArrayList):
List item #0 has type java.lang.String and value prvni List item #1 has type java.lang.Boolean and value false List item #2 has type java.lang.Double and value 43.0 List item #3 has type java.lang.Double and value 0.0 List item #4 has type java.util.Date and value Thu Jan 01 01:00:00 CET 1970 List item #5 has type java.lang.String and value prvni List item #6 has type java.lang.Boolean and value false List item #7 has type java.lang.Double and value 43.0 List item #8 has type java.lang.Double and value 0.0 List item #9 has type java.util.Date and value Thu Jan 01 01:00:00 CET 1970
9. Odkazy na Internetu
- JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Package javax.script (JavaDoc)
http://download.oracle.com/javase/6/docs/api/javax/script/package-summary.html - javax.script.Bindings
http://download.oracle.com/javase/6/docs/api/javax/script/Bindings.html - javax.script.Compilable
http://download.oracle.com/javase/6/docs/api/javax/script/Compilable.html - javax.script.Invocable
http://download.oracle.com/javase/6/docs/api/javax/script/Invocable.html - javax.script.ScriptContext
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptContext.html - javax.script.ScriptEngine
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptEngine.html - javax.script.ScriptEngineFactory
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptEngineFactory.html - javax.script.AbstractScriptEngine
http://download.oracle.com/javase/6/docs/api/javax/script/AbstractScriptEngine.html - javax.script.CompiledScript
http://download.oracle.com/javase/6/docs/api/javax/script/CompiledScript.html - javax.script.ScriptEngineManager
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptEngineManager.html - javax.script.SimpleBindings
http://download.oracle.com/javase/6/docs/api/javax/script/SimpleBindings.html - javax.script.SimpleScriptContext
http://download.oracle.com/javase/6/docs/api/javax/script/SimpleScriptContext.html - javax.script.ScriptException
http://download.oracle.com/javase/6/docs/api/javax/script/ScriptException.html - The Java Compatibility Test Tools: JavaTest Harness
http://java.sun.com/developer/technicalArticles/JCPtools2/ - JavaScript engine (Wikipedia)
http://en.wikipedia.org/wiki/JavaScript_engine - Rhino (JavaScript engine)
http://en.wikipedia.org/wiki/Rhino_(JavaScript_engine) - Rhino: JavaScript for Java
http://www.mozilla.org/rhino/ - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - HugePages
http://linux-mm.org/HugePages - Tuning big java heap and linux huge pages
http://www.tikalk.com/alm/forums/tuning-big-java-heap-and-linux-huge-pages - How do I set up hugepages in Red Hat Enterprise Linux 4
http://magazine.redhat.com/2007/05/29/how-do-i-set-up-hugepages-in-red-hat-enterprise-linux-4/ - Java SE Tuning Tip: Large Pages on Windows and Linux
http://blogs.sun.com/dagastine/entry/java_se_tuning_tip_large - Translation lookaside buffer
http://en.wikipedia.org/wiki/Translation_lookaside_buffer - Physical Address Extension
http://en.wikipedia.org/wiki/Physical_Address_Extension - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Amdahl's law
http://en.wikipedia.org/wiki/Amdahl_law - Garbage collection (computer science)
http://en.wikipedia.org/wiki/Garbage_collection_(computer_science) - Dr. Dobb's | G1: Java's Garbage First Garbage Collector
http://www.drdobbs.com/article/printableArticle.jhtml?articleId=219401061&dept_url=/java/ - Java's garbage-collected heap
http://www.javaworld.com/javaworld/jw-08–1996/jw-08-gc.html - Compressed oops in the Hotspot JVM
http://wikis.sun.com/display/HotSpotInternals/CompressedOops - 32-bit or 64-bit JVM? How about a Hybrid?
http://blog.juma.me.uk/2008/10/14/32-bit-or-64-bit-jvm-how-about-a-hybrid/ - Compressed object pointers in Hotspot VM
http://blogs.sun.com/nike/entry/compressed_object_pointers_in_hotspot - Java HotSpot™ Virtual Machine Performance Enhancements
http://download.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html - Using jconsole
http://download.oracle.com/javase/1.5.0/docs/guide/management/jconsole.html - jconsole – Java Monitoring and Management Console
http://download.oracle.com/javase/1.5.0/docs/tooldocs/share/jconsole.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - x86–64
http://en.wikipedia.org/wiki/X86–64 - Physical Address Extension
http://en.wikipedia.org/wiki/Physical_Address_Extension - Java performance
http://en.wikipedia.org/wiki/Java_performance - 1.6.0_14 (6u14)
http://www.oracle.com/technetwork/java/javase/6u14–137039.html?ssSourceSiteId=otncn - Update Release Notes
http://www.oracle.com/technetwork/java/javase/releasenotes-136954.html - Java virtual machine: 4.10 Limitations of the Java Virtual Machine
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#88659 - Java™ Platform, Standard Edition 7 Binary Snapshot Releases
http://dlc.sun.com.edgesuite.net/jdk7/binaries/index.html - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - ClosableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - ArrayList (JDK 1.4)
http://download.oracle.com/javase/1.4.2/docs/api/java/util/ArrayList.html