OSGi The Lifecycle Layer The Extender Pattern Reading

  • Slides: 37
Download presentation
OSGi: The Lifecycle Layer. The Extender Pattern

OSGi: The Lifecycle Layer. The Extender Pattern

Reading • Ch. 1: OSGi revealed • Ch 2: Mastering modularity • Ch 3:

Reading • Ch. 1: OSGi revealed • Ch 2: Mastering modularity • Ch 3: Learning lifecycle • Ch 4: Studying services • Ch 11: Component models and frameworks

Lifecycle Management • Defines the bundle lifecycle operations : – The lifecycle operations allow

Lifecycle Management • Defines the bundle lifecycle operations : – The lifecycle operations allow you to manage and evolve an application from its outside, by dynamically changing the composition of bundles inside a running framework. • Defines how your bundles gain access to their execution context : – Internal to an application, the lifecycle layer provides bundles with a way to interact with the OSGi framework and the facilities it provides at execution time.

OSGi Bundle Lifecycle

OSGi Bundle Lifecycle

OSGI Console • Most OSGi framework implementations have their own shells for interacting with

OSGI Console • Most OSGi framework implementations have their own shells for interacting with a running framework • Some OSGI Console commands: – – – – – help List all available commands. ss List of all bundles together with their state and id. ss <string> List all bundles with names containing that string. start <id> Start up the bundle with a given id. stop <id> Stop the bundle with the given id. install <url> Install the bundle that the URL refers to. uninstall <id> Uninstall the bundle with the given id. diag <id> Show resolution problems for bundle with given id. exit

The System Bundle • At execution time, the framework is represented as a bundle

The System Bundle • At execution time, the framework is represented as a bundle with an identifier of 0, called the system bundle. You don’t install the system bundle—it always exists while the framework is running. • The system bundle follows the same lifecycle as normal bundles, so you can manipulate it with the same operations as normal bundles, but be careful ! Stopping the system bundle means shutting the system down.

The Bundle Cache • When installing a bundle, the framework reads the bundle JAR

The Bundle Cache • When installing a bundle, the framework reads the bundle JAR file and saves a copy in a private area known as the bundle cache. This means two things: – Installing a bundle into the framework is a persistent operation. – After the bundle is installed, the framework no longer needs the original copy of the bundle JAR file. • If you start an OSGi framework, install some bundles, shut down the framework, and then restart it, the bundles you installed will still be there in their original states • In terms of your application, the bundle cache acts as the deployed configuration of the application. Your application’s configuration is the set of bundles you install into the framework

The Lifecycle Layer API • The lifecycle layer API is composed of three main

The Lifecycle Layer API • The lifecycle layer API is composed of three main interfaces: Bundle. Activator, Bundle. Context, and Bundle. • A Bundle. Activator is how a bundle hooks into the lifecycle layer to become lifecycle aware, • The Bundle. Context represents the OSGi container, offers access to all framework facilities for inspecting and modifying the framework state at execution time. • Each bundle has an associated Bundle object

Bundle Activator • The bundle activator is a class implementing the org. osgi. framework.

Bundle Activator • The bundle activator is a class implementing the org. osgi. framework. Bundle. Activator interface • This interface provides the bundle with a hook into the lifecycle layer and the ability to customize what happens when it’s started or stopped. • The framework knows about the activator of a bundle from its manifest file: the Bundle-Activator header specifies the class implementing the activator • Not all bundles need an activator! An activator is necessary only if you’re creating a bundle and wish to specifically interact with OSGi API or need to perform custom initialization/de-initialization actions.

