06 maja 2008

Egzaminy OSGi i EJB3 na JavaBlackBelt oraz łyk OSGi - rozdział 4.4 Kontekst pakunku specyfikacji OSGi 4.1

No i proszę, do czegoś się przydałem. W ostatnim wydaniu JavaBlackBelt Newsletter - May 08 pojawiała się wzmianka o możliwości zdobycia zielonego pasa bez wymaganego wcześniej udzielania się w serwisie (Get your green belt for free), a w sekcji Moderator spotlight pojawiłem się i ja!

Congratulations to all our moderators for their hard work, with special mention going to:
  • Benedikt for the Java 3D - Basic exam, JAX-B exam and c# exams
  • Jacek & Toni for the OSGi exam
  • Amzad for racking up 10.000 contributions points
  • Yours truly for the Algo - Basic and Programming Aptitude exams
Ten Jacek od egzaminu OSGi to nieskromnie mówiąc właśnie ja! Pracuję nad tym, aby w końcu egzamin pojawił się publicznie z pełną punktacją i aby wreszcie można było rozpoznać zadowolenie użytkowników z jakości pytań. Może Ty spróbuj tym razem i napisz, co Ci się podobało, a czego brakuje? Zamieniam się w słuch.

Skoro mnie wspomniano pomyślałem, że może w końcu uda mi się zdać egzamin z EJB 3.0 Basic, który ostatnio oblałem. Z niektórymi pytaniami się nie zgodziłem i napisałem do autorów, ale na razie ni widu ni słychu, a ja co oblałem to moje. Szczęśliwie upłynął już czas od ostatniego podejścia wymagany, aby podejść ponownie i tym razem...Congratulation, you successfully passed this test! z wynikiem 15/ 17 = 88% poprawnych odpowiedzi. Poległem na 2 pytaniach i na swoje usprawiedliwienie napiszę, że przy jednym dłużej się zastanawiałem (i nawet miałem wybrać poprawną odpowiedź!). Dwa pytania na których poległem to:

During stateful session bean activation, transient fields are treated the same way as during serialization, that is, transient fields are initialized to their default values (null for object references, 0 for Integers, etc).

oraz

Which of the following code snippets mark the OrderManager as a remote interface and OrderManagerBean as a stateless session bean?

Odpowiedzi nie podam z oczywistych względów, ale mogę skierować do rozdziału 4.2.1 Instance Passivation and Conversational State (strona 63) oraz 4.6.6 Session Bean's Business Interface (strona 94) w specyfikacji EJB 3.0. Na uwagę zasługuje fakt, że autorem drugiego pytania o biznesowym interfejsie zdalnym oraz wielu innych, które mi się przytrafiły podczas egzaminu jest nasz rodak - Mateusz Kwasniewski. Gość już wymiata w technologiach javowych mając brązowy pas.

W euforii podszedłem do kolejnego egzaminu w ścieżce do zielonego pasa, którym okazał się być egzamin Java SE Collections i który mnie w tylu miejscach zaskoczył, że ostatecznie wynik wcale mnie nie zdziwił Sorry, you failed this test, tj. 14/ 22 = 63% poprawnych przy 80% wymaganych. Na dzisiaj dosyć tego egzaminowania.

Pora wrócić do przyjemniejszych rzeczy, jak na przykład specyfikacji OSGi. Zacznę od odpowiedzi na pytanie MZ, który w komentarzu do wpisu Specyfikacja OSGi - rozdział 4.3 Obiekt Bundle - dokończenie zapytał:

Jest w końcu metoda start/stop(int) czy start/stop(BundleContext) czy obie?

Odpowiedź zdawkowa: obie.

Odpowiedź znacznie dłuższa: W konteście OSGi głównymi bytami, z którymi przyjdzie nam pracować będą typu org.osgi.framework.Bundle lub org.osgi.framework.BundleActivator. Pierwszy z nich Bundle reprezentuje pakunek OSGi i za pomocą odpowiednich metod możemy ustawić go w odpowiednim stanie (wyróżniamy 6 stanów pakunku/etapów w cyklu rozwojowym pakunku). Jednymi z takich metod, które zmieniają etap rozwojowy pakunku są start() oraz stop(). Obie należą do typu (interfejsu) org.osgi.framework.Bundle.
Za pomocą BundleActivator dostawca pakunku ma możliwość zmiany sposobu uruchomienia i zatrzymania pakunku. Podczas uruchomienia pakunku wywoływana jest metodą start(BundleContext context) aktywatora, podczas gdy przy jego zatrzymaniu wywołana zostanie metoda stop(BundleContext context). Aktywator pakunku deklarowany jest w manifeście w nagłówku Bundle-Activator.
W zależności od kontekstu rozmowy metody start() oraz stop() mogą pojawić się odnośnie aktywatora pakunku, albo samego pakunku. Uwaga na kontekst!

A skoro jestem przy kontekście to zajmę się tematem rozdziału 4.4 Kontekst pakunku specyfikacji OSGi 4.1.

