OSGi Startreihenfolge von Bundles

splinter

Mitglied
Hi Leute.

Ich habe gerade angefangen mich mit OSGi vertraut zu machen und stehe vor folgendem Problem:

2 Bundles:
- test.host
- test.user

test.host exportiert die API test.host, welche aus einem POJO mit einem Getter für ein Objekt besteht. In einem internen Package (test.host.internal) ist ein Activator, welcher beim Start die Variable für den Getter in der API setzt.

test.user exportiert keine API und hat den einzigen Zweck in der Start-Methode des Activators über test.host genannte Variable auszulesen und auf der Konsole auszugeben.

Manifest von test.host:
Code:
Bundle-SymbolicName: test.host
Import-Package: org.osgi.framework;version="[1.5.0,2.0.0)"
Export-Package: test.host;version="1.0.0"
Bundle-Activator: test.host.internal.Activator

Manifest von test.user:
Code:
Bundle-SymbolicName: test.user
Bundle-Activator: test.user.internal.Activator
Import-Package: org.osgi.framework;version="[1.5.0,2.0.0)",
 test.host;version="[1.0.0,2.0.0)"

Startlevels sind für beide Bundles auf default.
OSGi-Implementation ist Equinox (org.eclipse.osgi v3.6.2), IDE ist Eclipse Helios.

Wird nun diese Platform gestartet, so startet test.user zuerst und erhät über die test.host API eine NullPointerException, weil die Variable noch nicht gesetzt wurde. Erst nachher wird test.host fehlerfrei gestartet. Starte ich nun test.user manuell verläuft alles fehlerfrei.

Die Bundles werden nicht asynchron gestartet (habe Threads über hashCode() verglichen), Lazy-Activation hat hier keine Auswirkungen.

Meines Wissens nach sollte ein Bundle vor dem ersten exportieren einer eigenen Klasse gestartet werden und nicht danach.
Worin liegt hier mein grundlegendes Fehlverständnis über OSGi?

MfG
splinter

P.S.: Auch wenn ich Anfänger bin dürft ihr ruhig mit Spezialbegriffen antworten, habe ein 500-seitiges Manual über OSGi und Equinox neben mir, dass ich grösstenteils auch gelesen habe.
 

Wildcard

Top Contributor
Wie versuchst du denn auf die Variable zuzugreifen, bzgw wo bekommst du die Instanz her?
Das ganze hört sich auch sehr danach an das hier eigentlich ein Service angebracht wäre. Schau dir zB mal Declarative Services an.
 

splinter

Mitglied
Da hast du völlig recht, in diesem Fall wäre DS durchaus angebracht und ich werde diese auch verwenden. Jedoch geht es mir hier um das grundlegende Verständnis über Classloading und Activators in OSGi, betrachte die Anwendung als Beispiel.

test.host besteht aus den folgenden zwei Klassen:

test.host.internal.Activator
Java:
package test.host.internal;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

	private static final int MODE = 0;

	private static String[] configuration = null;

	public static String getMode() {
		return configuration[MODE];
	}

	private static void loadConfiguration() {
		configuration = new String[]{"on"};
	}

	@Override
	public void start(BundleContext context) {
		System.out.println("Starting host on: " + Thread.currentThread().hashCode());
		loadConfiguration();
		System.out.println("Host started!");
	}

	@Override
	public void stop(BundleContext context) {
		System.out.println("Stopping host on: " + Thread.currentThread().hashCode());
	}
}

test.host.Config
Java:
package test.host;

import test.host.internal.Activator;

public class Config {

	public String getMode() {
		return Activator.getMode();
	}
}

test.user besteht nur aus einem Activator:

test.user.internal.Activator
Java:
package test.user.internal;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

import test.host.Config;

public class Activator implements BundleActivator {

	private static Config configuration = null;

	@Override
	public void start(BundleContext bundleContext) {
		System.out.println("Starting user on: " + Thread.currentThread().hashCode());
		configuration = new Config();
		System.out.println("Mode: " + configuration.getMode());
	}

	@Override
	public void stop(BundleContext bundleContext) throws Exception {
		System.out.println("Stopping user on: " + Thread.currentThread().hashCode());
		configuration = null;
	}
}

Erstens ist mir jetzt nicht klar weshalb das Framework test.user zuerst startet, obwohl dieses auf test.host angewiesen ist. Ich bin davon ausgegangen, dass ein Import aus einem fremden Bundle erst NACH dem Starten des Bundles möglich ist, also folgender Ablauf erfolgt:

  • Classloading-Request von test.user an test.host
  • Erstellen des Classloaders für test.host
  • Starten von test.host (ausführen von Activator.start)
  • Rückgabe der gewünschten Klasse an test.user

Starte ich die Bundles erhalte ich jedoch folgendes Log:

Code:
Starting user on: 760805843
!SESSION 2011-05-04 19:03:13.560 -----------------------------------------------
eclipse.buildId=unknown
java.version=1.6.0_21
java.vendor=Sun Microsystems Inc.
BootLoader constants: OS=win32, ARCH=x86_64, WS=win32, NL=de_CH
Command-line arguments:  -dev file:D:/workspace/.metadata/.plugins/org.eclipse.pde.core/QBot/dev.properties -os win32 -ws win32 -arch x86_64 -consoleLog -console

