02 kwietnia 2008

Specyfikacja OSGi - rozdział 4.3 Obiekt Bundle - dokończenie

Kontynuuję lekturę specyfikacji OSGi i jej relacji w Notatniku. Tym razem zajmę się rozdziałem 4.3 Obiekt Bundle.

Interfejs org.osgi.framework.BundleContext przekazywany jako parametr wejściowy do metod aktywatora pakunku, udostępnia metody pozwalające na instalację nowych pakunków - installBundle(String), gdzie parametr wejściowy jest położeniem instalowanego pakunku (URL) oraz installBundle(String, InputStream) do instalacji pakunku dostarczanego przez wejściowy InputStream.

Jeśli spróbujemy zainstalować pakunek o zadanym identyfikatorze, który istnieje w systemie, zwrócony zostanie obiekt Bundle dla pakunku już zainstalowanego.

Instalacja pakunku odbywa się w sposób atomowy, tj. pakunek zostanie zainstalowany w całości, bądź w przypadku błędu system wróci do stanu sprzed instalacji. Stan pakunku jest trwały, w tym sensie, że zatrzymanie Platformy OSGi czy samej JVM nie wpływa na jego zmianę (jeśli jest ACTIVE to po zatrzymaniu Szkieletu OSGi i jego uruchomieniu, pakunek nadal będzie w stanie ACTIVE).

Pakunek przejdzie do stanu RESOLVED, jeśli wszystkie jego zależności zadeklarowane w manifeście (MANIFEST.MF) zostaną rozwiązane (=są dostępne w środowisku). W przypadku wystąpienia błędu podczas rozwiązywania zależności, metoda BundleActivator.start(BundleContext) zgłosi wyjątek org.osgi.framework.BundleException. Stan po uruchomieniu może być ACTIVE bądź STARTING w zależności od ustawień aktywacji (o której za moment).

Pakunek może zostać uruchomiony automatycznie podczas startu Szkieletu, jeśli konfiguracja pakunku zawiera opcję automatycznego uruchomienia.

Po uruchomieniu, pakunek musi zostać uaktywniony przez Szkielet, aby mógł rozpoczać swoją inicjalizację. Aktywacja może odbyć się w trybie opóźnionym (dopiero podczas wczytania jednej z udostępnianych klas) bądź gorliwym (natychmiast). Wyróżnia się, więc 3 ustawienia aktywacji pakunku - zatrzymany (nie zostanie uruchomiony automatycznie), uruchomiony z opóźnioną aktywacją oraz uruchomiony z natychmiastową aktywacją.

Interfejs Bundle udostępnia metodę start(int) w celu kontroli aktywacji pakunku.

Pakunki częściowe (ang. fragment bundles) nie mogą być uruchamiane i metoda start() zawsze zgłosi wyjątek BundleException podczas próby.

Aktywacja pakunku odbywa się poprzez wywołanie aktywatora pakunku (jego metody start()), jeśli istnieje. Wskazanie aktywatora następuje poprzez (opcjonalny) nagłówek Bundle-Activator. Można wyobrazić sobie sytuację, w której pakunek jest faktycznie biblioteką klas, co powoduje, że aktywator jest zbędny. Jeśli metoda start(BundleContext) zgłosi wyjątek, Szkielet OSGi zmieni stan pakunku na STOPPING oraz STOPPED bez wywołania metody stop(BundleContext) aktywatora. Metoda stop(BundleContext) jest zazwyczaj przeciwstawna do start(BundleContext), gdzie następuje zatrzymanie wszystkich aktywnych wątków i połączeń, chociaż niekonieczne jest odrejestrowanie usług i słuchaczy, gdyż one zostaną wyczyszczone przez Szkielet i tak.

Pakunki częściowe nie mogą definiować aktywatora.

Aktywacja pakunku może zostać przesunięta w czasie w zależności od polityki aktywacji określonej przez nagłówek Bundle-ActivationPolicy w manifeście. Jedynym dozwolonym parametrem nagłówka jest lazy, gdyż domyślnie (przy braku Bundle-ActivationPolicy) zakłada się natychmiastowe uruchomienie. Przy opóźnionej aktywacji, faktyczne uruchomienie pakunku nastąpi dopiero przy próbie wczytania udostępnianej przez niego klasy, czy to przez normalne wczytanie klasy przez zarządcę klas, bądź poprzez wywołanie metody org.osgi.framework.Bundle.loadClass(). Wczytanie udostępnianego zasobu nie aktywuje pakunku. Nagłówek Bundle-ActivationPolicy posiada dwa atrybuty: include oraz exclude, które pozwalają określić, które klasy wpływają na opóźnioną aktywację pakunku. Domyślną wartością dla include są wszystkie klasy, podczas gdy dla exclude żadna.
 Bundle-ActivationPolicy: lazy; exclude:="pl.jaceklaskowski.osgi.pomoc"
