Java Scripting with JavaScript

Jakiś czas temu pisałem o tym jaki to Jython jest świetny ze względu na to, że potrafi się świetnie integrować z Javą itd. Nadal uważam, że to dobra rzecz, ale po pewnym czasie używania tegoż zaczęła mnie irytować pewna sprawa – czas uruchamiania!

Na początku było tak, że za każdym razem przy uruchamianiu aplikacji kompilowałem skrypty. Myślałem wówczas, że tworzenie interpretera i kombinowanie da się później wyeliminować kompilując skrypty do plików *.class. Tak się niestety nie stało.

Ostatnio usiłując poprawić czas uruchamiania aplikacji skompilowałem najpierw te kilka skryptów testowych do plików binarnych klas Javy i spróbowałem je załadować zupełnie pomijając tworzenie obiektu klasy PythonInterpreter. Życie nie okazało się jednak łaskawe i jak się okazało Jython utworzył mi klasę NazwaKlasy$py.class. Wszelkie próby załadowania tej klasy bez użycia Jythona skończły się niepowodzeniem… Sam Jython oczywiście potrafił bez problemów wczytać ten plik ale zajmowało to tyle samo czasu ile wcześniej (ok. 5 sekund), zatem startowanie aplikacji nie przyspieszyło.

Postanowiłem zatem poszukać pomocy gdzie indziej – zmienić język skryptowy. Po krótkim poszukiwaniu stwiedziłem, że chyba najbardziej trafnym językiem skryptowym dla Javy będzie.. JavaScript 🙂
Szybko zabrałem się za pisanie testowych skryptów i kodu uruchamiającego. Kiedy opanowałem samo wykonywanie, przyszedł czas na kompilację i ładowanie. Po kilkudziesięciu minutach z dokumentacją i przykładowymi kawałkami kodu udało mi się stworzyć program kompilujący plij *.js do pliku *.class. Okazało się to być znacznie prostrze niż przypuszczałem gdyż wystarczył taki fragmencik:

String script = readSource("scripts/base.js");
CompilerEnvirons env = new CompilerEnvirons();
ClassCompiler classCompiler = new ClassCompiler(env);
Object[] classes = classCompiler.compileToClassFiles(script, "scripts/base.js", 1, "Base");

I tyle! Funkcja readScript(..) wczytuje oczywiście z pliku kod skryptu. Wówczas tablica obiektów classes zawiera nazwy klas oraz skompilowane wersje binarne następujące kolejno po sobie. Wystarczy już tylko wpisać dane do odpowiednich plików *.class:

for(int i=0; i < classes.length; i+=2) {
 String name = (String)classes[i];
 byte[] bytes = (byte[])classes[i+1];

 File out = new File("scripts/" + name + ".class");
 FileOutputStream os = new FileOutputStream(out);
 os.write(bytes);
 os.close();
}

Wersja binarna może być już łatwo odczytana przez Javę:

URL[] urls = new URL[] { new File("/path/to/scripts").toURI().toURL() };
URLClassLoader loader = new URLClassLoader(urls);
Class cl = Class.forName("Base", true, loader);

Na koniec aby wykonac skrypt można utworzyć obiekt przy pomocy klasy oraz wywołać funkcję exec():

Script scr = (Script) cl.newInstance();
Context cx = Context.enter();
Scriptable scope = cx.initStandardObjects();
scr.exec(cx, scope);

Na zakończenie powiem tylko, że czasowo kompilacja kilku skryptów nie robi praktycznie żadnej różnicy w działaniu programu. Jest to zatem duży plus dla silnika JavaScriptu od Mozilli 🙂 Warto również dodać, że nie traci się spejcalnie na funkcjonalności ponieważ z poziomu skryptów JS można również korzystać z klas Javy 😉

Później napiszę jeszcze jak ze skryptu wywoływać konkretne funkcje, bo to w sumie nadaje się na osobny wpis 😉

Reklamy

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Wyloguj / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Wyloguj / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Wyloguj / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Wyloguj / Zmień )

Connecting to %s