Podpora skriptovacích jazyků v JDK6 a OpenJDK6 (3.část)

28. 4. 2011
Doba čtení: 25 minut

Sdílet

Dnes se již potřetí vrátíme k aplikačnímu programovému rozhraní (API) navrženého v rámci JSR 223, které slouží k podpoře skriptovacích jazyků běžících nad virtuálním strojem Javy. Zaměříme se na předávání hodnot (obecně objektů) mezi aplikací v Javě a skriptem s využitím sdílených pojmenovaných objektů.

Obsah

1. Využití sdílených objektů mezi aplikací v Javě a skripty

2. Základní vlastnosti metod ScriptEngineMa­nager.put() a ScriptEngineMa­nager.get(), přístup ke sdíleným objektům ze skriptů

3. První demonstrační příklad: použití metod ScriptEngineMa­nager.put() a ScriptEngineMa­nager.get()

4. Druhý demonstrační příklad: výpis hodnot předaných do skriptovacího en­ginu

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

9. Odkazy na Internetu

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 ScriptEngineMa­nager (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 ScriptEngineMa­nager.put() a ScriptEngineMa­nager.get(), přístup ke sdíleným objektům ze skriptů

Metoda ScriptEngineMa­nager.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 ScriptEngineMa­nager.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 ScriptEngineMa­nager.put() a ScriptEngineMa­nager.get()

V dnešním prvním demonstračním příkladu jsou ukázány všechny vlastnosti metod ScriptEngineMa­nager.put() a ScriptEngineMa­nager.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.By­te, …):

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 en­ginu

Ve druhém demonstračním příkladu se nejprve pomocí metody ScriptEngineMa­nager.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.hun­lock.com/blog­s/The_Complete_Ja­vascript_Number_Re­ference). 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 ScriptEngineMa­nager.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:

