OSGi bez irytacji

post_img

Niedawno, tworząc pluginy do JIRA, natknąłem się na dość irytujący problem, który objawiał się dziwnymi komunikatami o niemożności zaimportowania niektórych pakietów. Ponieważ pluginami tymi są moduły OSGi, postanowiłem nieco zgłębić to zagadnienie.

Na początek słowo wstępu o tym, czym jest OSGi.  Open Services Gateway Initiative –  bo tak należy rozwinąć ten skrót – to nic innego jak system dynamicznych modułów dla Java. Definiuje architekturę modułowych aplikacji, które mogą być uruchamiane w kontenerach wspierających OSGi. Do tychże zaliczyć możemy

Po co dodatkowy kontener? Co daje modularność? Pytania są jak najbardziej słuszne. Jeżeli idzie o kontenery, to są one tworzone specjalnie po to, by umożliwić zarządzanie aplikacjami modularnymi w sposób najwygodniejszy z możliwych – podmienianie modułów, ich dołączanie i odłączanie bez konieczności restartu całego serwera.  Umożliwiają zarządzanie zależnościami między modułami i pozwalają budować aplikacje najróżniejszych typów – od mobilnych do RIA.

Coraz większa popularność OSGi z pewnością ma też coś wspólnego ze wsparciem tego modelu przez Eclipse IDE. Środowisko to posiada wbudowane wizardy do tworzenia modułów OSGi, jak i własny kontener do ich testowania i debugowania.

Utwórzmy prosty moduł OSGi  (zwany Bundle) właśnie w środowisku Eclipse (Europa). Aby to zrobić, wybieramy New->Project->Plug-in Development -> Plug-in Project. Dostaniemy do wypełnienia wizard, który utworzy dla nas przykładowy projekt.

Nazwa: TestBundle

Target Platform: OSGi framework -> standard

Klikamy Next, Next. Jako szablon wybieramy Hello OSGi Bundle i klikamy next do końca kreatora.

Po zakończeniu powinniśmy dostać projekt z klasą Activator, plikiem MANIFEST.FM oraz build.properties.

Przyjrzyjmy się wszystkim tym plikom.

Activator

public class Activator implements BundleActivator {

 /*
 * (non-Javadoc)
 * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
 */
 public void start(BundleContext context) throws Exception {
 System.out.println("Hello World!!");
 }

 /*
 * (non-Javadoc)
 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
 */
 public void stop(BundleContext context) throws Exception {
 System.out.println("Goodbye World!!");
 }

}

Activator to klasa implementująca BundleActivator – interfejs, który wskazuje, jak nasz moduł ma być startowany i zatrzymywany. W metodach start/stop wykonywane są operacje dostarczające danych do dalszej pracy i sprzątające przy zamknięciu (nawiązanie połączenia z bazą, zakończenie połączenia)

MANIFEST.FM

Na pierwszy rzut oka, plik MANIFEST to nic nadzwyczajnego, jest on zaszyty w niemal każdej bibliotece JAR. Dla mnie miał on jednak szczególne znaczenie, gdyż był przyczyną owych nieszczęsnych komunikatów o nieprawidłowych pakietach. Wygenerowany plik ma następującą treść

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TestBundle Plug-in
Bundle-SymbolicName: TestBundle
Bundle-Version: 1.0.0
Bundle-Activator: testbundle.Activator
Import-Package: org.osgi.framework;version="1.3.0"

Pierwsze, co powinno rzucić się nam w oczy, to dyrektywa Bundle-Activator. Wymieniona jest tam nasza klasa Activator.  Sugeruje to,  że plik MANIFEST, może odgrywać dużo większą rolę niż się wydaje. Prześledźmy więc kolejno wszystkie dyrektywy

  • Bundle-ManifestVersion: 2 – informuje o tym, z jakiej wersji specyfikacji OSGi korzysta ten bundle. 2 oznacza kompatybilność z release 4 specyfikacji, 1 z wcześniejszymi wersjami
  • Bundle-Name: – nazwa bundle w formie czytelnej dla człowieka
  • Bundle-SymbolicName – nazwa bundle, pod którą będzie widziany z innych modułów
  • Bundle-Version: wersja tego bundle (w kontenerach może jednocześnie działać kilka wersji tego samego bundle’a).
  • Bundle-Activator: klasa aktywatora bundle’a, czyli z jakiej klasy należy skorzystać przy uruchamianiu i zatrzymywaniu modułu
  • Import-Package:  tu definiujemy jakie pakiety chcemy zaimportować do naszego bundle’a. Możliwe jest importowanie pakietów z innych modułów, a więc ich wzajemne wiązanie i wykorzystywanie funkcjonalności

W powyższym zestawieniu brak jednak dyrektywy najbardziej istotnej z punktu widzenia mojego problemu. A mianowicie:

  • Export-Package: tu określamy jakie pakiety z naszego bundle’a chcemy wyeksportować. Tylko wyeksportowane pakiety mogą być importowane przez inne moduły.

Plik manifestu może posiadać jeszcze parę innych ważnych dyrektyw. Są to

  • Private-Package: tu wymienione są pakiety, których nie chcemy udostępnić. Początkowo wydaje się to nie potrzebne, ale przecież może zaistnieć sytuacja, w której będziemy chcieli wyeksportować klasy pakietu pl.atena.* ale nie pakietu pl.atena.secret.*.  Header ten nie mieści się jednak w oficjalnej specyfikacji, wykorzystywany jest przez narzędzie BND do budowania bundle’a. Może to być różnie interpretowane w różnych kontenerach
  • Bundle-Classpath: tu możemy określić, gdzie w naszej bibliotece znajdują się inne wykorzystywane biblioteki. Domyślnie specyfikacja zakłada, że wszelkie dodatkowe pliki jar są w korzeniu bundle’a.
  • Ignore-Package: tu wymienione są pakiety ignorowane w trakcie budowania za pomocą narzędzia BND. Podobnie jak Private-Package nie jest to oficjalny nagłówek.

build.properties

Plik ten określa tylko ścieżki do wykorzystania w trakcie budowania modułu.

Aplikacje modularne robią coraz większą furorę, więc przyszłość OSGi wydaje się jasna i pewna. Niewykluczone, że jeszcze nie raz przyjdzie mi spotkać się z nimi. Z pewnością podzielę się wtedy swoimi doświadczeniami w ich tworzeniu.

Źródła:

Dodaj komentarz

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