The Bundle. Activator Interface public interface Bundle. Activator { public void start(Bundle. Context context)

The Bundle. Activator Interface public interface Bundle. Activator { public void start(Bundle. Context context) throws Exception; public void stop(Bundle. Context context) throws Exception; } • When the bundle is installed and started, the framework constructs an instance of the activator class and invokes the start() method. When the bundle is stopped, the framework invokes the stop() method. • The activator instance on which start() is called is the same instance on which stop() is called. • After stop() is called, the activator instance is destroyed. If the bundle is restarted after being stopped, a new activator instance is created !

Activator Example import org. foo. hello. Greeting; import org. osgi. framework. Bundle. Activator; import

Activator Example import org. foo. hello. Greeting; import org. osgi. framework. Bundle. Activator; import org. osgi. framework. Bundle. Context; public class Activator implements Bundle. Activator { public void start(Bundle. Context ctx) { System. out. println("My Message - Started bundle Hello"); } public void stop(Bundle. Context ctx) { System. out. println("My Message - Stopped bundle Hello"); } }

Example: Hello World Which bundle really needs an Activator ? org. foo. hello Greeting

Example: Hello World Which bundle really needs an Activator ? org. foo. hello Greeting org. foo. hello import org. foo. hello. cli Client import Activator org. foo. hello. impl Import org. osgi. * Greeting. Impl

Example: Hello World Which bundle really needs an Activator ? • Not all bundles

Example: Hello World Which bundle really needs an Activator ? • Not all bundles need an activator! • The hello and hello. impl bundles are used in this scenario as simple libraries. They do not need activators. Still we may give them an activator (such as the one printing messages) • The client bundle does not export anything; it needs an activator to start its activity. The start() method of the activator is acting as an entry point similar with a main() function.

Client. Activator (version 1) public class Client. Activator implements Bundle. Activator { private Greeting

Client. Activator (version 1) public class Client. Activator implements Bundle. Activator { private Greeting g=null; public void start(Bundle. Context context) throws Exception { g= new Greeting. Impl(); System. out. println(g. say. Hello()); } public void stop(Bundle. Context context) throws Exception { System. out. println("Greeting - stop"); } } The start() method must return quickly ! (because only after this the bundle is ACTIVE) Threads may be created and started here if needed ! All threads must be finished at stop().

Client. Activator (Version 2) public void start(Bundle. Context context) throws Exception { g=new Greeting.

Client. Activator (Version 2) public void start(Bundle. Context context) throws Exception { g=new Greeting. Impl(); stop = false; new Thread(new Runnable() { public void run() { while (!stop) { try { Thread. sleep(8000); System. out. println(g. say. Hello()); } catch (Exception e) {} } } }). start(); } public void stop(Bundle. Context context) throws Exception { stop = true; }

The Bundle. Context Interface public interface Bundle. Context {. . . String get. Property(String

The Bundle. Context Interface public interface Bundle. Context {. . . String get. Property(String key); Bundle get. Bundle(); Bundle install. Bundle(String location, Input. Stream input) throws Bundle. Exception; Bundle install. Bundle(String location) throws Bundle. Exception; Bundle get. Bundle(long id); Bundle[] get. Bundles(); void add. Bundle. Listener(Bundle. Listener listener); void remove. Bundle. Listener(Bundle. Listener listener); void add. Framework. Listener(Framework. Listener listener); void remove. Framework. Listener(Framework. Listener listener); . . . }

The Bundle Interface public interface Bundle {. . . Bundle. Context get. Bundle. Context();

The Bundle Interface public interface Bundle {. . . Bundle. Context get. Bundle. Context(); long get. Bundle. Id(); Dictionary get. Headers(String locale); String get. Location(); int get. State(); String get. Symbolic. Name(); Version get. Version(); void start(int options) throws Bundle. Exception; void start() throws Bundle. Exception; void stop(int options) throws Bundle. Exception; void stop() throws Bundle. Exception; void update(Input. Stream input) throws Bundle. Exception; void update() throws Bundle. Exception; void uninstall() throws Bundle. Exception; . . . }

Bundle. Context Example import org. foo. hello. Greeting; org. osgi. framework. Bundle. Activator; org.

Bundle. Context Example import org. foo. hello. Greeting; org. osgi. framework. Bundle. Activator; org. osgi. framework. Bundle. Context; public class Activator implements Bundle. Activator { public void start(Bundle. Context ctx) { System. out. println("My Message - Started bundle Hello"); System. out. println("All the bundles in my context: "); Bundle[] bundles=ctx. get. Bundles(); for (Bundle b: bundles) { System. out. println(b. get. Bundle. Id()+" "+b. get. Symbolic. Name()) } } … }

Listening for events • The lifecycle layer API provides access to a lot of

Listening for events • The lifecycle layer API provides access to a lot of information, but it isn’t easy to poll for changes; it’s much more convenient if you can be notified when changes occur. To make this possible, the OSGi framework supports two types of events: Bundle. Events and Framework. Events. The former event type reports changes in the lifecycle of bundles, whereas the latter reports framework-related issues. • You can use the normal Java listener pattern in your bundles to receive these events. The Bundle. Context object has methods to register Bundle. Listener and Framework. Listener objects for receiving Bundle. Event and Framework. Event notifications, respectively.

Bundle events • • Bundle. Event. INSTALLED Bundle. Event. RESOLVED Bundle. Event. STARTED Bundle.

Bundle events • • Bundle. Event. INSTALLED Bundle. Event. RESOLVED Bundle. Event. STARTED Bundle. Event. STOPPED Bundle. Event. UPDATED Bundle. Event. UNINSTALLED Bundle. Event. UNRESOLVED

Bundle. Listener Example class My. Bundle. Listener implements Bundle. Listener { public void bundle.

Bundle. Listener Example class My. Bundle. Listener implements Bundle. Listener { public void bundle. Changed(Bundle. Event event) { System. out. println("Event type="+event. get. Type()+ " bundle"+event. get. Bundle()+ " source="+event. get. Source()); } } public class Activator implements Bundle. Activator { public void start(Bundle. Context ctx) { System. out. println("My Message - Started bundle Hello"); ctx. add. Bundle. Listener(new My. Bundle. Listener()); } … }

The Extender Pattern • The goal of the extender pattern is to achieve dynamically

The Extender Pattern • The goal of the extender pattern is to achieve dynamically extensible applications. • The technique of the extender pattern relies on exploiting the lifecycle events (installing, resolving, starting, stopping, etc) of other bundles and the feature of defining custom attributes in bundle metadata.

The Extender Pattern • Typically, some bundle in the application acts as the extender:

The Extender Pattern • Typically, some bundle in the application acts as the extender: it listens for bundles being started and/or stopped. When a bundle is started, the extender probes it to see if it’s an extension bundle. The extender looks in the bundle’s manifest for specific metadata it recognizes. • If the bundle does contain an extension, the extension is described by the metadata. The extender reads the metadata and performs the necessary tasks on behalf of the extension bundle to integrate it into the application. • The extender also listens for extension bundles to be stopped, in which case it removes the associated extensions from the application.

Example: Hello World Dynamically Extensible Application • The client must detect, during its runtime,

Example: Hello World Dynamically Extensible Application • The client must detect, during its runtime, if Greeting Extension bundles are activated and immediately use them • If a Greeting Extension bundle is stopped the client will remove the extension • A bundle is considered a Greeting Extension if: – Its metadata contain the custom attributes • Greeting. Extension. Name • Extension. Class – It is expected that the extensionclass implements the interface Greeting

Example: Hello World Dynamically Extensible Application org. foo. hello Greeting Extension bundle org. foo.

Example: Hello World Dynamically Extensible Application org. foo. hello Greeting Extension bundle org. foo. hello. impl 1 MF Greeting. Impl 1

Example: Hello World Manifest file for English. Greeting Extension Manifest-Version: 1. 0 Bundle-Manifest. Version:

Example: Hello World Manifest file for English. Greeting Extension Manifest-Version: 1. 0 Bundle-Manifest. Version: 2 Bundle-Name: Implem 1 Bundle-Symbolic. Name: org. foo. hello. implem 1 Bundle-Version: 1. 0. 0. qualifier Bundle-Activator: org. foo. hello. implem 1. Activator Import-Package: org. foo. hello, org. osgi. framework; version="1. 3. 0" Bundle-Required. Execution. Environment: Java. SE-1. 7 Greeting-Extension-Name: English. Greeting Extension-Class: org. foo. hello. implem 1. Greeting. Impl 1

Example: Hello World Manifest file for French. Greeting Extension Manifest-Version: 1. 0 Bundle-Manifest. Version:

Example: Hello World Manifest file for French. Greeting Extension Manifest-Version: 1. 0 Bundle-Manifest. Version: 2 Bundle-Name: Implem 2 Bundle-Symbolic. Name: org. foo. hello. implem 2 Bundle-Version: 1. 0. 0. qualifier Bundle-Activator: org. foo. hello. implem 2. Activator Import-Package: org. foo. hello, org. osgi. framework; version="1. 3. 0" Bundle-Required. Execution. Environment: Java. SE-1. 7 Greeting-Extension-Name: French. Greeting Extension-Class: org. foo. hello. implem 2. Greeting. Impl 2

Example: Hello World Dynamically Extensible Application org. foo. hello Greeting org. foo. hello import

Example: Hello World Dynamically Extensible Application org. foo. hello Greeting org. foo. hello import org. foo. hello. cli org. foo. hello. impl 1 Client Activator Greeting. Impl 1 org. foo. hello. impl 2 MF Import org. osgi. * Greeting. Impl 2

Example: Hello World Dynamically Extensible Application • Applying the Extender Pattern: – The client

Example: Hello World Dynamically Extensible Application • Applying the Extender Pattern: – The client registers Greeting. Listener - a Bundle. Listener listening for Bundle. Event. STARTED and Bundle. Event. STOPPED events – When receiving the event notification, it will probe if the bundle that generated it is a Greeting Extension (by inspecting its metadata for the Extension. Name attribute) – If the event was not generated by an extension bundle, ignore it – If the bundle is an extension bundle that has to be added: • Inspect metadata to know the value of the Extension. Class attribute • Request the bundle to load the extension class and instantiate it via reflection • The client will get the instantiated object and use it via the Greeting interface

public class Greeting. Listener implements Bundle. Listener { private Greeting. Set m_gs = null;

public class Greeting. Listener implements Bundle. Listener { private Greeting. Set m_gs = null; public Greeting. Listener(Greeting. Set gs) { m_gs = gs; } public void bundle. Changed(Bundle. Event event) { … } private boolean is. Extension(Bundle bundle) { … } private void add. Extension(Bundle bundle) { … } private void remove. Extension(Bundle bundle) { … } }

Greeting. Listener: : bundle. Changed public void bundle. Changed(Bundle. Event event) { System. out.

Greeting. Listener: : bundle. Changed public void bundle. Changed(Bundle. Event event) { System. out. println("Listener: Event type=" + event. get. Type() + " bundle“ + event. get. Bundle() + " source=" + event. get. Source()); Bundle bundle = event. get. Bundle(); if (event. get. Type() == Bundle. Event. STARTED) { if (is. Extension(bundle)) add. Extension(bundle); } if (event. get. Type() == Bundle. Event. STOPPED) { if (is. Extension(bundle)) remove. Extension(bundle); } }

Greeting. Listener: : is. Extension private boolean is. Extension(Bundle bundle) { Dictionary dict =

Greeting. Listener: : is. Extension private boolean is. Extension(Bundle bundle) { Dictionary dict = bundle. get. Headers(); // Try to get the name of the extension. String name = (String) dict. get(“Greeting-Extension-Name”); // Return immediately if the bundle is not an extension. if (name == null) { System. out. println("Bundle " + bundle. get. Symbolic. Name() + " not a Greeting Extension"); return false; } System. out. println("Bundle " + bundle. get. Symbolic. Name() + " is a Greeting Extension"); return true; }

Greeting. Listener: : add. Extension private void add. Extension(Bundle bundle) { System. out. println("Greeting

Greeting. Listener: : add. Extension private void add. Extension(Bundle bundle) { System. out. println("Greeting extension will be added: " + bundle. get. Symbolic. Name()); Dictionary dict = bundle. get. Headers(); String name = (String) dict. get(“Greeting-Extension-Name”); // Get the class of the extension. String class. Name = (String) dict. get(“Extension-Class”); Class clazz = null; try { clazz = bundle. load. Class(class. Name); m_gs. add. Greeting(name, (Greeting) clazz. new. Instance()); } catch (Exception e) { e. print. Stack. Trace(); } }

Client. Activator: : start public void start(Bundle. Context context) throws Exception { stop =

Client. Activator: : start public void start(Bundle. Context context) throws Exception { stop = false; m_gs = new Greeting. Set(); context. add. Bundle. Listener(new Greeting. Listener(m_gs)); new Thread(new Runnable() { public void run() { while (!stop) { try { Thread. sleep(8000); String text = ""; for (Object g : m_gs. get. All. Greetings()) { text = text + " " + ((Greeting) g). say. Hello(); } System. out. println(text); } catch (Exception e) {} } } }). start(); }

Summary • A bundle can only be used by installing it into a running

Summary • A bundle can only be used by installing it into a running instance of the OSGi framework. • The framework associates a lifecycle state with each installed bundle • The lifecycle layer API is composed of three main interfaces: Bundle. Activator, Bundle. Context, and Bundle. • A Bundle. Activator is how a bundle hooks into the lifecycle layer to become lifecycle aware, which allows it to gain access to all framework facilities for inspecting and modifying the framework state at execution time. • Monitoring bundle lifecycle events is a form of dynamic extensibility available in the OSGi framework based on the dynamically changing installed set of bundles (also known as the extender pattern).