JRuby, Script Engine i Java


JRuby, „w 100% czysta implementacja Javy na bazie języka Ruby”, jak określają go twórcy, dostarcza zalety obydwu tych języków. W artykule tym skupię się jednak tylko na jednym ciekawym aspekcie JRubiego, czyli wykorzystania go bezpośrednio w aplikacjach stricte javowych poprzez Script Engine.

Często programiści zostawiają w tworzonych aplikacjach różnego rodzaju furtki, by w razie awarii, jakiegoś nietypowego zachowania, mogli dojść do przyczyn. Czasem też dane w programie ewoluują, i okazuje się, że brakuje nam informacji by wykonać daną operację, np. stworzyć zaległy raport czy fakturę.

Java dostarcza środków do sprawnej realizacji tego typu zadań – silnik skryptowy. Dzięki niemu, możemy bez ingerencji w kod aplikacji czy magicznych sztuczek na poziomie interfejsu dostać się do interesujących nas danych i zmodyfikować je. I to wszystko bez potrzeby restartowania serwera czy przebudowywania kodu zależnie od problemu. Wystarczy umieścić w aplikacji Script Engine i interfejs do jego obsługi. Istnieje cały projekt zajmujący się dostarczaniem bibliotek do obsługi języków skryptowych w Javie – zainteresowanym polecam stronę https://scripting.dev.java.net/

Java dostarcza dwa rodzaje API do obsługi skryptów. Są to:

  • JSR 223 Scripting API
  • Bean Scripting Framework (BSF) API

Java 1.6 standardowo dostarcza engine do obsługi javaskryptu, jeżeli chcemy wykorzystywać JSR 223 do JRubiego, musimy dodać odpowiednie biblioteki do naszej aplikacji:

Przyjrzyjmy się jak działa Script Engine na przykładzie*

Powyższa klasa tworzy engine JRuby i próbuje wykonać skrypt napisany w tym języku. Ten prosty przykład wypisuje tylko powitanie, jednak poprzez jego parametryzację możemy w łatwy sposób uzyskać obsługę skomplikowanych skryptów.

Poza ewaluacją skryptu Script Engine pozwala nam na wywoływanie konkretnych metod zdefiniowanych w skrypcie, w zależności od potrzeb.

//klasa Javy **

// myruby.rb **

Klasa wczytuje plik myruby.rb, który definiuje metodę fact obliczającą silnię dla podanego parametru. Po ewaluacji, engine pozwala nam ustawić lokalne zmienne [1] i wykorzystać je do wywołania metody zdefiniowanej w pliku [2]. Dodatkowo zmienne możemy ustawiać wywołując kolejne ewaluacje [3]. Engine.getBindings pozwala nam odczytać ustawione w skrypcie zmienne [4].

Wszystko fajnie, ale co z modyfikowaniem danych i obiektów Javy? Tu wkraczamy bardziej w samą składnię JRubiego niż istotę Script Engine jednak pozwolę sobie na prosty przykład

//example1.rb***

Wczytując i wykonując powyższy skrypt powinniśmy dostać okienko pokazujące nam informację o aktualnym czasie. Linia [1] jest najistotniejsza w powyższym pliku. To ona pozwala nam na dostęp do standardowych klas Javy. Od tego momentu możemy używać klas Javy poprzez podanie pełnych ścieżek i nazw klas. Aby nie podawać ciągle pakietu klasy możemy użyć słowa import [2]. Linia [3] tworzy nam nowego JFrame’a, [4] JLabel z informacją o czasie. Operator :: używany jest do dostępu do statycznych metod i pól klasy. Do niestatecznych metod dostęp uzyskujemy przez standardową kropkę [5]. Aby uniknąć wielu importów klas, można importować całe pakiety dyrektywą include_package a klasy z poza projektu dostępne są po dodaniu pliku jar do skryptu (dyrektywa require). Tym sposobem, możemy w skrypcie dodać brakujące dane, stworzyć potrzebny obiekt i usunąć zbędne dane (np generowanie zaległej faktury po zmianie oferowanych produktów).

Załóżmy, że mamy działającą aplikację na serwerze, z zaimplementowanym Script Engine w postaci formularza z polem tekstowym, który służy do wpisania skryptu. Submit formularza wywołuje metodę ewaluującą wpisany tekst.

Naszą nietypową sytuacją jest konieczność wykonania operacji na fakturze, jej wydrukowanie i usunięcie

// skrypt wpisany do pola tekstowego

//funkcja obsługująca submit formularza

}

// klasa obsługi faktur

class InvoiceRepository {

InvoiceRepository getInstance() {…}

List<Invoice> getInvoices() {….}

void printInvoice(Invoice invoice) {….}

boolean removeInvoice(Invoice invoice {…}

}

Sytuacji wyjątkowych może być całe mnóstwo, a wszystkie je możemy obsłużyć w analogiczny sposób – bez konieczności modyfikacji nawet kawałka kodu naszej aplikacji. Jeżeli dodamy do tego, że aplikacja nie dostarcza bezpośrednio GUI do obsługi funkcji removeInvoice, ujawnia się nam prawdziwa potęga Script Engine.

JRuby pozwala na dużo więcej, wszystkich zainteresowanych odsyłam na stronę projektu http://jruby.codehouse.org

Materiały źródłowe:

* http://java.sun.com/developer/technicalArticles/scripting/jruby/

** http://www.ics.muni.cz/~makub/ruby/

*** http://wiki.jruby.org/wiki/Calling_Java_from_JRuby

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *