29 marca 2008

Trochę lektury o JRuby on Rails z GlassFish i powrót do OSGi - rozdział 4.3 Obiekt Bundle

Przeczytałem wczoraj artykuł dotyczący JRuby on Rails z GlassFishem - Rails Powered by the GlassFish Application Server i tylko niepotrzebnie się rozgrzałem. Artykuł wyśmienicie przedstawił zalety zastosowania serwera aplikacji Java EE - GlassFish, aby udostępnić funkcjonalność niedostępną w typowych rozwiązaniach RoR bez wyrafinowanych "sztuczek" administracyjnych - klastrowanie, współdzielenie zasobów, prostsza administracja, w tym aktualizacja aplikacji, możliwość uruchomienia wielu aplikacji RoR na jednej instancji serwera Java EE. Co niestety mnie zmartwiło, to, że kiedy tylko już skończyłem "wstęp" i już nie mogłem doczekać się przykładu z wykorzystaniem usługi oferowanej przez GlassFisha, okazało się, że to, co postrzegałem jako "wstęp" faktycznie było całym artykułem! I po co mi to było?! Tylko się człowiek niepotrzebnie napalił. Nie zamierzam jeszcze rozpoznawać tematu samodzielnie, ale ciekawym czy ktoś z czytających ten wpis ma doświadczenie w tego typu instalacjach, gdzie korzysta się z serwera aplikacji Java EE (zapewne GlassFish jako gem) i korzysta z jego usług w aplikacji RoR. Może jest tak, że osoby z kręgu RoR nie widzą potrzeby zastosowania serwera aplikacji Java EE. Dlaczego? Może chociaż ktoś rozważa taką integrację? Kiedyś pewnie i samemu spróbuję, ale na razie zawieszam temat. Oczywiście artykuł warty uwagi, chociaż należy podejść ze swoimi oczekiwaniami bardzo skromnie. Uaktualniłem w międzyczasie JRuby i popróbowałem się z kilkoma poleceniami, ale zdecydowanie za mało dla wymagających. Na zakończenie artykułu pozwoliłem sobie na skomentowanie go i wysłanie uwag do autorów. Nieoczekiwanie już dzisiaj dostałem od jednego z autorów - Ricka Palkovica - odpowiedź:
 Thanks for your comments on our article "Rails Powered by the GlassFish Application Server."

As you noted, the article really focuses on the use of GlassFish for development and deployment
of JRuby applications, not on how to use JRuby. For information about JRuby on Rails and using
it with a Java code, you might want to read:

"JRuby and the Java Platform" by Monica Powlan
(http://java.sun.com/developer/technicalArticles/scripting/jruby/index.html)

or the JRuby tutorials at http://jruby.codehaus.org/Tutorials

If you're a NetBeans user, you also might want to look at
"How to Work with JRuby in the NetBeans IDE"
(http://wiki.netbeans.org/NetbeansedJRuby)

Also, check out Arun's blog, where he often gives examples with JRuby and NetBeans.
(http://blogs.sun.com/arungupta)
Also, his webcasts, for example "First JRuby App in GlassFish:
(http://download.java.net/javaee5/screencasts/jruby-in-glassfish/)

Hope this helps,
-- Rick
Pewnie, że "helps", ale nie chodziło zupełnie o samo użycie JRuby, ale raczej o jego integrację z Java EE 5, czyli stworzenie aplikacji w ten sposób, że jej część będzie bazowała na RoR, a część na Korporacyjnej Javie. Odkładam temat na (przysłowiowe) później.

Jakby tego było mało lektura Java is losing the battle for the modern Web. Can the JVM save the vendors? dała na zakończenie dużo do myślenia w kontekście przyszłego rozpoznawania tematu JRuby, Ruby on Rails i GlassFish. Nie zamierzam streszczać artykułu tutaj i zachęcam do jego lektury. Wydaje się, że komuś ta Java już się przejadła. Ciekawe czasy.

Po krótkiej przerwie, wracam do OSGi i jego atrakcji. Pod młotek pójdzie rozdział 4.3 o obiekcie Bundle.

Każdy pakunek reprezentowany jest w środowisku uruchomieniowym OSGi przez swojego "partnera" w postaci obiektu org.osgi.framework.Bundle, który posiada unikatowy identyfikator typu long przypisany przez Platformę OSGi. Identyfikator pakunku jest niezmienny przez cały cykl rozwojowy pakunku (wliczając w to wszelkie operacje związane z jego stanem, w tym i jego aktualizację oraz ponowne uruchomienie środowiska OSGi). Odinstalowanie pakunku i jego ponowna instalacja skutkuje utworzeniem nowego identyfikatora. Identyfikatory pakunków są przydzielane w rosnącym porządku. Zarządzanie pakunkiem odbywa się właśnie poprzez obiekt Bundle zazwyczaj przez dedykowany przez Platformę OSGi pakunek zarządczy (Management Agent).

Poza identyfikatorem pakunku wyróżnia się unikatowy identyfikator położenia pakunku w Systemie, który jest przypisywany podczas instalacji i zazwyczaj interpretowany jest jako URL do pliku jar zawierającego pakunek (podkreśla się słowo zazwyczaj, gdyż nie jest to wymagane). Podobnie jak z identyfikatorem pakunku identyfikator położenia nie zmienia się w trakcie cyklu rozwojowego pakunku.

Symboliczna nazwa pakunku (ang. bundle symbolic name) jest nazwą określaną przez dostawcę pakunku poprzez nagłówek Bundle-SymbolicName. Para {wersja pakunku, nazwa symboliczna pakunku} jest unikatowa w ramach Systemu OSGi.

Metoda Bundle.getBundleId() zwraca identyfikator pakunku.

Metoda Bundle.getLocation() zwraca identyfikator położenia pakunku w Systemie.

Metoda Bundle.getSymbolicName() zwraca symboliczną nazwę pakunku, jaką przypisano przez jego dostawcę.

Pakunek może znajdować się w 6 stanach:
  • INSTALLED - pakunek poprawnie zainstalowany w Systemie
  • RESOLVED - wszystkie klasy Java wymagane przez pakunek są dostępne; pakunek jest gotowy do uruchomienia lub właśnie został zatrzymany
  • STARTING - pakunek jest uruchamiany i wywołanie metody start() aktywatora (BundleActivator.start()) jeszcze nie zakończyło się; wykorzystanie polityki uruchomienia może spowodować wstrzymanie pakunku w tym stanie (więcej o tym niebawem)
  • ACTIVE - pakunek został uruchomiony; wykonanie metody start() aktywatora zakończyło się poprawnie
  • STOPPING - pakunek jest zatrzymywany i wywołanie metody stop() aktywatora (BundleActivator.stop()) nie zakończyło się jeszcze
  • UNINSTALLED - pakunek usunięto z Systemu, niemożliwa zmiana stanu
Pakunek powinien wykonywać swoją pracę wyłącznie w stanie STARTING, ACTIVE lub STOPPING. Pakunek w stanie UNINSTALLED nie może zmienić swojego stanu (wymagana jest ponowna instalacja, co w kontekście nowego identyfikatora powoduje, że dla systemu jest to całkowicie inny pakunek). Zmiana stanu jest utrwalana przez Szkielet OSGi aż do odinstalowania (usunięcia) pakunku.

Metoda Bundle.getState() zwraca aktualny stan pakunku.

Jedynie Szkielet OSGi może powoływać obiekty Bundle do życia, które istnieją wyłącznie w ramach Platformy OSGi, który je powołał.

I na koniec krótki przykład dla utrwalenia wiedzy. Różnica między tym przykładem, a poprzednimi leży w dodaniu kilku System.out.println.
 package pl.jaceklaskowski.osgi;

import java.util.logging.Logger;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class AktywatorPakunku implements BundleActivator {

Logger log = Logger.getLogger(AktywatorPakunku.class.getName());

public void start(BundleContext bundleContext) throws Exception {
log.info("start() wykonano - rozpoczynam pracę");
Bundle bundle = bundleContext.getBundle();
long bundleId = bundle.getBundleId();
String bundleLocation = bundle.getLocation();
String bundleSymbolicName = bundle.getSymbolicName();
System.out.println("------------------------------------");
System.out.println("Charakterystyka aktywowanego pakunku:");
System.out.println(" Identyfikator: " + bundleId);
System.out.println(" Identyfikator położenia: " + bundleLocation);
System.out.println(" Nazwa symboliczna: " + bundleSymbolicName);
System.out.println("------------------------------------");
}

public void stop(BundleContext bundleContext) throws Exception {
log.info("stop() wykonano - czyszczę po sobie");
}

}
Polecenia są identyczne, za wyjątkiem wykonania aktualizacji po tym jak zmieniłem wcięcie w System.out.println.
 jlaskowski@dev /cygdrive/c/apps/felix
$ java -jar bin/felix.jar

Welcome to Felix.
=================

Enter profile name: notatnik

DEBUG: WIRE: 1.0 -> org.osgi.service.packageadmin -> 0
DEBUG: WIRE: 1.0 -> org.osgi.service.startlevel -> 0
DEBUG: WIRE: 1.0 -> org.ungoverned.osgi.service.shell -> 1.0
DEBUG: WIRE: 1.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 1.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 2.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 2.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 3.0 -> org.osgi.service.obr -> 3.0
DEBUG: WIRE: 3.0 -> org.osgi.framework -> 0
-> DEBUG: WIRE: 3.0 -> org.apache.felix.shell -> 1.0
ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.3)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.2)
-> install file:c:/projs/osgi/spring-osgi-lifecycle/target/spring-osgi-lifecycle-1.0.jar
Bundle ID: 4
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.3)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.2)
[ 4] [Installed ] [ 1] Spring OSGi Bundle (1.0)
-> start 4
DEBUG: WIRE: 4.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 4.0 -> pl.jaceklaskowski.osgi.impl -> 4.0
DEBUG: WIRE: 4.0 -> pl.jaceklaskowski.osgi -> 4.0
2008-03-27 13:21:03 pl.jaceklaskowski.osgi.AktywatorPakunku start
INFO: start() wykonano - rozpoczynam pracę
------------------------------------
Charakterystyka aktywowanego pakunku:
Identyfikator: 4
Identyfikator położenia: file:c:/projs/osgi/spring-osgi-lifecycle/target/spring-osgi-lifecycle-1.0.jar
Nazwa symboliczna: pl.jaceklaskowski.osgi.spring-osgi-lifecycle
------------------------------------
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.3)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.2)
[ 4] [Active ] [ 1] Spring OSGi Bundle (1.0)
-> stop 4
2008-03-27 13:21:51 pl.jaceklaskowski.osgi.AktywatorPakunku stop
INFO: stop() wykonano - czyszczŕ po sobie
-> start 4
2008-03-27 13:22:03 pl.jaceklaskowski.osgi.AktywatorPakunku start
INFO: start() wykonano - rozpoczynam pracŕ
------------------------------------
Charakterystyka aktywowanego pakunku:
Identyfikator: 4
Identyfikator po-o¬enia: file:c:/projs/osgi/spring-osgi-lifecycle/target/spring-osgi-lifecycle-1.0.jar
Nazwa symboliczna: pl.jaceklaskowski.osgi.spring-osgi-lifecycle
------------------------------------
Tutaj następuje aktualizacja pakunku i sprawdzenie, czy wszystkie identyfikatory są jak poprzednio.
 -> update 4 file:c:/projs/osgi/spring-osgi-lifecycle/target/spring-osgi-lifecycle-1.0.jar
