22 września 2008

Pakunki częściowe OSGi w akcji z Equinox

0 komentarzy
Już wiem, że pakunki częściowe (ang. fragment bundles) nie są wspierane przez Apache Felix (więcej o nich i braku wsparcia przez Feliksa w OSGi - 3.14 Pakunki częściowe), więc nie pozostaje mi nic innego jak korzystać z Eclipse Equinox do dalszych testów. Tak długo, jak wymagane będzie wsparcie pakunków częściowych Platformą OSGi nie będzie Apache Felix. To jest właśnie ów techniczny powód, dla którego, w kontekście Spring-DM, często pojawiał się będzie Equinox. Zainteresowanych zmianami w tej materii uprasza się o śledzenie rozwoju zgłoszenia FELIX-29 Implement bundle fragments.

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 \
-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
, po którym tworzę pakunek częściowy springdm-fragment-bundle:
 mvn archetype:generate \
-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
Możnaby utworzyć je w ramach większego projektu mavenowego (packaging=pom), ale pozostawiam to dla zaangażowanych.

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/eclipse
$ 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
Dodam 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).

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;

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);
}

}
Pierwsze uruchomienie przykładu zakończyło się niepowodzeniem.
 jlaskowski@work /cygdrive/c/apps/eclipse
$ 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
O 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:
 <plugin>
<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>
Kluczem do sukcesu jest nagłówek Private-Package.

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/equinox
$ rm -rf configuration
Podczas 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).
 jlaskowski@work /cygdrive/c/apps/equinox
$ 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
Ciekawe przedstawienie tematu dostępne również w bezpłatnej książce OSGi in Practice w rozdziale The Extender Model.

Pojawił się nowy harmonogram nowej wersji NetBeans 6.5 - NetBeans NB65Milestones. Przyjdzie jeszcze trochę poczekać na finalną wersję NetBeans 6.5 - 12 listopada 2008.

21 września 2008

Spring Dynamic Modules a aplikacje webowe

5 komentarzy
Już pisałem o tym 4 maja 2008 w Aplikacja webowa jako pakunek OSGi ze Spring Dynamic Modules, ale po sobie widzę, że warto wspomnieć o tym jeszcze raz.

Rozdział 8. Web Support nie pozostawia złudzeń, czego możemy dodatkowo oczekiwać od Spring Dynamic Modules (Spring-DM) poza podstawową cechą jako jest wsparcie dla tworzenia pakunków OSGi korzystając ze Spring Framework. Mowa w nim o wsparciu dla aplikacji webowych (będącymi dystrybuowane jako war - plik jar z odpowiednią strukturą katalogową, np. konieczność istnienia WEB-INF/web.xml) będącymi de facto pakunkami OSGi. Jeśli ktokolwiek zastanawiał się nad sensownością zastosowania OSGi w swoich aplikacjach, to przynajmniej obszar aplikacji webowych powinien być już rozwikłany z pomocą tego rozdziału. Spring-DM łączy cechy OSGi z Korporacyjną Javą udostępniając niezbędne elementy jako pakunki OSGi - kontener servletów jak i samą aplikację webową - które wiąże mechanizmami OSGi. Spring-DM to po prostu warstwa wspierająca uruchamianie aplikacji springowych oraz webowych na Platformie OSGi.

Przyjrzyjmy się dokładniej, co takiego oferuje Spring-DM, czego nie znajdziemy w innych środowiskach. Do poprawnego uruchomienia aplikacji webowej potrzebny jest kontener servletów (być może z jsp, ale kto by tego obecnie używał?!). Podstawowym bytem Platformy OSGi jest pakunek. Jeśli cokolwiek miałoby być uruchomione na Platformie OSGi musi być pakunkiem. Pakunek OSGi to zwykły plik jar z odpowiednimi nagłówkami w META-INF/MANIFEST.MF i stąd wypływa niezwykłość OSGi - niby nic szczególnego, a za darmo mamy możliwości niebagatelnej wartości, m.in. wersjonowanie, dedykowane ładowarki klas, uprawnienia, co z kolei składa się na funkcjonalność uktualniania aplikacji bez konieczności jej zatrzymywania. Połączenie elementów Java EE z OSGi polega na udostępnieniu tych pierwszych jako pakunków OSGi i...tyle. Już możemy okrzyknąć nasze rozwiązanie jako zgodne z pryncypiami OSGi. Do tego wcale nie potrzeba Spring-DM. Gdzie dostrzeżemy zaletę jego wykorzystania, to w sposobie integracji trzech (!) technologii OSGi, Java EE oraz Spring Framework - podczas uruchomienia pakunku OSGi z rozszerzeniem pliku .war, lub zawierającego specyficzne dla Spring-DM nagłówki w manifeście, nastąpi ich automatyczne "związanie" z pakunkiem będącym kontenerem servletów (obecnie Tomcat i Jetty). Właśnie owe rozszerzenie Platformy OSGi o funkcjonalność rozpoznawania pakunków będących faktycznie aplikacjami korporacyjnymi - aplikacjami webowymi - jest wartością Spring-DM. Wprowadzając Spring-DM do naszej aplikacji wprowadzamy jednocześnie cechy OSGi, Spring Framework oraz Java EE. Takie 3-w-1, albo po prostu OSprin-JEE-i.

