Stworzę dokładnie taki sam pakunek jak poprzednio z tym, że teraz dorzucę Springa DM. Po co? Sam jeszcze nie wiem, ale właśnie w ten sposób mam zamiar się dowiedzieć. Spodziewam się, że cała maszyneria DI będzie dostępna za darmo, a to już sprawia, że jest warto.
Na stronach Spring DM zawarty jest przepis krok-po-kroku na pakunek OSGi - How to create a Spring bundle project in maven (and eclipse...), którym się wesprę.
jlaskowski@dev /cygdrive/c/projs/osgiJak przedstawiono we wspomnianym dokumencie, zalety wykorzystania Spring DM to m.in. automatyczne utworzenie testów integracyjnych i ich możliwość wykonania poza kontenerem OSGi.
$ mvn archetype:create \
-DarchetypeGroupId=org.springframework.osgi \
-DarchetypeArtifactId=spring-osgi-bundle-archetype \
-DarchetypeVersion=1.0 \
-DgroupId=pl.jaceklaskowski.osgi \
-DartifactId=spring-osgi-lifecycle \
-Dversion=1.0
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'archetype'.
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Default Project
[INFO] task-segment: [archetype:create] (aggregator-style)
[INFO] ------------------------------------------------------------------------
...
[INFO] OldArchetype created in dir: c:\projs\osgi\spring-osgi-lifecycle
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
jlaskowski@dev /cygdrive/c/projs/osgi/spring-osgi-lifecycleTrochę trwało zanim wszystkie zależności zostały pobrane (zapewne nie wszystkie są konieczne), ale fakt faktem, że jakiś test się wykonał i nie mam pojęcia, czy wymagało to zestawienia środowiska OSGi, czy nie. BUILD SUCCESSFUL intryguje, aby iść dalej.
$ mvn package
[INFO] Scanning for projects...
Downloading: http://repo1.maven.org/maven2//org/apache/felix/maven-bundle-plugin/1.0.0/maven-bundle-plugin-1.0.0.pom
2K downloaded
Downloading: http://repo1.maven.org/maven2//org/apache/felix/maven-bundle-plugin/1.0.0/maven-bundle-plugin-1.0.0.jar
125K downloaded
[INFO] ------------------------------------------------------------------------
[INFO] Building Spring OSGi Bundle
[INFO] task-segment: [package]
[INFO] ------------------------------------------------------------------------
...
[INFO] [surefire:test]
[INFO] Surefire report directory: c:\projs\osgi\spring-osgi-lifecycle\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running pl.jaceklaskowski.osgi.impl.BeanIntegrationTest
log4j:WARN No appenders could be found for logger (pl.jaceklaskowski.osgi.impl.BeanIntegrationTest).
log4j:WARN Please initialize the log4j system properly.
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.218 sec
Running pl.jaceklaskowski.osgi.impl.BeanImplTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 sec
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
...
[INFO] [bundle:bundle]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
Pora zaimportować projekt do jakiegoś IDE. Kiedy zaimportowałem projekt do Eclipse 3.4M5 przy pomocy wtyczki m2eclipse, dostałem ostrzeżenie
Cannot nest 'spring-osgi-lifecycle/src/main/resources/META-INF' inside 'spring-osgi-lifecycle/src/main/resources'.Usunąłem spring-osgi-lifecycle/src/main/resources/META-INF z Java Build Path, co "zniosło" komunikat.
To enable the nesting exclude 'META-INF/' from 'spring-osgi-lifecycle/src/main/resources'
Dodatkowo pojawiło się ostrzeżenie odnośnie zmiennej JUNIT_HOME, który rozwiązałem przez usunięcie JUNIT_HOME/junit.jar z zakładki Libraries. Dodałem w zamian bibliotekę JUnit 4 (Add Library > JUnit > JUnit 4).
Utworzenie projektu przy pomocy spring-osgi-bundle-archetype to dodatkowo utworzenie dwóch testów: jednostkowego dla wykonania ziarna (które Spring "ubiera" w szaty modułu OSGi podczas uruchomienia) i integracyjnego do zasymulowania pracy pakunku w środowisku OSGi.
Interfejs biznesowy utworzonego pakunku to pl.jaceklaskowski.osgi.Bean:
package pl.jaceklaskowski.osgi;a jego implementacja pl.jaceklaskowski.osgi.impl.BeanImpl jest następująca:
public interface Bean {
boolean isABean();
}
package pl.jaceklaskowski.osgi.impl;W końcu, co nie było do tej pory przeze mnie podkreślane, to właśnie jest pakunek OSGi, czyli moduł udostępniający pewną usługę - interfejs i jego implementacja.
import pl.jaceklaskowski.osgi.Bean;
public class BeanImpl implements Bean {
public boolean isABean() {
return true;
}
}
Konfiguracja Springa znajduje się w pliku src/main/resources/META-INF/spring/bundle-context.xml.
<?xml version="1.0" encoding="UTF-8"?>Nic szczególnego ponownie, bo i OSGi w swojej prostocie i łatwości wykorzystania jest właśnie takie. Można by powiedzieć nic szczególnego. To, co nadaje sens zastosowaniu OSGi w projektach to jego możliwości, które uaktywniają się podczas uruchomienia aplikacji opartej o pakunki OSGi, gdzie m.in. dostęp do zasobów jest dodatkowo zabezpieczony przez warstwę bezpieczeństwa OSGi. W przypadku "czystego" JVM dostęp do klas i interfejsów jest dozwolony, dla każdego, jeśli tylko klasa/interfejs znajduje się na ścieżce klas.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- regular spring configuration file defining the beans for this
bundle. We've kept the osgi definitions in a separate
configuration file so that this file can easily be used
for integration testing outside of an OSGi environment -->
<bean name="myBean" class="pl.jaceklaskowski.osgi.impl.BeanImpl" />
</beans>
Dodaję do projektu pakunku jego aktywatora - klasę pl.jaceklaskowski.osgi.AktywatorPakunku, który prezentowałem w poprzedniej notatce o OSGi. Z jakiś nieznanych mi powodów domyślna konfiguracja projektu po wczytaniu do Eclipse nie dawała mi dostępu do klas OSGi i jedynym rozwiązaniem było wykonanie mvn eclipse:eclipse w katalogu projektu i F5 (Refresh) projektu w Eclipse. W ten sposób zostały dodane do projektu Eclipse biblioteki zależne jak poszukiwany (org.osgi.)org.osgi.core-4.0.jar.
Dodanie aktywatora wymusiło utworzenie konfiguracji pakunku w pom.xml w konfiguracji wtyczki maven-bundle-plugin o dodanie nagłówka Bundle-Activator. Warto zapamiętać.
Pora na sprawdzenie poprawności konstrukcji pakunku przez jego uruchomienie.
Uruchamiam środowisko OSGi, którym będzie Apache Felix.
jlaskowski@dev /cygdrive/c/apps/felixNastępnie sprawdzam dostępne pakunki w systemie.
$ java -jar bin/felix.jar
Welcome to Felix.
=================
Enter profile name: notatnik
DEBUG: WIRE: 1.0 -> org.osgi.service.packageadmin -> 0
DEBUG: WIRE: 1.0 -> org.osgi.service.startlevel -> 0
DEBUG: WIRE: 1.0 -> org.ungoverned.osgi.service.shell -> 1.0
DEBUG: WIRE: 1.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 1.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 2.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 2.0 -> org.apache.felix.shell -> 1.0
DEBUG: WIRE: 3.0 -> org.osgi.service.obr -> 3.0
DEBUG: WIRE: 3.0 -> org.osgi.framework -> 0
-> DEBUG: WIRE: 3.0 -> org.apache.felix.shell -> 1.0
-> psInstaluję właśniezbudowany pakunek z użyciem Springa...
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.3)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.2)
-> install file:c:/projs/osgi/spring-osgi-lifecycle/target/spring-osgi-lifecycle-1.0.jari uruchamiam go.
Bundle ID: 4
-> start 4Teraz mogę zatrzymać pakunek poleceniem stop.
DEBUG: WIRE: 4.0 -> org.osgi.framework -> 0
DEBUG: WIRE: 4.0 -> pl.jaceklaskowski.osgi.impl -> 4.0
DEBUG: WIRE: 4.0 -> pl.jaceklaskowski.osgi -> 4.0
2008-03-26 13:40:48 pl.jaceklaskowski.osgi.AktywatorPakunku start
INFO: start() wykonano - rozpoczynam pracŕ
-> stop 4Kolejne sprawdzenie, co mamy w systemie dostępnego (jakie pakunki są znane systemowi).
2008-03-26 13:40:53 pl.jaceklaskowski.osgi.AktywatorPakunku stop
INFO: stop() wykonano - czyszczŕ po sobie
-> psOdinstalowanie pakunku o identyfikatorze 4, który jest moim pakunkiem stworzonym ze Springiem.
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.3)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.2)
[ 4] [Resolved ] [ 1] Spring OSGi Bundle (1.0)
-> uninstall 4Weryfikacja, czy wszystko poszło jak należy (a raczej, czy wszystko wciąż rozumiem).
-> psI na koniec zamknięcie systemu - środowiska OSGi, którym jest Felix.
START LEVEL 1
ID State Level Name
[ 0] [Active ] [ 0] System Bundle (1.0.3)
[ 1] [Active ] [ 1] Apache Felix Shell Service (1.0.0)
[ 2] [Active ] [ 1] Apache Felix Shell TUI (1.0.0)
[ 3] [Active ] [ 1] Apache Felix Bundle Repository (1.0.2)
-> shutdownWszystko poszło zgodnie z planem i użycie Springa DM było całkowicie bezinwazyjne. Oczywiście zakładam, że poznanie tego, co faktycznie integracja między Spring a OSGi w wykonaniu Spring DM dostarcza jest wciąż przede mną, ale o tym w następnych odsłonach (bo w końcu możnaby zadać pytanie Po co w ogóle zajmować się OSGi?, nieprawdaż?).
Do pełnego obrazu, pozostaje zaprezentować pom.xml:
<?xml version="1.0"?>Na koniec, pytanie (nie)konkursowe dla wytrwałych: Jaki nagłówek MANIFEST.MF definiuje aktywator pakunku? I pytanie uzupełniające: W jaki sposób zdefiniować nagłówek aktywatora pakunku w projekcie zarządzanym przez Maven2? Nagród nie należy oczekiwać.
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>pl.jaceklaskowski.osgi</groupId>
<artifactId>spring-osgi-lifecycle</artifactId>
<packaging>bundle</packaging>
<name>Spring OSGi Bundle</name>
<version>1.0</version>
<url>http://www.springframework.org/osgi</url>
<properties>
<slf4j.version>1.4.3</slf4j.version>
<spring.maven.artifact.version>2.5.1</spring.maven.artifact.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.osgi</groupId>
<artifactId>junit.osgi</artifactId>
<version>3.8.2-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.maven.artifact.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.maven.artifact.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.maven.artifact.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.maven.artifact.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl104-over-slf4j</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>4.0</version>
</dependency>
</dependencies>
<!-- for packaging as an OSGi bundle, we use the maven-bundle-plugin -->
<!-- see http://felix.apache.org/site/maven-bundle-plugin-bnd.html for more info -->
<build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<version>1.0.0</version>
<configuration>
<instructions>
<manifestLocation>META-INF</manifestLocation>
<Export-Package>pl.jaceklaskowski.osgi*</Export-Package>
<Import-Package>*</Import-Package>
<Bundle-Activator>${groupId}.AktywatorPakunku</Bundle-Activator>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
<!-- ================================================ -->
<!-- Repository Configuration -->
<!-- ================================================ -->
<repositories>
<repository>
<id>apache.m2.incubator</id>
<name>Apache M2 Incubator Repository</name>
<url>http://people.apache.org/repo/m2-incubating-repository/</url>
</repository>
<repository>
<id>eclipse-repository</id>
<name>Eclipse Repository</name>
<url>http://repo1.maven.org/eclipse/</url>
</repository>
<repository>
<id>safehaus-repository</id>
<name>Safehaus Repository</name>
<url>http://m2.safehaus.org</url>
</repository>
<repository>
<id>spring-ext</id>
<name>Spring External Dependencies Repository</name>
<url> https://springframework.svn.sourceforge.net/svnroot/springframework/repos/repo-ext/ </url>
</repository>
<repository>
<id>i21-s3-osgi-repo</id>
<name>i21 osgi artifacts repo</name>
<snapshots>
<enabled>true</enabled>
</snapshots>
<url>http://s3.amazonaws.com/maven.springframework.org/osgi</url>
</repository>
<repository>
<id>i21-s3-maven-repo</id>
<name>i21 S3 milestone repo</name>
<url>http://s3.amazonaws.com/maven.springframework.org/milestone</url>
</repository>
<!--
Spring-DM snapshot repository - disabled by default
<repository>
<snapshots><enabled>true</enabled></snapshots>
<id>springframework.org</id>
<name>Springframework Maven SNAPSHOT Repository</name>
<url>http://s3.amazonaws.com/maven.springframework.org/snapshot</url>
</repository>
-->
</repositories>
<pluginRepositories>
<pluginRepository>
<id>maven-repo</id>
<name>maven repo</name>
<url>http://repo1.maven.org/maven2/</url>
</pluginRepository>
</pluginRepositories>
</project>
Thanks for your posting and have a good week.
OdpowiedzUsuńNo to mam szansę odpowiedzieć na pytanie :) - przez półtora roku nie było nikogo bardziej wytrwałego!
OdpowiedzUsuńAktywator pakunku jest wskazany przez nagłówek
Bundle-Activator: your.package.BundleActivator
a w mavenie jako parametr do pluginu org.apache.felix:maven-bundle-plugin
[Bundle-Activator]your.package.BundleActivator[/Bundle-Activator]