Między Platformą OSGi a samym pakunkiem istnieje więź w postaci kontekstu uruchomieniowego - egzemplarza org.osgi.framework.BundleContext. Kontekst jest tworzony podczas uruchomienia pakunku i przekazywany jako parametr wejściowy do wspomnianej wcześniej metody start(BundleContext context) aktywatora. Każdy pakunek ma swój prywatny egzemplarz BundleContext, który nie powinien być przekazywany innym pakunkom i służy do instalowania innych pakunków OSGi, pozyskiwania informacji o środowisku i zainstalowanych pakunkach, rejestracji usług dostarczanych przez pakunek oraz korzystania z usług innych oraz nasłuchiwania zdarzeń emitowanych przez Platformę OSGi. Zakończenie wykonania metody stop(BundleContext context) powoduje wyłączenie BundleContext i jego późniejsze użycie zawsze zakończy się zgłoszeniem wyjątku IllegalStateException.

BundleContext dostarcza metod, za pomocą których możemy pozyskać informację o pakunkach zainstalowanych w Platformie OSGi.
  • getBundle() - zwraca egzemplarz Bundle związany z BundleContext (pakunek kontekstowy).
  • getBundles() - zwraca tablicę zainstalowanych pakunków
  • getBundle(long) - zwraca egzemplarz Bundle wskazywany przez identyfikator lub null, jeśli nie istnieje pakunek o danym identyfikatorze.
Platforma OSGi powinna dostarczać prywatną przestrzeń trwałą dla pakunku w postaci systemu plików, gdzie metoda getDataFile(String filename) zwraca plik odpowiadający podanej nazwie (filename) lub null, jeśli Platforma nie wspiera prywatnej przestrzeni plikowej. Wywołanie metody getDataFile z pustą nazwą pliku zwróci katalog główny przestrzeni.

Interfejs BundleContext udostępnia metodę getProperty(String), która zwraca wartość parametru systemowego lub javowego (podobnie, jak ma to miejsce w Javie z System.getProperty(String)). Wyróżnia się następujące parametry systemowe, wszystkie rozpoczynające się od przedrostka org.osgi.framework: version (dla wersji OSGi 4.1 wartością będzie 1.3), vendor, language, executionenvironment, processor, os.version, os.name, bootdelegation, system.packages oraz rozpoczynające się od przedrostka org.osgi.supports: framework.extension (cecha obowiązkowa implementacji OSGi), więc wartość true), bootclasspath.extension, framework.fragment (cecha obowiązkowa, więc wartość true), framework.requirebundle (cecha obowiązkowa, więc wartość true).

BundleContext udostępnia wiele innych metod, np. służących do rejestracji słuchaczy (obiektów nasłuchujących), pobranie referencji do usług czy ich zwolnienie, o czym będzie w kolejnych odsłonach specyfikacji OSGi.

W dużej mierze polecenia wydawane podczas pracy z wybraną Platformą OSGi to właśnie odpowiedniki wspomnianych metod, więc ich korzystanie rozpoczyna się już od pierwszych chwil pracy z Platformą OSGi, czy to jest Apache Felix, Eclipse Equinox czy Knoplerfish.

Przyjrzyjmy się poleceniu ps w Apache Felix (bądź ss w Equinox) w formie (aktywatora) pakunku, którego wynikiem jest właśnie lista zainstalowanych pakunków.
 package pl.jaceklaskowski.osgi;

import java.util.Dictionary;
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 context) throws Exception {
for (Bundle pakunek : context.getBundles()) {
Dictionary headers = pakunek.getHeaders();
String stan = zwrocNazweStanu(pakunek.getState());
String linia = String.format("[%1$4d] [%2$-11s] [ ?] %3$s (%4$s)", pakunek.getBundleId(), stan,
headers.get("Bundle-Name"), headers.get("Bundle-Version"));
System.out.println(linia);
}
}

/**
* TODO: Dokończyć obsługę pozostałych stanów
*
* @param identyfikator
* identyfikator stanu
* @return nazwa stanu dla podanego identyfikatora stanu (tylko dla ACTIVE oraz STARTING)
*/
private String zwrocNazweStanu(int identyfikator) {
switch (identyfikator) {
case Bundle.ACTIVE:
return "Active";
case Bundle.STARTING:
return "Starting";
}
return null;
}

public void stop(BundleContext bundleContext) throws Exception {
}

}
Pora zademonstrować przykład w akcji.
 jlaskowski@work /cygdrive/c/apps/felix
$ java -Dfelix.cache.profile=osgi-bundlecontext -jar bin/felix.jar

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

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

-> version
1.0.4
-> install file:/c:/projs/osgi/osgi-bundlecontext/target/osgi-bundlecontext-1.0.jar
Bundle ID: 4
-> start 4
DEBUG: WIRE: 4.0 -> org.osgi.framework -> 0
[ 0] [Active ] [ ?] System Bundle (1.0.4)
[ 1] [Active ] [ ?] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ ?] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ ?] Apache Felix Bundle Repository (1.0.3)
[ 4] [Starting ] [ ?] OSGi 4.4 Kontekst pakunku (1.0)
-> ps
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.4)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.1)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.1)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.3)
[ 4] [Active ] [ 1] OSGi 4.4 Kontekst pakunku (1.0)
-> shutdown
Pytanie konkursowe: Jaka jest zależność między interfejsami Bundle, BundleActivator i BundleContext? A teraz trochę trudniejsze: Dlaczego w wyniku działania aktywatora stan pakunku "OSGi 4.4 Kontekst pakunku" jest Starting? Nagród materialnych nie przewiduje się, ale publiczne wychwalenie wiedzy jak najbardziej. Ciekawe odpowiedzi, np. zawierające kod źródłowy, planuję umieścić w kolejnych wpisach (za zgodą autora).