Użycie javax.transaction.TransactionManager w OSGi z Apache Felix
Z Jacek Laskowski - Wiki Projektanta Java EE
Podczas migracji Apache OpenEJB do OSGi - nazywam to OSGifikacją OpenEJB - natrafiłem na niezwykle wyrafinowany problem związany z pakietem javax.transaction dostępnym na platformie OSGi jako domyślny pakiet systemowy (udostępniany przez pakunek o identyfikatorze 0). Pakiet javax.transaction jest częścią samego JRE, jednakże tylko częściowo (w porównaniu z tym, co oferuje bogatszy świat Java EE). OpenEJB jest w kontekście migracji jedynie jedną z wielu typowych, korporacyjnych aplikacji javowych, więc to że jest kontenerem EJB nie zmienia faktu, że potrzebuje bogatszego javax.transaction, podobnie jak inne aplikacje transakcyjne.
Migracja OpenEJB na platformę OSGi nie obywa się bez niespodzianek i ostatnio wyszedł temat "kompletności" pakietu javax.transaction w JRE vs Java EE. Samo JRE dostarcza pakiet javax.transaction, ale bez interfejsu javax.transaction.TransactionManager, który jest istotną częścią tego drugiego. Właśnie to rozczłonkowanie, czy też rozbudowanie, pakietu spowodowało, że przyjemna migracja zaplanowana na przysłowiową chwilę skończyła się kilkugodzinną walką szczęśliwie zakończoną sukcesem, ale nieszczęśliwie, że dopiero o 3-ciej nad ranem (a obiecywałem sobie, nigdy więcej tych nocnych wojaży technologicznych).
Na platformie OSGi pakiet udostępniany przez więcej niż jeden pakunek nazywany jest pakietem podzielonym (ang. split package). Użycie (import) takiego pakietu wymaga specjalnego traktowania i wciąż zaliczam to do bardziej wyrafinowanej wiedzy OSGi - taki 2. stopień wtajemniczenia w OSGi-fu. Domyślnie import pakietu spowoduje związanie go tylko z jednym pakunkiem eksportującym, a proces łączenia pakunków - importu z eksportem - nazywamy wiązaniem (ang. wiring).
Jeśli instalujemy pakunek z pełniejszym pakietem, np. wspomnianym javax.transaction, to pierwszym pytaniem, jakie należałoby sobie postawić w środowisku OSGi jest z pewnością
Który z pakunków eksportujących pakiet importowany będzie użyty we wiązaniu?
Odpowiedzi szukamy w specyfikacji OSGi Service Platform Core Specification, Release 4, Version 4.1 (nota bene, istnieje już wersja 4.2, ale nie będę korzystał z platformy OSGi ją wspierającą, więc zostaję przy 4.1). Sugeruję zapoznać sie z całym rozdziałem 3. "Module Layer", ale dla niecierpliwych poniżej umieściłem kilka znaczących wycinków specyfikacji. Zacznijmy od rozdziału 3.4.1 "Resolving" (strona 34):
The Framework must resolve bundles. Resolving is the process where importers are wired to exporters. Resolving is a process of satisfying constraints. This process must take place before any code from a bundle can be loaded or executed.
A wire is an actual connection between an exporter and an importer, which are both bundles. A wire is associated with a number of constraints that are defined by its importer’s and exporter's manifest headers. A valid wire is a wire that has satisfied all its constraints.
Dalej w 3.5.4 "Import-Package Header" (strona 36) możemy przeczytać:
The Import-Package header defines the constraints on the imports of shared packages.
W 3.6.2 "Version Matching" (strona 41) znajdziemy dalszą część odpowiedzi:
Version constraints are a mechanism whereby an import definition can declare a precise version or a version range for matching an export definition.
W 3.2.5 "Version Ranges" (strona 28):
A version range describes a range of versions using a mathematical interval notation.
If a version range is specified as a single version, it must be interpreted as the range [version,?) . The default for a non-specified version range is 0, which maps to [0.0.0,?).
Jak widać, skakanie po sekcjach ma swój sens, jeśli wiadomo, gdzie czego szukać. Dla dokładnego poglądu na sprawę wiązania w OSGi polecam lekturę całego rozdziału 3.
Teraz wszystko powinno być już jasne - dwa pakunki udostępniające ten sam pakiet rywalizują o dostęp do pakunku importującego (bądź odwrotnie - importujący rywalizuje o dostęp do pakunku eksportującego). Reguła wiązania mówi o pierwszym porównaniu względem wersji importu, a później identyfikatora pakunku eksportującego - czym niższy tym eksporty są ważniejsze.
Sprawdźmy to na przykładzie wspomnianego pakietu javax.transaction, który z niewinnie wyglądającej migracji potrafi zrobić piekło :)
Kompletny projekt jest dostępny do pobrania jako javax-transaction-transactionmanager.zip.
Spis treści |
Zestawienie przestrzeni projektowej
Nauka empirycznie ma swoje wady i zalety. Nie patrząc na jej wady, licząc, że ich wpływ jest znikomy na wnioski, jakie na jej podstawie wyciągniemy, zestawiamy projekt do naszych doświadczeń.
Do wyboru mamy zintegrowane środowisko graficzne, np. Eclipse IDE z jego typem projektowym Plug-in Project i prowadzenie projektu z jego poziomu lub zejście na poziom linii poleceń z Apache Maven, a później "wzmocnienie" warsztatu o IDE, które potrafi współpracować z nim, np. NetBeans IDE czy IntelliJ IDEA. Ja wskazuję na rozwojową wersję NetBeans IDE 6.8 z jego wsparciem dla projektów mavenowych, aczkolwiek żadna z części naszego doświadczenia nie będzie zależna od IDE. Zaczniemy od linii poleceń, aby później resztę dokończyć z poziomu IDE. Wybór podejścia zostawiam Tobie i jeśli można go uatrakcyjnić, koniecznie się ze mną skontaktuj.
Najpierw jednak lektura. Warto zapoznać się z artykułami - Pakunki OSGi w projekcie wielomodułowym Apache Maven 2 z maven-bundle-plugin oraz Jak długo korzystać z referencji bezstanowego komponentu sesyjnego EJB (na przykładzie EJB 3.0 i GlassFish v3) - w których opisałem pracę z wtyczką maven-bundle-plugin oraz sam proces utworzenia projektu z Apache Maven.
Mając szkic kolejnych kroków jakie należy wykonać, rozpoczynamy od stworzenia projektu pakunku, który nazwiemy javax-transaction-transactionmanager, z mvn archetype:generate z linii poleceń. Inną możliwością jest skorzystanie z pomocy IDE do stworzenia pakunku we właściwy dla niego sposób. Użyłem Mavena za jego wsparcie do zarządzania zależnościami - wystarczy je zadeklarować - oraz jego zestaw wtyczek, które obsłużą zbudowanie pakunku. Paradoksalnie, w IDE, które z założenia ma udostępniać różnego rodzaju pomoce, musiałbym najpierw pobrać odpowiednie zależności i dodać je do projektu (chyba, że korzystamy z "nakładki" na Apache Maven w postaci wspomnianych NetBeans IDE czy IntelliJ IDEA. Eclipse IDE również potrafi przez wtyczkę M2Eclipse)
Przechodzimy do wybranego katalogu projektowego, np. c:\nauka i wykonujemy polecenie mvn archetype:generate.
jlaskowski@work /cygdrive/c/nauka $ mvn archetype:generate -B \ -DarchetypeArtifactId=maven-archetype-quickstart \ -DgroupId=pl.jaceklaskowski.osgi \ -DartifactId=javax-transaction-transactionmanager \ -Dversion=1.0
Kasuję utworzone klasy przykładowe z katalogu projektu.
jlaskowski@work /cygdrive/c/nauka/javax-transaction-transactionmanager $ find . -name App*.java | xargs rm
Zmieniamy plik konfiguracyjny projektu - pom.xml, tak aby packaging był bundle (automatyczne wykonanie wtyczki maven-bundle-plugin podczas budowania projektu), podnoszę poziom kompilatora do 1.5 (w zasadzie w tym przypadku mniej istotne i mogłoby zostać na poziomie domyślnym 1.3), konfiguruję wtyczkę maven-bundle-plugin (aby podczas uruchomienia dołączyła aktywatora do manifestu), aby w końcu dodać zależności projektowe, z których skorzystamy podczas tworzenia aktywatora pakunku.
Ostatecznie plik pom.xml wygląda następująco:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>pl.jaceklaskowski.osgi</groupId> <artifactId>javax-transaction-transactionmanager</artifactId> <packaging>bundle</packaging> <version>1.0</version> <name>javax-transaction-transactionmanager</name> <description>Użycie javax.transaction.TransactionManager w OSGi z Apache Felix</description> <url>http://www.jaceklaskowski.pl/wiki/Użycie_javax.transaction.TransactionManager_w_OSGi_z_Apache_Felix</url> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.5</source> <target>1.5</target> </configuration> </plugin> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.0.1</version> <extensions>true</extensions> <configuration> <instructions> <Bundle-Activator>pl.jaceklaskowski.osgi.impl.Aktywator</Bundle-Activator> </instructions> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.felix</groupId> <artifactId>org.osgi.core</artifactId> <version>1.2.0</version> </dependency> <dependency> <groupId>org.apache.geronimo.components</groupId> <artifactId>geronimo-transaction</artifactId> <version>2.1.3</version> </dependency> </dependencies> </project>
Utworzenie aktywatora - pl.jaceklaskowski.osgi.impl.Aktywator
Mamy skonfigurowany projekt, w którym możemy rozbudować pakunek o klasę aktywatora. Będzie to jedyna funkcjonalność pakunku. Aktywator to ta część, która jest uruchamiana podczas uruchomienia (faza start) i zatrzymania (faza stop) pakunku.
To jest ten moment, w którym możemy podeprzeć się wsparciem wybranego IDE. Można wykonywać pozostałe czynności z linii poleceń, ale skorzystanie z IDE znacznie uprości stworzenie klas pakunku, w tym i aktywatora.
Nasza klasa aktywatora znajduje się w pakiecie pl.jaceklaskowski.osgi.impl. Umownie, wszystkie klasy/interfejsy prywatne umieszcza się w pakiecie impl.
Kompletna klasa aktywatora pl.jaceklaskowski.osgi.impl.Aktywator powinna prezentować się następująco:
package pl.jaceklaskowski.osgi.impl;
import javax.transaction.InvalidTransactionException;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Aktywator implements BundleActivator {
public void start(BundleContext bc) throws Exception {
System.out.println("Aktywator uruchomiony - pora na dostep do TransactionManager");
// to jest właśnie różnica między javax.transaction w JRE 6.0, a specyfikacją Java EE
// W pierwszym nie ma interfejsu TransactionManager, a w drugim jest
// Naturalnie, sama implementacja interfejsu pozostawiona jest dostawcom serwerów aplikacyjnych
// Symulujemy użycie TransactionManager
TransactionManager txmgr = new TransactionManager() {
public void begin() throws NotSupportedException, SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void commit() throws SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public int getStatus() throws SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public Transaction getTransaction() throws SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void resume(Transaction tobj) throws IllegalStateException, InvalidTransactionException, SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void rollback() throws IllegalStateException, SecurityException, SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void setRollbackOnly() throws IllegalStateException, SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public void setTransactionTimeout(int seconds) throws SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
public Transaction suspend() throws SystemException {
throw new UnsupportedOperationException("Not supported yet.");
}
};
}
public void stop(BundleContext bc) throws Exception {
}
}
Uruchomienie pakunku na platformie OSGi - Apache Felix
W zasadzie to koniec naszych przygód programistycznych i pozostało nam zbudować i uruchomić pakunek na platformie OSGi. W naszym przypadku użyjemy Apache Felix 2.0.1 (mówi się, że Eclipse Equinox nie spowodowałby tego typu problemów).
Instalacja i uruchomienie Apache Felix
Pobieramy Felix z jego strony domowej i instalujemy, co sprowadza się do rozpakowania w wybranym katalogu.
jlaskowski@work /cygdrive/c/nauka $ wget -c http://www.apache.net.pl/felix/felix-framework-2.0.1.tar.gz jlaskowski@work /cygdrive/c/nauka $ tar -zxvf felix-framework-2.0.1.tar.gz
Poprawność instalacji sprawdzamy przez uruchomienie Felix.
jlaskowski@work /cygdrive/c/nauka $ cd felix-framework-2.0.1/ jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1 $ java -jar bin/felix.jar "C:\nauka\_felix-cache" Welcome to Felix ================ -> version 2.0.1 -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (2.0.1) [ 1] [Active ] [ 1] Apache Felix Bundle Repository (1.4.2) [ 2] [Active ] [ 1] Apache Felix Shell Service (1.4.1) [ 3] [Active ] [ 1] Apache Felix Shell TUI (1.4.1) -> shutdown
Wyświetlenie dostępnych pakunków jest wystarczającym testem na poprawność instalacji.
Instalacja pakunku - javax-transaction-transactionmanager
Uruchomioną platformę OSGi wzbogacamy o kolejny pakunek - nasz javax-transaction-transactionmanager.
Czy to z poziomu IDE, czy linii poleceń budujemy nasz pakunek. W przypadku NetBeans IDE wystarczy uruchomić menu Clean and Build (menu kontekstowe projektu, pod prawym klawiszem myszki), a na linii poleceń mvn clean package zrobi swoje. Zakładam, że gotowy pakunek znajduje się w katalogu target projektu, np. C:\nauka\javax-transaction-transactionmanager\target\javax-transaction-transactionmanager-1.0.jar.
UWAGA: Gdybyśmy zdecydowali się na budowanie pakunku z linii poleceń, zatrzymanie Felix jest możliwe poleceniem shutdown (bądź dla bardziej radykalnych Ctrl-C).
jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1
$ java -jar bin/felix.jar "C:\nauka\_felix-cache"
Welcome to Felix
================
-> install file:C:\nauka\javax-transaction-transactionmanager\target\javax-transaction-transactionmanager-1.0.jar
Bundle ID: 4
-> headers 4
javax-transaction-transactionmanager (4)
----------------------------------------
Bnd-LastModified = 1256288561015
Build-Jdk = 1.6.0_14
Built-By = jlaskowski
Bundle-Activator = pl.jaceklaskowski.osgi.impl.Aktywator
Bundle-Description = U?ycie javax.transaction.TransactionManager w OSGi z Apache Felix
Bundle-ManifestVersion = 2
Bundle-Name = javax-transaction-transactionmanager
Bundle-SymbolicName = pl.jaceklaskowski.osgi.javax-transaction-transactionmanager
Bundle-Version = 1.0
Created-By = Apache Maven Bundle Plugin
Import-Package = javax.transaction;version="1.1",org.osgi.framework;version="1.4"
Manifest-Version = 1.0
Tool = Bnd-0.0.357
-> start 4
Aktywator uruchomiony - pora na dostep do TransactionManager
org.osgi.framework.BundleException: Activator start error in bundle pl.jaceklaskowski.osgi.javax-transaction-transactionmanager [4].
at org.apache.felix.framework.Felix.activateBundle(Felix.java:1751)
at org.apache.felix.framework.Felix.startBundle(Felix.java:1622)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:915)
at org.apache.felix.shell.impl.StartCommandImpl.execute(StartCommandImpl.java:114)
at org.apache.felix.shell.impl.Activator$ShellServiceImpl.executeCommand(Activator.java:286)
at org.apache.felix.shell.tui.Activator$ShellTuiRunnable.run(Activator.java:184)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.NoClassDefFoundError: javax/transaction/TransactionManager
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
at org.apache.felix.framework.ModuleImpl$ModuleClassLoader.findClass(ModuleImpl.java:1787)
at org.apache.felix.framework.ModuleImpl.findClassOrResourceByDelegation(ModuleImpl.java:682)
at org.apache.felix.framework.ModuleImpl.access$100(ModuleImpl.java:60)
at org.apache.felix.framework.ModuleImpl$ModuleClassLoader.loadClass(ModuleImpl.java:1650)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
at pl.jaceklaskowski.osgi.impl.Aktywator.start(Aktywator.java:21)
at org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:639)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:1700)
... 6 more
Caused by: java.lang.ClassNotFoundException: javax.transaction.TransactionManager
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at org.apache.felix.framework.ExtensionManager$ExtensionManagerModule.getClassByDelegation(ExtensionManager.java:658)
at org.apache.felix.framework.searchpolicy.R4Wire.getClass(R4Wire.java:108)
at org.apache.felix.framework.ModuleImpl.searchImports(ModuleImpl.java:1364)
at org.apache.felix.framework.ModuleImpl.findClassOrResourceByDelegation(ModuleImpl.java:677)
at org.apache.felix.framework.ModuleImpl.access$100(ModuleImpl.java:60)
at org.apache.felix.framework.ModuleImpl$ModuleClassLoader.loadClass(ModuleImpl.java:1650)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 17 more
java.lang.NoClassDefFoundError: javax/transaction/TransactionManager
I teraz można postawić pytanie - czy samo "uzupełnienie" środowiska o pakunek dostarczający javax/transaction/TransactionManager wystarczy? Nawet, jeśli pytanie wydaje się być początkowo trywialne i można mieć nieodparte wrażenie, że odpowiedź sama ciśnie się na usta, to chwila z Google pokazuje złożoność problemu i ilu osobom spędzało to sen z powiek (mnie wliczając).
Właściwa odpowiedź na to pytanie w środowisku Apache Felix nie jest taka prosta, bo składa się z kilku kroków. Po pierwsze, wymaga zmiany w konfiguracji Felix, w pliku conf/config.properties, w którym należy zdefiniować właściwą konfigurację parametru org.osgi.framework.system.packages. Można też jeszcze inaczej.
W wątku Using javax.transaction within Apache Felix Richard S. Hall, który jest twórcą projektu Apache Felix, napisał:
There is no way to just exclude from packages. The easiest way is to just set the org.osgi.framework.system.packages property to be the value you want it to be. To make your life simpler, you can just get the default.properties file in felix.jar and copy the value out of it and put it into your config.properties.
Zgodnie z jego sugestią wystarczy wyłączyć systemowe eksportowanie javax.transaction i...chciałoby się napisać tyle, ale jeszcze potrzebny jest krok drugi - instalacja pakunku, który udostępnia pełny pakiet javax.transaction, np. pakunek org.apache.geronimo.specs.geronimo-jta_1.1_spec.
Zanim podejdziemy do zmian, przyjrzyjmy się dokładniej problemowi. Zatrzymujemy Felix poleceniem shutdown i włączamy tryb DEBUG, któremu odpowiada cyfra 4 we wspomnianym pliku konfiguracyjnym Felix - conf/config.properties (domyślnie felix.log.level=1).
felix.log.level=4
Ponownie uruchamiamy Felix. Podczas uruchamiania środowiska pojawią się komunikaty wiązania pakunków - wiązaniach ich importów z eksportami.
jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1
$ java -jar bin/felix.jar "C:\nauka\_felix-cache"
Welcome to Felix
================
...
-> DEBUG: WIRE: 4.0 -> javax.transaction -> 0
DEBUG: WIRE: 4.0 -> org.osgi.framework -> 0
...
Caused by: java.lang.ClassNotFoundException: *** Package 'javax.transaction' is imported by bundle 4 from bundle 0,
but the exported package from bundle 0 does not contain the requested class 'javax.transaction.TransactionManager'.
Please verify that the class name is correct in the importing bundle 4 and/or that the exported package is correctly bundled in 0. ***
at org.apache.felix.framework.ModuleImpl$ModuleClassLoader.loadClass(ModuleImpl.java:1664)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
... 15 more
Dla zwrócenia uwagi pokazane są jedynie komunikaty związane z naszym pakunkiem i jak następuje wiązanie. Widzimy, że import javax.transaction pakunku o identyfikatorze 4.0 jest związany z pakunkiem o identyfikatorze 0 - DEBUG: WIRE: 4.0 -> javax.transaction -> 0.
Poleceniem ps sprawdźmy identyfikatory pakunków.
-> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (2.0.1) [ 1] [Active ] [ 1] Apache Felix Bundle Repository (1.4.2) [ 2] [Active ] [ 1] Apache Felix Shell Service (1.4.1) [ 3] [Active ] [ 1] Apache Felix Shell TUI (1.4.1) [ 4] [Resolved ] [ 1] javax-transaction-transactionmanager (1.0)
Jak widać pakunek 4 to nasz pakunek javax-transaction-transactionmanager, który związywany jest z pakunkiem systemowym (każda platforma OSGi musi posiadać pakunek o identyfikatorze 0, który jest pakunkiem systemowym). Wiemy, że pakunek systemowy udostępnia jedynie część pakietu javax.transaction, co jest zawarte w komunikacie Caused by: java.lang.ClassNotFoundException: *** Package 'javax.transaction'... wyżej.
Teraz wiadomo, co i kiedy jest wybierane jako strona podczas wiązania. Wystraczy wydać polecenie inspect package capability 0, aby dowiedzieć się o wszystkich pakietach eksportowanych przez pakunek systemowy, a inspect package requirement 4 o tych, które są importowane przez nasz pakunek.
-> inspect package requirement 4 pl.jaceklaskowski.osgi.javax-transaction-transactionmanager [4] imports packages: --------------------------------------------------------------------------------- org.osgi.framework; version=1.5.0 -> org.apache.felix.framework [0] javax.transaction; version=1.6.0 -> org.apache.felix.framework [0]
Możemy, więc zainstalować nowy pakunek z javax.transaction z wyższą wersją niż 1.6.0 i odpowiednio zadać import z wersją explicite, albo wyłączyć eksport pakietu javax.transaction przez pakunek systemowy, przez odpowiednią zmianę w pliku default.properties w bin/felix.jar i...dalej już wiemy, jak obsłużyć temat - typowa sytuacja w środowisku OSGi. Nie? Czytaj dalej.
Instalacja pakunku geronimo-jta_1.1_spec (1.1.1) w Apache Felix
Pełny pakiet javax.transaction jest zapewne eksportowany przez wiele pakunków OSGi. My skorzystamy z geronimo-jta_1.1_spec (1.1.1).
Wyłączenie javax.transaction jako pakietu systemowego
Najpierw konieczne jest wyłączenie javax.transaction jako pakietu systemowego.
TODO: Napisać procedurę wyłączenia w Groovy/Clojure/Scala/[dowolny język dynamiczny] zamiast/dodatkowo do poniższej z poleceniami uniksowymi.
jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1 $ cd bin jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1/bin $ jar xf felix.jar default.properties jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1/bin $ sed -i 's,javax.transaction,#javax.transaction,' default.properties jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1/bin $ jar uf felix.jar default.properties jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1/bin $ rm default.properties jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1/bin $ cd ..
Eksport pakietu javax.transaction
Uruchamiamy Felix i instalujemy pakunek geronimo-jta_1.1_spec (1.1.1), który eksportuje (udostępnia) pakiet javax.transaction, a później nasz pakunek, który go importuje.
Warto rozpocząć pracę z czystym katalogiem podręcznym.
jlaskowski@work /cygdrive/c/nauka/felix-framework-2.0.1 $ java -jar bin/felix.jar "C:\nauka\_felix-cache" Welcome to Felix ================ DEBUG: WIRE: 1.0 -> org.osgi.framework -> 0 DEBUG: WIRE: 1.0 -> org.osgi.service.log -> 2.0 DEBUG: WIRE: 1.0 -> org.osgi.service.url -> 0 DEBUG: WIRE: 2.0 -> org.osgi.framework -> 0 DEBUG: WIRE: 2.0 -> org.osgi.service.packageadmin -> 0 DEBUG: WIRE: 2.0 -> org.osgi.service.startlevel -> 0 DEBUG: DYNAMIC WIRE: 1.0 -> org.apache.felix.shell -> 2.0 DEBUG: WIRE: 3.0 -> org.apache.felix.shell -> 2.0 DEBUG: WIRE: 3.0 -> org.osgi.framework -> 0 -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (2.0.1) [ 1] [Active ] [ 1] Apache Felix Bundle Repository (1.4.2) [ 2] [Active ] [ 1] Apache Felix Shell Service (1.4.1) [ 3] [Active ] [ 1] Apache Felix Shell TUI (1.4.1) -> install http://repo2.maven.org/maven2/org/apache/geronimo/specs/geronimo-jta_1.1_spec/1.1.1/geronimo-jta_1.1_spec-1.1.1.jar Bundle ID: 4 -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (2.0.1) [ 1] [Active ] [ 1] Apache Felix Bundle Repository (1.4.2) [ 2] [Active ] [ 1] Apache Felix Shell Service (1.4.1) [ 3] [Active ] [ 1] Apache Felix Shell TUI (1.4.1) [ 4] [Installed ] [ 1] geronimo-jta_1.1_spec (1.1.1) -> install file:C:\nauka\javax-transaction-transactionmanager\target\javax-transaction-transactionmanager-1.0.jar Bundle ID: 5 -> start 5 DEBUG: WIRE: 5.0 -> javax.transaction -> 4.0 DEBUG: WIRE: 5.0 -> org.osgi.framework -> 0 Aktywator uruchomiony - pora na dostep do TransactionManager -> ps START LEVEL 1 ID State Level Name [ 0] [Active ] [ 0] System Bundle (2.0.1) [ 1] [Active ] [ 1] Apache Felix Bundle Repository (1.4.2) [ 2] [Active ] [ 1] Apache Felix Shell Service (1.4.1) [ 3] [Active ] [ 1] Apache Felix Shell TUI (1.4.1) [ 4] [Resolved ] [ 1] geronimo-jta_1.1_spec (1.1.1) [ 5] [Active ] [ 1] javax-transaction-transactionmanager (1.0)
Oczywiście kolejność instalacji nie ma znaczenia, jednakże w sytuacji, gdzie najpierw instalujemy nasz pakunek i próbujemy go uruchomić, pojawi się wyjątek niedostępności pakietów javax.transaction.
Zauważmy, że instalacja pakunku była możliwa poprzez protokół HTTP, którego obsługa w poleceniu install jest domyślnie dostępna w Felix.
Na koniec warto jeszcze sprawdzić, jak nastąpiło wiązanie między pakunkami poleceniem inspect package requirement.
-> inspect package requirement 5 pl.jaceklaskowski.osgi.javax-transaction-transactionmanager [5] imports packages: --------------------------------------------------------------------------------- org.osgi.framework; version=1.5.0 -> org.apache.felix.framework [0] javax.transaction; version=1.1.0 -> org.apache.geronimo.specs.geronimo-jta_1.1_spec [4]
Teraz problemy z użyciem javax.transaction.TransactionManager wydają się być trywialne. Miłej OSGifikacji własnych aplikacji korporacyjnych.