Porównując wsparcie Spring-DM dla uruchamiania aplikacji springowych a aplikacjami webowymi (potencjalnie korzystającymi z elementów springowych) można zauważyć, że w przypadku aplikacji webowych są one jedynie rozpoznawane i przekazywane do kontenera servletów (który uruchomiony jest na Platformie OSGi jako pełnoprawny pakunek OSGi). Nic poza rozpoznaniem aplikacji webowych nie pozostaje w gestii Spring-DM. Tymczasem rozpoznanie aplikacji springowych skutkuje wzbudzeniem Spring-DM Extender, który wykonuje całą pracę uruchomienia pełnej maszyneri springowej.

Jest kilka istotnych kwestii do zapamiętania, aby aplikację webową uruchomić w ramach Spring-DM, które są implikowane przez prawa rządzące OSGi - kwestia widoczności klas. Domyślnie klasy należące do pakietów javax.servlet.*, WEB-INF/lib/*.jar oraz WEB-INF/classes są widoczne dla aplikacji webowej w "zwykłym" kontenerze servletów. Dodatkowo kontener ma możliwość zdefiniowania wspólnej przestrzeni klas/bibliotek dołączanych do aplikacji webowych, o którą ją rozszerza. W przypadku środowiska OSGi obowiązują bardziej restrykcyjne reguły wymagające, aby pakunek deklarował swoją przestrzeń klas przez nagłówki Import-Package oraz Bundle-Classpath w manifeście. Za ich pomocą należy skonstruować wymaganą przestrzeń klas - zawartość ładowarki klas związanej z pakunkiem. Bez nich zasady obowiązujące w specyfikacji Java Servlet są niepełne w środowisku Spring-DM. Zgodnie z adnotacją w 8. Web Support możnaby oczekiwać, aby owe deklaracje były automatycznie dodawane do pakunku przez Spring-DM, ale póki co, taka funkcjonalność nie jest dostępna. Cechą, która jest niezwykle interesująca, a wręcz wskazana, przy konstruowaniu aplikacji webowej w ramach OSGi (za pomocą Spring-DM) jest wyniesienie bibliotek aplikacji poza jej strukturę katalogową, co przy bardzo radykalnym podejściu skończy się pustymi katalogami WEB-INF/lib oraz WEB-INF/classes, a ich zawartość zostanie uruchomiona w ramach OSGi jako osobne pakunki związane z aplikacją przez nagłówki Import-Package (opcjonalnie Require-Bundle) w manifeście. Uaktualnienie aplikacji o nową funkcjonalność, bądź wdrożenie poprawek, to wyłącznie uaktualnienie pakunków OSGi. Niezwykle potężne oręże upraszczające tworzenie aplikacji webowych ze Spring-DM.

18 września 2008

OSGi - 3.14 Pakunki częściowe

1 komentarzy
Ostatnimi czasy wielokrotnie spotykałem się z terminami OSGi fragment lub OSGi fragment bundle i zawsze kończyło się na...odłożeniu rozpoznania tematu na później (nawet podczas relacji lektury rozdziału 3. Warstwa modułowa w Kolejny łyk OSGi - rozdział 3. Warstwa modułowa nie napisałem o nich ani słowa!). Rozdział 3.14 Fragment Bundles to jedynie 4 strony (plus niewielki diagram), więc samej lektury na niecałe 5-10 minut i można ją powtórzyć (nawet kilkakrotnie).

Przyjrzyjmy się dokładniej, cóż specyfikacja ma do powiedzenia nt. pakunków częściowych w rodziale 3.14 Fragment Bundles.

Pakunki częściowe (ang. fragment bundles) są całkowicie zależne od pakunku wiodącego (przewodniego, ang. host bundle). Podczas fazy rozwiązywania (zależności) pakunku wiodącego Platforma OSGi dołącza do niego wszystkie dostępne pakunki częściowe. Innymi słowy pakunki częściowe stają się logicznie integralną częścią pakunku wiodącego (aczkolwiek fizycznie są wciąż osobnymi bytami). Stąd też pakunki częściowe współdzielą ładowarkę klas (ang. classloader) pakunku wiodącego.

Specyfikacja OSGi określa rolę pakunków częściowych jako dostawców tłumaczeń, co umożliwia dostarczanie ich niezależnie od pakunku wiodącego (w późniejszym terminie). Można sobie wyobrazić działanie pakunku wiodącego jako aplikacji webowej, do której tłumaczenia są dostarczane jako osobne pakunki częściowe, które ostatecznie stają się częścią aplikacji webowej. Wszystko obsługuje Platforma OSGi.

Aktualizacja pakunku częściowego jest widoczna dla pakunku wiodącego dopiero po ponownym uruchomieniu Platformy OSGi lub odświeżeniu pakunku wiodącego. Do tego momentu uaktualniony pakunek częściowy jest dołączony do pakunku wiodącego równolegle do poprzedniej wersji, ale pozostaje nieaktywny.

Usługa Package Admin zwraca ostatnią wersję dołączonego pakunku częściowego.

Pakunek częściowy może deklarować import części chronionej (prywatnej) pakunku wiodącego.

Podczas rozwiązywania środowiska dla pakunku częściowego jakikolwiek konflikt w deklaracjach z pakunkiem wiodącym działają na niekorzyść tego pierwszego, co w efekcie prowadzi do niezwiązania go z pakunkiem wiodącym. Poprawne rozwiązanie pakunku częściowego to pomyślnie związanie go z pakunkiem wiodącym i przejście do stanu RESOLVED.

Przeszukiwanie zasobów pakunku częściowego odbywa się po przeszukaniu zawartości pakunku wiodącego.

Pakunek częściowy nie może być zadeklarowany jako obowiązkowy przez nagłówek Require-Bundle.

Deklaracja środowiska pakunku częściowego zadeklarowana w jego META-INF/MANIFEST.MF jest dołączana na końcu odpowiednich deklaracji pakunku wiodącego (patrz: Bundle-Classpath). Kolejność dowiązania pakunków częściowych odpowiada porządkowi ładowania poszczególnych pakunków częściowych, czyli zgodnie z ich identyfikatorami, rosnąco.

Pakunek staje się pakunkiem częściowym przez użycie nagłówka Fragment-Host ze wskazaniem na pakunek wiodący z opcjonalnym wskazaniem na jego wersję (domyślnie najwyższa), np.
 Fragment-Host: org.springframework.bundle.osgi.extender;bundle-version=1.1.0
Nazwa pakunku wiodącego użyta w Fragment-Host musi być inna niż pakunku częściowego.

Pakunek wiodący zezwala na dowiązanie pakunków częściowych, jeśli zadeklarował BundlePermission[<nazwa symboliczna pakunku>,HOST], podczas gdy pakunek częściowy musi zadeklarować BundlePermission[<nazwa symboliczna pakunku>,FRAGMENT], aby został związany.

Pakunek częściowy jest związany, dopóty jego pakunek wiodący jest w stanie RESOLVED. Przejście w stan INSTALLED powoduje odłączenie pakunków częściowych. Wywołanie metod org.osgi.service.packageadmin.PackageAdmin.refreshPackages(Bundle[] bundles) lub org.osgi.service.packageadmin.PackageAdmin.resolveBundles(Bundle[] bundles) z pakunkiem wiodącym i/lub częściowym może spowodować zmianę stanu pakunku częściowego na INSTALLED.

Zabronione jest, aby pakunek częściowy deklarował własny aktywator (nagłówek Bundle-Activator).

Ostatecznie, dla utrwalenia wiadomości, warto zapoznać się z dokumentem E.1. OSGi Fragments w dokumentacji Spring Dynamic Modules (Spring-DM).

Niezwykle istotną informacją dotyczącą wsparcia pakunków częściowych (i potencjalnie dalszej ewaluacji Spring-DM) przez dostępne platformy OSGi jest jego brak w Apache Felix - FELIX-29 Implement bundle fragments oraz FELIX-656 Implement fragment support for extending a host's bundle class path. Ciekawy jest komentarz do zgłoszenia FELIX-656, gdzie można przeczytać "I got some feedback from the GlassFish team that after I fixed the last bug it has started working for their test case, so I am resolving this for now, but there is definitely more work to be done in this area." autorstwa Richard'a Hall'a, a przecież Richard przeszedł niedawno do Sun - Sun Hires Richard Hall, więc można spodziewać się więcej tego typu komentarzy (w końcu GlassFish v3 pracuje na kontenerze OSGi, podobnie jak IBM WebSphere Application Server 6.1 czy Eclipse IDE).

Dla zainteresowanych do czego zmierzam z rozpoznawaniem roli pakunków częściowych polecam lekturę dokumentu 8.4. Configuring the web extender w dokumentacji Spring-DM. Zdaje się, że bez zrozumienia pakunków częściowych nie ma co marzyć o zrozumieniu działania Spring-DM i jego poprawnej konfiguracji (z potencjalnym rozwiązywaniem błędów). Wciąż żądni wiedzy o OSGi?! Niedługo kolejne odsłony i relacje z moich rekonesansów po terenach OSGi.

16 września 2008

Nowy sezon spotkań Warszawa JUG rozpoczęty i moje lokalne repozytorium SVN

3 komentarzy
Nie potrafię tego wytłumaczyć, ale czy to wdzięk i powab Bolesława Dawidowicza, czy też tematyka - LDAP, czyli kiedy relacyjna baza danych nie jest najlepszym rozwiązaniem, ale na otwierające, 33. spotkanie grupy Warszawa JUG przyszły tłumy - naliczyłem ponad 60 osób, w tym kilka płci żeńskiej (!) Tym samym nowy sezon spotkań grupy mamy rozpoczęty, a kolejne - 30 września - powinno spotkać się z niemniejszym zainteresowaniem, gdyż nie tylko, że temat ciekawy Zrób to sam: kompilator - podstawy języków formalnych, ANTLR i ANTLRWorks, ale któż będzie go prezentował - sam Tom (aka Szimano) ;-) Co tu dużo ukrywać - po prostu zapowiada się "tłumny" wrzesień. Szkoda, że nie wziąłem aparatu, bo zdjęcia z pewnością uatrakcyjniłyby wpis i sami moglibyście przekonać się o celowości przybycia na spotkanie (chociażby, aby znaleźć się na zdjęciu). A jakie dyskusje się toczyły. Widać, że ludzikom brakowało rzeczowych dyskusji o technologiach, bo można było odczyć pewny letni "niedosyt technologiczny". Przygotowujemy się do kolejnej konferencji Warsjava + Eclipse DemoCamp 2008 w dniu 22 listopada 2008 i dyskusje będą z pewnością jeszcze bardziej ożywione. Do zobaczenia na kolejnym 34. spotkaniu 30-tego!

Zanim jednak pojawiłem się na spotkaniu rozwiązałem pewien problem organizacyjny. Ileż to razy przyszło mi pracować lokalnie z dokumentem czy projektem, kiedy to chciałem móc wrócić do poprzedniej wersji? Na pewno uzbierałoby się tego trochę, ale jakimś cudem, mimo ciągłej pracy z repozytoriami SVN, nigdy nie przyszło mi do głowy, aby założyć własne...lokalnie. W końcu nic nie kosztuje, a może uratować cenny czas. Dzisiaj przypadkiem trafiłem na stronę Subversion Cheat Sheet i zrozumiałem, że koniec dumania czy warto, a po prostu należy skorzystać, bo jest i to całkowicie bezpłatnie (finansowo i czasowo). Teraz każdorazowo przy utrzymywaniu jakiegokolwiek pliku tekstowego umieszczę go w moim lokalnym repozytorium. W końcu zamiast serii Ctrl+Z można skorzystać z svn revert, czyż nie?

Rozpoczynam od utworzenia lokalnego repozytorium SVN.
 jlaskowski@work /cygdrive/c/projs/sandbox
$ svnadmin create 'c:/projs/sandbox/.svn_repo'
Pora na zestawienie początkowej struktury katalogowej projektu, który trafi do repozytorium. Zaleca się utworzenie podkatalogów trunk (główna gałąź rozwojowa), branches (odgałęzienia) oraz tags (wersje oznakowane).
 jlaskowski@work /cygdrive/c/projs/sandbox/projekt
$ mkdir trunk branches tags
W trunk umieszczam przykładowy plik README.txt.
 jlaskowski@work /cygdrive/c/projs/sandbox/projekt
$ ls -lR
.:
total 0
drwxr-xr-x+ 2 jlaskowski None 0 Sep 16 23:24 branches
drwxr-xr-x+ 2 jlaskowski None 0 Sep 16 23:24 tags
drwxr-xr-x+ 2 jlaskowski None 0 Sep 16 23:24 trunk

./branches:
total 0

./tags:
total 0

./trunk:
total 0
-rw-r--r-- 1 jlaskowski None 0 Sep 16 23:23 README.txt
Wszystko do tej pory robię z poziomu katalogu projektu, który ostatecznie importuję do lokalnego repozytorium.
 jlaskowski@work /cygdrive/c/projs/sandbox/projekt
$ svn import . 'file:///c:/projs/sandbox/.svn_repo/projekt' -m 'Rozpoczynamy'
Adding trunk
Adding trunk/README.txt
Adding branches
Adding tags

Committed revision 1.
Pierwsza wersja znalazła się w repozytorium. Teraz wystarczy pobrać projekt na dowolne miejsce na dysku.
 jlaskowski@work /cygdrive/c/projs/sandbox
$ svn co 'file:///c:/projs/sandbox/.svn_repo/projekt/trunk' projekt
A projekt/README.txt
Checked out revision 1.

jlaskowski@work /cygdrive/c/projs/sandbox
$ ls -l projekt/
total 0
-rw-r--r-- 1 jlaskowski None 0 Sep 16 23:26 README.txt
I tyle. Projekt jest już utrzymywany przez SVN. Oczywiście nie uchroni to przed awarią komputera, ale jako wsparcie kopii zapasowych, powinno być wręcz nieodzowne. Teraz mogę już zmieniać pliki dowolnie i przy najmniejszym wahaniu wrócić do poprzednich wersji. Mam wybór i o to chodzi.

15 września 2008

Bundle.findEntries() i spring-osgi-bundle-archetype w akcji - monitorowanie pakunków OSGi z określoną strukturą katalogową

2 komentarzy
Spring Dynamic Modules (dalej Spring-DM) umożliwia tworzenie ziaren springowych jako pakunki OSGi umożliwiając skorzystanie z cech obu środowisk - Spring Framework i OSGi. Tak określiłbym motto projektu. Jednym z elementów wspomagających tworzenie aplikacji korporacyjnych ze Spring-DM jest stworzenie środowiska uruchomieniowego, w którym cechy OSGi są integralną częścią środowiska. W ten sposób aplikacja webowa może być dystrybuowana w postaci pakunku (wystarczy jedynie dopisanie kilku nagłówków w META-INF/MANIFEST.MF) i uruchomiona w ramach Platformy OSGi (Equinox, Felix czy Knopflerfish). Teoretycznie (jeszcze) możemy sobie wyobrazić sytuację, w której poszczególne części aplikacji są dystrybuowane jako pakunki OSGi, które z kolei składają się na większy pakunek OSGi będący nota bene aplikacją webową. "A po co?" - możnaby zapytać. Odpowiedź sama się nasuwa - wprowadzenie/podniesienie modularności w aplikacji. Jeśli weźmiemy za przykład aplikację webową, to jej jedną z części mogą być servlety zgrupowane jako pakunek OSGi, który dzięki mechanizmom OSGi moglibyśmy podmienić...w trakcie działania naszej aplikacji (!) Czyż nie jest to cecha, której wprowadzenia pożądalibyśmy w naszych aplikacjach? Z pewnością!

W moim nowym artykule Bundle.findEntries() i spring-osgi-bundle-archetype w akcji - monitorowanie pakunków OSGi z określoną strukturą katalogową przedstawiłem funkcjonowanie metody org.osgi.framework.Bundle.findEntries(), której zadaniem jest zwrócenie zawartości odpytywanego pakunku jako listę URLi. W ten sposób możemy prześwietlić zawartość pakunków i poznać ich strukturę katalogową. Dodając do tego możliwość rejestracji słuchacza zdarzeń instalacja/odinstalowanie pakunków przy pomocy org.osgi.framework.BundleListener mamy doskonały sposób na monitorowanie zmian na Platformie OSGi i weryfikację, czy zainstalowany właśnie pakunek nie jest przypadkiem specjalnego traktowania przez naszą aplikację monitorującą. Zgłoszenie zdarzenia podmiany (=odinstalowania i instalacji) pakunku ponownie powoduje wzbudzenie BundleListener i wykonanie właściwej akcji. Mam wrażenie, że jest to jedyny tego typu szkielet aplikacyjny, który znosi z nas obowiązek własnoręcznego tworzenia mechanizmu monitorowania zmian w środowisku - w OSGi wystarczy jedynie implementacja BundleListener. Więcej w samym artykule. Miłej lektury!

13 września 2008

33. spotkanie Warszawskiej Grupy Użytkowników Technologii Java (Warszawa JUG)

0 komentarzy
Warszawska Grupa Użytkowników Technologii Java (Warszawa JUG) zaprasza na 33. spotkanie, które odbędzie się 16.09.2008 (nadchodzący wtorek) o godzinie 18:00 w sali 5440 Wydziału MIMUW przy ul. Banacha 2 w Warszawie.

Temat prezentacji: LDAP - czyli kiedy relacyjna baza danych nie jest najlepszym rozwiązaniem
Prowadzący: Bolesław Dawidowicz

Celem spotkania jest zaprezentowanie podstaw technologii LDAP oraz jej zastosowań. Słuchacze dowiedzą się czym różni się serwer katalogowy od bazy danych oraz w jakich sytuacjach warto rozważyć jego użycie. Zostanie przedstawione w jaki sposób można wykorzystywać usługi katalogowe z poziomu Javy oraz w jaki sposób mogą one wspomóc zabezpieczanie aplikacji JEE

Plan prezentacji:
- Wprowadzenie do technologii usług katalogowych
- Historia i zastosowanie protokołu LDAP oraz dostępne serwery
- LDAP a Java - krótkie wprowadzenie do JNDI
- LDAP i testy jednostkowe - Embedded OpenDS
- JEE Security i JAAS - krótkie wprowadzenie
- Uwierzytelnienie i autoryzacja aplikacji JEE w oparciu o LDAP - na przykładzie JBoss AS

Prezentacja prowadzona głównie w oparciu o slajdy acz nie zabraknie również kilku pokazów "na żywo" z otwartym IDE:

Wymagana wiedza: minimalna

Bolesław Dawidowicz - miłośnik i pasjonat Javy, zawodowo związany z projektem JBoss Portal. Interesuje się rozwiązaniami z kręgu Identity Management. Stara się aktywnie uczestniczyć w życiu Warszawa JUG. Przyrzeka spróbować tym razem nic nie powiedzieć o portletach ;)

Planowany czas prezentacji to 1,5 godziny, po której planuje się 15-30-minutową dyskusję.

Wstęp wolny!

Zapraszam w swoim imieniu i grupy Warszawa JUG!

11 września 2008

DSL dla konfiguracji Spring Framework

2 komentarzy
Nie pamiętam, co dokładnie sprawiło, że zacząłem poszukiwania znaczenia plików META-INF/spring.handlers oraz META-INF/spring.schemas w Spring Framework, ale pamiętam, że jednym z powodów było z pewnością znalezienie ich w źródłach Spring Dynamic Modules (moduł spring-osgi-core). A może to była lektura OSGi at LinkedIn: Integrating Spring DM (Part 1)? Postanowiłem samodzielnie spróbować się z tematem i po lekturze Appendix B. Extensible XML authoring sprawdzić w działaniu.

I po 10-15 minutach miałem temat rozpoznany. Na tyle, że kiedy dzisiaj pojawiło się pytanie w temacie The matching wildcard is strict, but no declaration can be found for element 'osgi:reference' od razu pośpieszyłem z odpowiedzią. To się nazywa proaktywna postawa wobec potrzeb klientów ;-)

Więcej o mechaniźmie upraszczania konfiguracji Spring Framework w moim artykule DSL dla konfiguracji Spring Framework.