bitcoin školení listopad 24

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

  1. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp­.org/en/jsr/de­tail?id=223
  2. Scripting for the Java Platform
    http://java.sun­.com/developer/techni­calArticles/J2SE/Des­ktop/scriptin­g/
  3. Scripting for the Java Platform (Wikipedia)
    http://en.wiki­pedia.org/wiki/Scrip­ting_for_the_Ja­va_Platform
  4. Java Community Process
    http://en.wiki­pedia.org/wiki/Ja­va_Specificati­on_Request
  5. Package javax.script (JavaDoc)
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/package-summary.html
  6. javax.script.Bin­dings
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Bindings.html
  7. javax.script.Com­pilable
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Compilable.html
  8. javax.script.In­vocable
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Invocable.html
  9. javax.script.Scrip­tContext
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptContex­t.html
  10. javax.script.Scrip­tEngine
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­.html
  11. javax.script.Scrip­tEngineFactory
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­Factory.html
  12. javax.script.Ab­stractScriptEn­gine
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/AbstractScrip­tEngine.html
  13. javax.script.Com­piledScript
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/CompiledScrip­t.html
  14. javax.script.Scrip­tEngineManager
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­Manager.html
  15. javax.script.Sim­pleBindings
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/SimpleBindin­gs.html
  16. javax.script.Sim­pleScriptContext
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/SimpleScrip­tContext.html
  17. javax.script.Scrip­tException
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptExcep­tion.html
  18. The Java Compatibility Test Tools: JavaTest Harness
    http://java.sun­.com/developer/techni­calArticles/JCPto­ols2/
  19. JavaScript engine (Wikipedia)
    http://en.wiki­pedia.org/wiki/Ja­vaScript_engi­ne
  20. Rhino (JavaScript engine)
    http://en.wiki­pedia.org/wiki/Rhi­no_(JavaScrip­t_engine)
  21. Rhino: JavaScript for Java
    http://www.mo­zilla.org/rhi­no/
  22. Java HotSpot VM Options
    http://www.ora­cle.com/technet­work/java/java­se/tech/vmopti­ons-jsp-140102.html
  23. HugePages
    http://linux-mm.org/HugePages
  24. Tuning big java heap and linux huge pages
    http://www.ti­kalk.com/alm/fo­rums/tuning-big-java-heap-and-linux-huge-pages
  25. How do I set up hugepages in Red Hat Enterprise Linux 4
    http://magazi­ne.redhat.com/2007/05­/29/how-do-i-set-up-hugepages-in-red-hat-enterprise-linux-4/
  26. Java SE Tuning Tip: Large Pages on Windows and Linux
    http://blogs.sun­.com/dagastine/en­try/java_se_tu­ning_tip_large
  27. Translation lookaside buffer
    http://en.wiki­pedia.org/wiki/Tran­slation_looka­side_buffer
  28. Physical Address Extension
    http://en.wiki­pedia.org/wiki/Phy­sical_Address_Ex­tension
  29. Java HotSpot VM Options
    http://www.ora­cle.com/technet­work/java/java­se/tech/vmopti­ons-jsp-140102.html
  30. Amdahl's law
    http://en.wiki­pedia.org/wiki/Am­dahl_law
  31. Garbage collection (computer science)
    http://en.wiki­pedia.org/wiki/Gar­bage_collecti­on_(computer_sci­ence)
  32. Dr. Dobb's | G1: Java's Garbage First Garbage Collector
    http://www.drdob­bs.com/article/prin­tableArticle.jhtml?ar­ticleId=219401061­&dept_url=/ja­va/
  33. Java's garbage-collected heap
    http://www.ja­vaworld.com/ja­vaworld/jw-08–1996/jw-08-gc.html
  34. Compressed oops in the Hotspot JVM
    http://wikis.sun­.com/display/Hot­SpotInternals/Com­pressedOops
  35. 32-bit or 64-bit JVM? How about a Hybrid?
    http://blog.ju­ma.me.uk/2008/10/­14/32-bit-or-64-bit-jvm-how-about-a-hybrid/
  36. Compressed object pointers in Hotspot VM
    http://blogs.sun­.com/nike/entry/com­pressed_objec­t_pointers_in_hot­spot
  37. Java HotSpot™ Virtual Machine Performance Enhancements
    http://downlo­ad.oracle.com/ja­vase/7/docs/techno­tes/guides/vm/per­formance-enhancements-7.html
  38. Using jconsole
    http://downlo­ad.oracle.com/ja­vase/1.5.0/doc­s/guide/manage­ment/jconsole­.html
  39. jconsole – Java Monitoring and Management Console
    http://downlo­ad.oracle.com/ja­vase/1.5.0/doc­s/tooldocs/sha­re/jconsole.html
  40. Great Computer Language Shootout
    http://c2.com/cgi/wi­ki?GreatCompu­terLanguageSho­otout
  41. x86–64
    http://en.wiki­pedia.org/wiki/X86–64
  42. Physical Address Extension
    http://en.wiki­pedia.org/wiki/Phy­sical_Address_Ex­tension
  43. Java performance
    http://en.wiki­pedia.org/wiki/Ja­va_performance
  44. 1.6.0_14 (6u14)
    http://www.ora­cle.com/technet­work/java/java­se/6u14–137039.html?ssSou­rceSiteId=otncn
  45. Update Release Notes
    http://www.ora­cle.com/technet­work/java/java­se/releasenotes-136954.html
  46. Java virtual machine: 4.10 Limitations of the Java Virtual Machine
    http://java.sun­.com/docs/book­s/jvms/second_e­dition/html/Clas­sFile.doc.html#88659
  47. Java™ Platform, Standard Edition 7 Binary Snapshot Releases
    http://dlc.sun­.com.edgesuite­.net/jdk7/bina­ries/index.html
  48. Trying the prototype
    http://mail.o­penjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  49. Better closures (for Java)
    http://blogs.sun­.com/jrose/en­try/better_clo­sures
  50. Lambdas in Java: An In-Depth Analysis
    http://www.in­foq.com/articles/lam­bdas-java-analysis
  51. Class ReflectiveOpe­rationExcepti­on
    http://downlo­ad.java.net/jdk7/doc­s/api/java/lan­g/ReflectiveO­perationExcep­tion.html
  52. Proposal: Indexing access syntax for Lists and Maps
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  53. Proposal: Elvis and Other Null-Safe Operators
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  54. Java 7 : Oracle pushes a first version of closures
    http://www.bap­tiste-wicht.com/2010/05­/oracle-pushes-a-first-version-of-closures/
  55. Groovy: An agile dynamic language for the Java Platform
    http://groovy­.codehaus.org/O­perators
  56. Better Strategies for Null Handling in Java
    http://www.sli­deshare.net/Step­han.Schmidt/bet­ter-strategies-for-null-handling-in-java
  57. Control Flow in the Java Virtual Machine
    http://www.ar­tima.com/under­thehood/flowP­.html
  58. Java Virtual Machine
    http://en.wiki­pedia.org/wiki/Ja­va_virtual_machi­ne
  59. ==, .equals(), compareTo(), and compare()
    http://leepoin­t.net/notes-java/data/expres­sions/22compa­reobjects.html
  60. New JDK7 features
    http://openjdk­.java.net/pro­jects/jdk7/fe­atures/
  61. Project Coin: Bringing it to a Close(able)
    http://blogs.sun­.com/darcy/en­try/project_co­in_bring_close
  62. ClosableFinder source code
    http://blogs.sun­.com/darcy/re­source/Projec­tCoin/Closeable­Finder.java
  63. Joe Darcy blog about JDK
    http://blogs.sun­.com/darcy
  64. Java 7 – more dynamics
    http://www.bap­tiste-wicht.com/2010/04­/java-7-more-dynamics/
  65. ArrayList (JDK 1.4)
    http://downlo­ad.oracle.com/ja­vase/1.4.2/doc­s/api/java/util/A­rrayList.html

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.