2008-03-27 13:22:50 pl.jaceklaskowski.osgi.AktywatorPakunku stop
INFO: stop() wykonano - czyszczę po sobie
DEBUG: WIRE: 4.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 4.0 -> pl.jaceklaskowski.osgi.impl -> 4.0
DEBUG: WIRE: 4.0 -> pl.jaceklaskowski.osgi -> 4.0
2008-03-27 13:22:50 pl.jaceklaskowski.osgi.AktywatorPakunku start
INFO: start() wykonano - rozpoczynam pracŕ
------------------------------------
Charakterystyka aktywowanego pakunku:
Identyfikator: 4
Identyfikator położenia: file:c:/projs/osgi/spring-osgi-lifecycle/target/spring-osgi-lifecycle-1.0.jar
Nazwa symboliczna: pl.jaceklaskowski.osgi.spring-osgi-lifecycle
------------------------------------
-> shutdown
-> 2008-03-27 13:23:05 pl.jaceklaskowski.osgi.AktywatorPakunku stop
INFO: stop() wykonano - czyszczę po sobie
Pytanie dla wytrwałych: Ile stanów jest w cyklu rozwojowym pakunku? oraz trochę trudniejsze - Jakie stany wyróżnia specyfikacja OSGi w cyklu rozwojowym pakunku? Nagród nie przewiduje się.