Interfejs Bundle udostępnia metodę stop(int) do zatrzymania pakunku. Wykonanie metody ustawia jego stan na RESOLVED. Parametr wejściowy określa, w jaki sposób pakunek może być aktywowany ponownie.

Próba zatrzymania pakunku częściego skutkuje zgłoszeniem wyjątku BundleException.

Wymaga się, aby pomyślne wykonanie metody stop(BundleContext) aktywatora powodowało zwolnienie wszystkuch zasobów, które mogłyby być uruchomione podczas wykonania odpowiadającej metody start(BundleContext) aktywatora. Aktywne wątki aplikacji po zatrzymaniu jej elementów OSGi nie mogą dalej korzystać z obiektów związanych z OSGi.
Pakiety z klasami/interfejsami udostępniane przez zatrzymany pakunek są wciąż dostępne i stąd zaleca się udostępniać wyłącznie interfejsy.

Interfejs Bundle udostępnia metodę update() oraz update(InputStream) do uaktualnienia pakunku. Wyeksportowane interfejsy/klasy są dostępne w starszej wersji dla pakunków, które korzystają z nich, aż do momentu ich zwolnienia, podczas gdy nowe wywołania korzystają już z nowszej ich wersji.

Metoda Bundle.uninstall() służy do odinstalowania pakunku. Odinstalowanie pakunku oznacza całkowite jego usunięcie i kolejna instalacja tego samego pakunku będzie traktowane jak instalacja zupełnie innego pakunku. Stan Szkieletu musi być taki sam, jak gdyby pakunek w ogóle nie został zainstalowany.

Szkielet OSGi jest zobowiązany do utrzymywania czasu ostatniej zmiany stanu, który jest dostępny przez metodę Bundle.getLastModified(). Jednym z potencjalnych zastosowań metody jest scenariusz, w którym jeden pakunek korzysta z własnej kopii zasobów udostępnianych przez inny i wyłącznie za pomocą getLastModified() następuje aktualizacja lokalnych zasobów (w przeciwnym przypadku koniecznym byłoby monitorowanie każdej kopii zasobu).

Interfejs Bundle udostępnia metody do pobrania nagłówków manifestu - getHeaders() oraz getHeader(String). Metoda getHeaders() zwraca słownik z nagłówkami i ich wartościami w bieżącej konfiguracji ustawień międzynarodowych (ang. locale). Jeśli nie znaleziono wartości klucza dla danego ustawienia międzynarodowego metoda getHeaders() zwróci wartości bez wiodącego znaku procenta "%". Nagłówki są dostępne nawet dla pakunków, która zostały odinstalowane, jednakże ustawienia międzynarodowe będą w postaci bezpośredniej, bez tłumaczenia, bądź przetłumaczone do bieżącego ustawienia w czasie odinstalowywania.

Metoda Bundle.loadClass() umożliwia wczytanie klasy z innego pakunku, uruchomienie aktywatora czy interakcję z aplikacjami zastanymi/"spadkowymi" (ang. legacy). Wczytanie klasy z innego pakunku może spowodować jego aktywację.

Metody Bundle.getResource(String) czy Bundle.getResources(String) umożliwiają wczytanie zasobów z pliku jar, pakunków częściowych, zaimportowanych pakietów lub ścieżki klas pakunku. Mogą spowodować zmianę stanu pakunku na RESOLVED.

Metody Bundle.getEntry(String) oraz Bundle.getEntryPaths(String) służą do wczytania plików w pliku jar pakunku i tylko w nim. Zaletą korzystania z metod getEntry* jest zaniechanie rozwiązywania zależności pakunku, co może okazać się kosztowne.

Istnieje jeszcze metoda Bundle.findEntries(String, String, boolean), która jest pomiędzy wspomnianymi metodami dostępowymi do zasobów środowiska.

Pytanie kontrolne: Jaki nagłówek w manifeście pozwala na zdefiniowanie polityki aktywacji pakunku?.