!ENTRY test.user 4 0 2011-05-04 19:03:14.855
!MESSAGE 
!STACK 0
org.osgi.framework.BundleException: Exception in test.user.internal.Activator.start() of bundle test.user.
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:806)
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:755)
	at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:370)
	at org.eclipse.osgi.framework.internal.core.AbstractBundle.resume(AbstractBundle.java:374)
	at org.eclipse.osgi.framework.internal.core.Framework.resumeBundle(Framework.java:1067)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:561)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:546)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.incFWSL(StartLevelManager.java:459)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.doSetStartLevel(StartLevelManager.java:243)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.dispatchEvent(StartLevelManager.java:440)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:227)
	at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:337)
Caused by: java.lang.NullPointerException
	at test.host.internal.Activator.getMode(Activator.java:13)
	at test.host.Config.getMode(Config.java:8)
	at test.user.internal.Activator.start(Activator.java:16)
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:783)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:774)
	... 11 more
Root exception:
java.lang.NullPointerException
	at test.host.internal.Activator.getMode(Activator.java:13)
	at test.host.Config.getMode(Config.java:8)
	at test.user.internal.Activator.start(Activator.java:16)
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl$1.run(BundleContextImpl.java:783)
	at java.security.AccessController.doPrivileged(Native Method)
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl.startActivator(BundleContextImpl.java:774)
	at org.eclipse.osgi.framework.internal.core.BundleContextImpl.start(BundleContextImpl.java:755)
	at org.eclipse.osgi.framework.internal.core.BundleHost.startWorker(BundleHost.java:370)
	at org.eclipse.osgi.framework.internal.core.AbstractBundle.resume(AbstractBundle.java:374)
	at org.eclipse.osgi.framework.internal.core.Framework.resumeBundle(Framework.java:1067)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:561)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.resumeBundles(StartLevelManager.java:546)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.incFWSL(StartLevelManager.java:459)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.doSetStartLevel(StartLevelManager.java:243)
	at org.eclipse.osgi.framework.internal.core.StartLevelManager.dispatchEvent(StartLevelManager.java:440)
	at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:227)
	at org.eclipse.osgi.framework.eventmgr.EventManager$EventThread.run(EventManager.java:337)
Starting host on: 760805843
Host started!

!ENTRY org.eclipse.osgi 4 0 2011-05-04 19:03:14.871
!MESSAGE Bundle test.user_1.0.0 [9] is not active.

Die Ablauf sieht also wie folgt aus:
  • Classloading-Request von test.user an test.host
  • Erstellen des Classloaders für test.host
  • Rückgabe der gewünschten Klasse an test.user
  • Starten von test.host (Ausführen von Activator.start)

Wenn ich nun mit start test.user den User manuell starte verläuft alles wie geplant:
Code:
osgi> start test.user

Starting user on: 51619133
Mode: on

osgi> ss

Framework is launched.

id	State       Bundle
0	ACTIVE      org.eclipse.osgi_3.6.2.R36x_v20110210
9	ACTIVE      test.user_1.0.0
16	ACTIVE      test.host_1.0.0

Auch beim beenden der Platform behält Equinox diese Reihenfolge bei:
Code:
osgi> close

Stopping host on: 51619133
Stopping user on: 51619133

Also stelle ich meine Frage allgemeiner: Wie verhält sich der Ablauf zwischen Classloading und Activator unter OSGi? In diesem Fall sind die Klassen von test.host bereits vor dem STARTING Zustand für andere Bundles verwendbar und nicht erst bei ACTIVE. Das widerspricht dem was ich über OSGi gelesen geglaubt habe. Wie siehts denn nun genau aus damit?
 
Zuletzt bearbeitet:
M

maki

Gast
Kann sein dass ich mich irre, aber OSGi stellt nur für Services (ein zentrales Konzepot für OSGi) die vorherige "Initialisierung" des Bundles durch den Activator sicher.
Das führt zu deinem Fehler in test.host.internal.Activator, dessen start Methode ist noch gar nicht aufgerufen, während du schon getMode() von test.user.internal.Activator.start() aus aufrufst.
 

splinter

Mitglied
Okay, hab die ganze Sache nun nochmals mit Lazy-Activation versucht und nun funktionierts... Keine Ahnung weshalb das beim ersten mal nicht funktioniert hat! :bahnhof:

Trotzdem... eigentlich sollte die Lazy-Policy nur den Start des Bundles verzögern und nicht einen anderen Classloading-Mechanismus einsetzen. ???:L

Wie auch immer, wenn jemand genaueres weiss darüber bin ich froh um jede Antwort. Ich muss demnächst eine solche Platform mit mehreren hundert Bundles für den produktiven Einsatz aufsetzen und habe nicht gerade ein reines Gewissen wenn ich über diese Mechanismen nicht genauestens Bescheid weis.

Vielen Dank für die bisherigen Antworten. :)
 

Wildcard

Top Contributor

Ähnliche Java Themen


Oben