Rozpocznę praktyczne rozpoznanie pakunków częściowych od stworzenia dwóch pakunków - pakunku macierzystego i pakunku częściowego - z pomocą spring-osgi-bundle-archetype.
UWAGA: Użyłem zadania archetype:generate z parametrem -Darchetype.interactive=false, gdyż wcześniejużywany archetype:create został oznaczony jako nieaktualny ([WARNING] This goal is deprecated. Please use mvn archetype:generate instead)
Najpierw pakunek macierzysty springdm-host-bundle:
mvn archetype:generate \, po którym tworzę pakunek częściowy springdm-fragment-bundle:
-DarchetypeGroupId=org.springframework.osgi \
-DarchetypeArtifactId=spring-osgi-bundle-archetype \
-DarchetypeVersion=1.2.0-m2-SNAPSHOT \
-DgroupId=pl.jaceklaskowski.springdm.fragment \
-DartifactId=springdm-host-bundle \
-Dversion=1.0 \
-Darchetype.interactive=false
mvn archetype:generate \Możnaby utworzyć je w ramach większego projektu mavenowego (packaging=pom), ale pozostawiam to dla zaangażowanych.
-DarchetypeGroupId=org.springframework.osgi \
-DarchetypeArtifactId=spring-osgi-bundle-archetype \
-DarchetypeVersion=1.2.0-m2-SNAPSHOT \
-DgroupId=pl.jaceklaskowski.springdm.fragment \
-DartifactId=springdm-fragment-bundle \
-Dversion=1.0 \
-Darchetype.interactive=false
Na początku zadeklaruję pakunek springdm-fragment-bundle jako pakunek częściowy dla springdm-host-bundle za pomocą nagłówka Fragment-Host. Jako, że projekt pakunku częściowego springdm-fragment-bundle zarządzany jest przez Apache Maven 2 nagłówek dodaję do konfiguracji wtyczki maven-bundle-plugin w pom.xml.
<Fragment-Host>pl.jaceklaskowski.springdm.fragment.springdm-host-bundle</Fragment-Host>Nazwę pakunku macierzystego można poznać przez utworzenie docelowego manifestu przez wykonanie polecenia mvn package i odczytanie nagłówka Bundle-SymbolicName w utworzonym META-INF/MANIFEST.MF.
Tworzę pakunki poleceniem mvn package i uruchamiam na Equinoksie (najświeższa wersja do pobrania ze strony equinox osgi downloads, np. Equinox Stable Build: 3.5M2, chociaż adres na stronie nie działa! Można skorzystać z jeszcze innego adresu equinox osgi downloads, gdzie można pobrać Equinox Stable Build: 3.5M1). Początkowo korzystałem z Equinoksa dostarczanego w ramach Eclipse Ganymede - plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar, ale przy finalnym uruchomieniu przeniosłem się na nowszą wersję - org.eclipse.osgi_3.5.0.v20080804-1730.jar, wyłącznie ze względów bycia na bieżąco.
Dla dociekliwych zaleca się lekturę niewielkiego podręcznika Equinoksa - Equinox QuickStart Guide.
jlaskowski@work /cygdrive/c/apps/eclipseDodam do pakunku macierzystego funkcjonalność, która przy każdorazowym zainstalowaniu nowego pakunku będącym rozszerzeniem (pakunkiem częściowym) dla niego, wypisze dostępne pliki wchodzącego w skład przestrzeni pakunku. Dzięki metodzie org.osgi.framework.Bundle.findEntries(String,String,boolean) pakunek ma możliwość przejrzenia wszystkich zasobów zawartych w nim (bezpośrednio w jego pliku jar) oraz wszystkich rozszerzeniach (pakunkach częściowych). Nie należy mylić działania tej metody z metodą Bundle.getResource(String) czy Bundle.getResources(String), które działają na całej przestrzeni klas dostęnych dla pakunku (co może być rozbudowane w porównaniu z przestrzenią pakunku o deklaracje w nagłówku Import-Package).
$ java -jar plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar -console
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
osgi> install file:/C:/projs/sandbox/springdm-fragment-bundle/target/springdm-fragment-bundle-1.0.jar
Bundle id is 1
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
1 INSTALLED pl.jaceklaskowski.springdm.fragment.springdm-fragment-bundle_1.0.0
osgi> start 1
org.osgi.framework.BundleException: A fragment bundle cannot be started:
file:/C:/projs/sandbox/springdm-fragment-bundle/target/springdm-fragment-bundle-1.0.jar [1]
at org.eclipse.osgi.framework.internal.core.BundleFragment.startWorker(BundleFragment.java:224)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:265)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:257)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:257)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:302)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:287)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:223)
at java.lang.Thread.run(Thread.java:595)
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
1 INSTALLED pl.jaceklaskowski.springdm.fragment.springdm-fragment-bundle_1.0.0
osgi> install file:/C:/projs/sandbox/springdm-host-bundle/target/springdm-host-bundle-1.0.jar
Bundle id is 2
osgi> start 2
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
1 RESOLVED pl.jaceklaskowski.springdm.fragment.springdm-fragment-bundle_1.0.0
Master=2
2 ACTIVE pl.jaceklaskowski.springdm.fragment.springdm-host-bundle_1.0.0
Fragments=1
osgi> bundle 1
file:/C:/projs/sandbox/springdm-fragment-bundle/target/springdm-fragment-bundle-1.0.jar [1]
Id=1, Status=RESOLVED Data Root=C:\apps\eclipse\plugins\configuration\org.eclipse.osgi\bundles\1\data
No registered services.
No services in use.
Exported packages
pl.jaceklaskowski.springdm.fragment; version="0.0.0"[exported]
No imported packages
Host bundles
file:/C:/projs/sandbox/springdm-host-bundle/target/springdm-host-bundle-1.0.jar [2]
No named class spaces
No required bundles
osgi> bundle 2
file:/C:/projs/sandbox/springdm-host-bundle/target/springdm-host-bundle-1.0.jar [2]
Id=2, Status=ACTIVE Data Root=C:\apps\eclipse\plugins\configuration\org.eclipse.osgi\bundles\2\data
No registered services.
No services in use.
Exported packages
pl.jaceklaskowski.springdm.fragment; version="0.0.0"[exported]
No imported packages
Fragment bundles
file:/C:/projs/sandbox/springdm-fragment-bundle/target/springdm-fragment-bundle-1.0.jar [1]
Named class space
pl.jaceklaskowski.springdm.fragment.springdm-host-bundle; bundle-version="1.0.0"[provided]
No required bundles
osgi> close
Definiuję aktywator w pakunku macierzystym, który zarejestruje słuchacza reagującego na zdarzenia rozwiązania (stan RESOLVED) pakunków (podpieram się artykułem Bundle.findEntries() i spring-osgi-bundle-archetype w akcji - monitorowanie pakunków OSGi z określoną strukturą katalogową).
package pl.jaceklaskowski.springdm.fragment.internal;Pierwsze uruchomienie przykładu zakończyło się niepowodzeniem.
import java.util.Dictionary;
import java.util.Enumeration;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
public class Aktywator implements BundleActivator {
private final class Sluchacz implements BundleListener {
private String nazwaPakunkuMacierzystego;
public Sluchacz(String nazwaPakunkuMacierzystego) {
this.nazwaPakunkuMacierzystego = nazwaPakunkuMacierzystego;
}
public void bundleChanged(BundleEvent event) {
Bundle bundle = event.getBundle();
String nazwaPakunku = bundle.getSymbolicName();
// Sprawdź, czy jest pakunkiem częściowym
Dictionary<?, ?> headers = bundle.getHeaders();
String pakunekMacierzysty = (String) headers.get("Fragment-Host");
if (!nazwaPakunkuMacierzystego.equalsIgnoreCase(pakunekMacierzysty)) {
return;
}
switch (event.getType()) {
case BundleEvent.RESOLVED:
System.out.println("Pakunek czesciowy " + nazwaPakunku + " w stanie RESOLVED");
wyswietlLiczbeDostepnychPlikow(bundle);
break;
case BundleEvent.STOPPED:
System.out.println("Pakunek czesciowy " + nazwaPakunku + " w stanie STOPPED");
wyswietlLiczbeDostepnychPlikow(bundle);
break;
}
}
}
private BundleListener sluchacz;
public void start(BundleContext context) throws Exception {
String nazwaPakunku = context.getBundle().getSymbolicName();
System.out.println("Pakunek macierzysty " + nazwaPakunku + " w stanie STARTING");
sluchacz = new Sluchacz(nazwaPakunku);
System.out.println("...instalacja " + sluchacz);
context.addBundleListener(sluchacz);
wyswietlLiczbeDostepnychPlikow(context.getBundle());
}
public void stop(BundleContext context) throws Exception {
String nazwaPakunku = context.getBundle().getSymbolicName();
System.out.println("Pakunek macierzysty " + nazwaPakunku + " w stanie STOPPING");
System.out.println("...usuniecie " + sluchacz);
context.removeBundleListener(sluchacz);
wyswietlLiczbeDostepnychPlikow(context.getBundle());
}
private void wyswietlLiczbeDostepnychPlikow(Bundle bundle) {
int liczbaPlikow = 0;
Enumeration<?> entries = bundle.findEntries("/", "*", true);
for (; entries.hasMoreElements(); entries.nextElement()) {
liczbaPlikow++;
}
System.out.println("+++ Liczba plikow: " + liczbaPlikow);
}
}
jlaskowski@work /cygdrive/c/apps/eclipseO dziwo nie doświadczałem tych problemów podczas pracy z Apache Felix (!) Najwyraźniej Equinox jest bardziej restrykcyjny i sprawdzając poprawność nagłówków, dla każdego Import-Package weryfikuje widoczność pakietów w przestrzeni klas. Jako, że domyślnie pakiet pl.jaceklaskowski.springdm.fragment.internal jest domyślnie wyłączany z Export-Package przez Spring-DM (a właściwie niewprost przez bnd wywoływane przez wtyczkę maven-bundle-plugin, która jest tak konfigurowana przez archetyp spring-osgi-bundle-archetype w pom.xml) pojawia się komunikat błędu o niespełnieniu wymagania nałożonego przez Import-Package. Analizując źródła projektu Spring-DM (dokładniej pomy w spring-osgi-extender oraz spring-osgi) kończę z następującą konfiguracją dla maven-bundle-plugin:
$ java -jar plugins/org.eclipse.osgi_3.4.0.v20080605-1900.jar -console
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
osgi> install file:/C:/projs/sandbox/springdm-host-bundle/target/springdm-host-bundle-1.0.jar
Bundle id is 1
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.4.0.v20080605-1900
1 INSTALLED pl.jaceklaskowski.springdm.fragment.springdm-host-bundle_1.0.0
osgi> start 1
org.osgi.framework.BundleException: The bundle could not be resolved. Reason:
Missing Constraint: Import-Package: pl.jaceklaskowski.springdm.fragment.internal; version="0.0.0"
at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:305)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:265)
at org.eclipse.osgi.framework.internal.core.AbstractBundle.start(AbstractBundle.java:257)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider._start(FrameworkCommandProvider.java:257)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
at org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter.execute(FrameworkCommandInterpreter.java:150)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.docommand(FrameworkConsole.java:302)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.console(FrameworkConsole.java:287)
at org.eclipse.osgi.framework.internal.core.FrameworkConsole.run(FrameworkConsole.java:223)
at java.lang.Thread.run(Thread.java:595)
osgi> close
<plugin>Kluczem do sukcesu jest nagłówek Private-Package.
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>1.4.0</version>
<configuration>
<manifestLocation>META-INF</manifestLocation>
<instructions>
<Export-Package>!pl.jaceklaskowski.springdm.fragment.*internal*</Export-Package>
<Private-Package>pl.jaceklaskowski.springdm.fragment.*internal*</Private-Package>
<Include-Resource>src/main/resources</Include-Resource>
<Bundle-Activator>pl.jaceklaskowski.springdm.fragment.internal.Aktywator</Bundle-Activator>
<Bundle-Version>2</Bundle-Version>
</instructions>
</configuration>
</plugin>
Warto pomiędzy uruchomieniami Equinoksa usuwać jego katalog konfiguracyjny - configuration, aby poprzednia konfiguracja nie kolidowała na bieżące testy. Katalog configuration tworzony jest w katalogu, w którym znajduje się uruchomieniowy jar Equinoksa.
jlaskowski@work /cygdrive/c/apps/equinoxPodczas analizy poniższego działania pakunków macierzystego i częściowego na Equinoksie proszę zwrócić uwagę na komunikat +++ Liczba plikow, który informuje o liczbie plików dostępnych w pakunku macierzystym (przypominam, że pakunek częściowy nie jest pełnoprawnym pakunkiem OSGi, tzn. obostrzenia OSGi zugożają go sprowadzając do roli pakunku rozszerzającego).
$ rm -rf configuration
jlaskowski@work /cygdrive/c/apps/equinoxCiekawe przedstawienie tematu dostępne również w bezpłatnej książce OSGi in Practice w rozdziale The Extender Model.
$ java -jar org.eclipse.osgi_3.5.0.v20080804-1730.jar -console
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.5.0.v20080804-1730
osgi> install file:/C:/projs/sandbox/springdm-host-bundle/target/springdm-host-bundle-1.0.jar
Bundle id is 1
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.5.0.v20080804-1730
1 INSTALLED pl.jaceklaskowski.springdm.fragment.springdm-host-bundle_2.0.0
osgi> start 1
Pakunek macierzysty pl.jaceklaskowski.springdm.fragment.springdm-host-bundle w stanie STARTING
...instalacja pl.jaceklaskowski.springdm.fragment.internal.Aktywator$Sluchacz@ca470
+++ Liczba plikow: 18
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.5.0.v20080804-1730
1 ACTIVE pl.jaceklaskowski.springdm.fragment.springdm-host-bundle_2.0.0
osgi> install file:/C:/projs/sandbox/springdm-fragment-bundle/target/springdm-fragment-bundle-1.0.jar
Bundle id is 2
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.5.0.v20080804-1730
1 ACTIVE pl.jaceklaskowski.springdm.fragment.springdm-host-bundle_2.0.0
2 INSTALLED pl.jaceklaskowski.springdm.fragment.springdm-fragment-bundle_1.0.0
osgi> refresh 1
osgi> Pakunek macierzysty pl.jaceklaskowski.springdm.fragment.springdm-host-bundle w stanie STOPPING
...usuniecie pl.jaceklaskowski.springdm.fragment.internal.Aktywator$Sluchacz@ca470
+++ Liczba plikow: 18
Pakunek macierzysty pl.jaceklaskowski.springdm.fragment.springdm-host-bundle w stanie STARTING
...instalacja pl.jaceklaskowski.springdm.fragment.internal.Aktywator$Sluchacz@42552c
+++ Liczba plikow: 29
osgi> ss
Framework is launched.
id State Bundle
0 ACTIVE org.eclipse.osgi_3.5.0.v20080804-1730
1 ACTIVE pl.jaceklaskowski.springdm.fragment.springdm-host-bundle_2.0.0
Fragments=2
2 RESOLVED pl.jaceklaskowski.springdm.fragment.springdm-fragment-bundle_1.0.0
Master=1
osgi> uninstall 2
osgi> refresh 1
osgi> Pakunek macierzysty pl.jaceklaskowski.springdm.fragment.springdm-host-bundle w stanie STOPPING
...usuniecie pl.jaceklaskowski.springdm.fragment.internal.Aktywator$Sluchacz@42552c
Pakunek macierzysty pl.jaceklaskowski.springdm.fragment.springdm-host-bundle w stanie STARTING
...instalacja pl.jaceklaskowski.springdm.fragment.internal.Aktywator$Sluchacz@1113622
+++ Liczba plikow: 18
osgi> close
Pakunek macierzysty pl.jaceklaskowski.springdm.fragment.springdm-host-bundle w stanie STOPPING
...usuniecie pl.jaceklaskowski.springdm.fragment.internal.Aktywator$Sluchacz@1113622
+++ Liczba plikow: 18
Pojawił się nowy harmonogram nowej wersji NetBeans 6.5 - NetBeans NB65Milestones. Przyjdzie jeszcze trochę poczekać na finalną wersję NetBeans 6.5 - 12 listopada 2008.
Brak komentarzy:
Prześlij komentarz