JBoss.orgCommunity Documentation

JBossOSGi - User Guide

Version: 1.0.0.Beta8-SNAPSHOT

Date: 01-Jun-2010 08:21:35


1. Introduction
1.1. What is OSGi
1.2. OSGi Framework Overview
1.3. OSGi Service Compendium
2. Getting Started
2.1. Download the Distribution
2.2. Running the Installer
2.3. Starting the Runtime
2.4. Provided Examples
2.5. Bundle Deployment
2.6. Managing installed Bundles
2.7. Hudson QA Environment
3. JBoss OSGi Runtime
3.1. Overview
3.2. Features
3.3. Runtime Profiles
4. Framework Integration
4.1. JBossMC Framework Integration
4.2. Apache Felix Integration
4.3. Equinox Integration
5. Developer Documentation
5.1. Service Provider Interface
5.2. Management View
5.3. Writing Test Cases
5.3.1. Simple Test Case
5.3.2. Simple Husky Test Case
5.4. Lifecycle Interceptors
6. Husky Test Framework
6.1. Overview
6.2. Architecture
6.3. Configuration
6.4. Writing Husky Tests
7. Provided Bundles and Services
7.1. Blueprint Container Service
7.2. HttpService
7.3. JAXB Service
7.4. JMX Service
7.5. JNDI Service
7.6. JTA Service
7.7. Microcontainer Service
7.8. ServiceLoader Interceptor
7.9. WebApp Extender
7.10. XML Parser Services
7.11. XML Binding Services
8. Provided Examples
8.1. Build and Run the Examples
8.2. Event Admin Example
8.3. Blueprint Container
8.4. HttpService
8.5. JAXB Service
8.6. JMX Service
8.7. JNDI Service
8.8. JTA Service
8.9. Lifecycle Interceptor
8.10. Microcontainer Service
8.11. Web Application
8.12. ServiceLoader Example
8.13. XML Parser Service
8.14. XML Unmarshaller Service
9. References
10. Getting Support

The Open Services Gateway Initiative (OSGi), specifications define a standardized, component-oriented, computing environment for networked services that is the foundation of an enhanced service-oriented architecture.

The OSGi specification defines two things:

  • A set of services that an OSGi container must implement
  • A contract between the container and your application

Developing on the OSGi platform means first building your application using OSGi APIs, then deploying it in an OSGi container.

The JBoss OSGi Project project has two distinct goals

  1. Provide an integration platform for 3rd party OSGi Frameworks
  2. Provide an OSGi compliant framework implementation based on the JBoss Microcontainer
What does OSGi offer to Java developers?

OSGi modules provide classloader semantics to partially expose code that can then be consumed by other modules. The implementation details of a module, although scoped public by the Java programming language, remain private to the module. On top of that you can install multiple versions of the same code and resolve dependencies by version and other criteria. OSGi also offers advanced security and lifecycle, which I'll explain in more detail further down.

What kind of applications benefit from OSGi?

Any application that is designed in a modular fashion where it is necessary to start, stop, update individual modules with minimal impact on other modules. Modules can define their own transitive dependencies without the need to resolve these dependencies at the container level. The OSGi platform builds an exellent foundation for the next generation JBoss ESB for example.

Should Java EE developers adopt the OSGi programming model?

Probably not. The OSGi runtime may be used internally by Java EE container providers to achieve the desired isolation and configuration flexibility that the container wishes to provide. At the application programming level, the Java EE model will continue to exist in its own right, whereas the OSGi model may provide the more suitable runtime environment for applications that require the modular isolation, security and lifecycle management that OSGi offers.

The functionality of the Framework is divided in the following layers:

OSGi Security Layer

The OSGi Security Layer is an optional layer that underlies the OSGi Service Platform. The layer is based on the Java 2 security architecture. It provides the infrastructure to deploy and manage applications that must run in fine grained controlled environments.

The OSGi Service Platform can authenticate code in the following ways:

  • By location
  • By signer

For example, an Operator can grant the ACME company the right to use networking on their devices. The ACME company can then use networking in every bundle they digitally sign and deploy on the Operator’s device. Also, a specific bundle can be granted permission to only manage the life cycle of bundles that are signed by the ACME company.

OSGi Module Layer

The OSGi Module Layer provides a generic and standardized solution for Java modularization. The Framework defines a unit of modularization, called a bundle. A bundle is comprised of Java classes and other resources, which together can provide functions to end users. Bundles can share Java packages among an exporter bundle and an importer bundle in a well-defined way.

Once a Bundle is started, its functionality is provided and services are exposed to other bundles installed in the OSGi Service Platform. A bundle can carry descriptive information about itself in the manifest file that is contained in its JAR file. Here are a few important Manifest Headers defined by the OSGi Framework:

  • Bundle-Activator - class used to start, stop the bundle
  • Bundle-SymbolicName - identifies the bundle
  • Bundle-Version - specifies the version of the bundle
  • Export-Package - declaration of exported packages
  • Import-Package - declaration of imported packages

The notion of OSGi Version Range describes a range of versions using a mathematical interval notation. For example

    Import-Package: com.acme.foo;version="[1.23, 2)", com.acme.bar;version="[4.0, 5.0)"
    

With the OSGi Class Loading Architecture many bundles can share a single virtual machine (VM). Within this VM, bundles can hide packages and classes from other bundles, as well as share packages with other bundles.

For example, the following import and export definition resolve correctly because the version range in the import definition matches the version in the export definition:

    A: Import-Package: p; version="[1,2)"
    B: Export-Package: p; version=1.5.1
    

Apart from bundle versions, OSGi Attribute Matching is a generic mechanism to allow the importer and exporter to influence the matching process in a declarative way. For example, the following statements will match.

    A: Import-Package: com.acme.foo;company=ACME
    B: Export-Package: com.acme.foo;company=ACME; security=false
    

An exporter can limit the visibility of the classes in a package with the include and exclude directives on the export definition.

    Export-Package: com.acme.foo; include:="Qux*,BarImpl"; exclude:=QuxImpl
    
OSGi Life Cycle Layer

The Life Cycle Layer provides an API to control the security and life cycle operations of bundles.

A bundle can be in one of the following states:

A bundle is activated by calling its Bundle Activator object, if one exists. The BundleActivator interface defines methods that the Framework invokes when it starts and stops the bundle.

A Bundle Context object represents the execution context of a single bundle within the OSGi Service Platform, and acts as a proxy to the underlying Framework. A Bundle Context object is created by the Framework when a bundle is started. The bundle can use this private BundleContext object for the following purposes:

  • Installing new bundles into the OSGi environment
  • Interrogating other bundles installed in the OSGi environment
  • Obtaining a persistent storage area
  • Retrieving service objects of registered services
  • Registering services in the Framework service
  • Subscribing or unsubscribing to Famework events
OSGi Service Layer

The OSGi Service Layer defines a dynamic collaborative model that is highly integrated with the Life Cycle Layer. The service model is a publish, find and bind model. A service is a normal Java object that is registered under one or more Java interfaces with the service registry.

The OSGi Service Compendium specifies a number of services that may be available in an OSGi runtime environment. Although the OSGi Framework specification is useful in itself already, it only defines the OSGi core infrastructure. The services defined in the compendium specification define the scope and functionality of some common services that bundle developers might want to use. Here is a quick summary:

Log Service

The Log Service provides a general purpose message logger for the OSGi Service Platform. It consists of two services, one for logging information and another for retrieving current or previously recorded log information.

Http Service

The Http Service supports two standard techniques for registering servlets and resources to develop communication and user interface solutions for standard technologies such as HTTP, HTML, XML, etc.

Device Access Specification

The Device Access specification supports the coordination of automatic detection and attachment of existing devices on an OSGi Service Platform, facilitates hot-plugging and -unplugging of new devices, and downloads and installs device drivers on demand.

Configuration Admin Service

The Configuration Admin service allows an Operator to set the configuration information of deployed bundles.

Metatype Service

The Metatype Service specification defines interfaces that allow bundle developers to describe attribute types in a computer readable form using so-called metadata.

Preferences Service

The Preferences Service allows storage of data that is specific to a particular user.

User Admin Service

Bundles can use the User Admin Service to authenticate an initiator and represent this authentication as an Authorization object. Bundles that execute actions on behalf of this user can use the Authorization object to verify if that user is authorized.

Wire Admin Service

The Wire Admin Service is an administrative service that is used to control a wiring topology in the OSGi Service Platform. It is intended to be used by user interfaces or management programs that control the wiring of services in an OSGi Service Platform.

IO Connector Service

The IO Connector Service specification adopts the Java 2 Micro Edition (J2ME) javax.microedition.io packages as a basic communications infrastructure.

UPnP Device Service

The UPnP Device Service specifies how OSGi bundles can be developed that interoperate with UPnP (Universal Plug and Play) devices and UPnP control points.

Declarative Services Specification

The Declarative Services specification addresses some of the complications that arise when the OSGi service model is used for larger systems and wider deployments, such as: Startup Time, Memory Footprint, Complexity. The service component model uses a declarative model for publishing, finding and binding to OSGi services.

Event Admin Service

The Event Admin Service provides an inter-bundle communication mechanism. It is based on a event publish and subscribe model, popular in many message based systems.

Deployment Admin Service

The Deployment Admin Service specification, standardizes the access to some of the responsibilities of the management agent: that is, the lifecycle management of interlinked resources on an OSGi Service Platform.

Auto Configuration Specification

The Auto Configuration Specification is to allow the configuration of bundles. These bundles can be embedded in Deployment Packages or bundles that are already present on the OSGi Service Platform.

Application Admin Service

The Application Admin Service is intended to simplify the management of an environment with many different types of applications that are simultaneously available.

DMT Admin Service

The DMT Admin Service specification defines an API for managing a device using concepts from the OMA DM specifications.

Monitor Admin Service

The Monitor Admin Service specification outlines how a bundle can publish Status Variables and how administrative bundles can discover Status Variables as well as read and reset their values.

Foreign Application Access Specification

The Foreign Application Access specification is to enable foreign application models like MIDP, Xlets, Applets, other Java application models to participate in the OSGi service oriented architecture.

Service Tracker Specification

The Service Tracker specification defines a utility class, ServiceTracker, that makes tracking the registration, modification, and unregistration of services much easier.

XML Parser Service Specification

The XML Parser Service specification addresses how the classes defined in JAXP can be used in an OSGi Service Platform.

Position Specification

The Position Specification provides bundle developers with a consistent way of handling geographic positions in OSGi applications.

Measurement and State Specification

The Measurement and State Specification provides a consistent way of handling a diverse range of measurements for bundle developers.

Execution Environment Specification

This Execution Environment Specification defines different execution environments for OSGi Server Platform Servers.

This chapter takes you through the first steps of getting JBoss OSGi and provides the initial pointers to get up and running.

JBoss OSGi is distributed as an IzPack installer archive. The installer is available from the JBoss OSGi download area.

If you selected JBoss OSGi Runtime during installation you should see a runtime folder, which contains the JBoss OSGi Runtime distribution. The JBoss OSGi Runtime is an OSGi container onto which services and applications can be deployed.

The layout of the JBoss OSGi Runtime after installation is similar to what you know from JBossAS.

You can start the Runtime by running bin/run.sh. The supported command line options are:

  • -c (--server-name) - The runtime profile to start. The default is the 'default' profile.
  • -b (--bind-address) - The network address various services can bind to. The default is 'localhost'
$ bin/run.sh
=========================================================================

  JBossOSGi Bootstrap Environment

  OSGI_HOME: /home/tdiesler/jboss-osgi-1.0.0.Beta7/runtime

  JAVA: /usr/java/jdk1.6/bin/java

  JAVA_OPTS: ...

=========================================================================

13:10:35,143 INFO  [OSGiBundleManager] JBossOSGi Core Framework - 1.0.0.Alpha4
...
13:10:36,405 INFO  Start DeploymentScanner: [scandir=.../server/default/deploy,interval=2000ms]
13:10:36,416 INFO  Bundle STARTED: Bundle{system.bundle-0.0.0}
13:10:36,442 INFO  JBossOSGi Runtime booted in 1.297sec
13:10:36,742 INFO  Bundle INSTALLED: Bundle{jboss-osgi-jndi-1.0.2}
13:10:36,967 INFO  Bundle INSTALLED: Bundle{jboss-osgi-common-core-2.2.13.GA}
13:10:36,994 INFO  Bundle INSTALLED: Bundle{jboss-osgi-jmx-1.0.3}
13:10:37,018 INFO  Bundle INSTALLED: Bundle{org.apache.felix.eventadmin-1.0.0}
13:10:37,167 INFO  Bundle STARTED: Bundle{jboss-osgi-common-core-2.2.13.GA}
13:10:37,297 INFO  Bundle STARTED: Bundle{jboss-osgi-jmx-1.0.3}
13:10:37,829 INFO  Bundle STARTED: Bundle{jboss-osgi-jndi-1.0.2}
13:10:38,070 INFO  Bundle STARTED: Bundle{org.apache.felix.eventadmin-1.0.0}
...
13:10:38,334 INFO  JBossOSGi Runtime started in 1.939sec
    

JBoss OSGi comes with a simple Web Console, which is currently based on the Apache Felix Web Console project. The JBoss OSGi Web Console is included in the runtime profiles 'web' or 'all'. After startup you can point your browser to http://localhost:8090/jboss-osgi.

The Web Console can also be used to install, start, stop and uninstall bundles.

Setup the Hudson QA Environment

The JBoss OSGi Hudson QA Environment is an integral part of the JBoss OSGi code base. It is designed for simplicity because we believe that comprehensive QA will only get done if it is dead simple to do so.

Consequently, you only have to execute two simple ant targets to setup the QA environment that was used to QA the JBoss OSGi release that you currently work with.

If in future we should discover a problem with a previous JBoss OSGi release, it will be possible to provide a patch and verify that change using the original QA environment for that release.

With every release we test the matrix of supported target containers and frameworks

Set Hudson Properties

You need to set a few properties, especially these

  • hudson.maven.path
  • hudson.username
  • hudson.password
  • hudson.root
$ cd build/hudson
$ cp ant.properties.example ant.properties
$ vi ant.properties

# Hudson Workspace Root
# hudson.root=/home/username/workspace/hudson/jboss-osgi

# Hudson QA Environment
# hudson.username=username

#hudson.jboss501.zip=file:///home/username/Download/java/jboss/jboss-5.0.1.GA.zip
#hudson.jboss510.zip=file:///home/username/Download/java/jboss/jboss-5.1.0.GA.zip
#hudson.jboss600.zip=file:///home/username/Download/java/jboss/jboss-6.0.0.M2.zip

# JDK settings
java.home.jdk15=/usr/java/jdk1.5.0_22
java.home.jdk16=/usr/java/jdk1.6.0_17

# Maven settings
hudson.maven.name=apache-maven-2.2.1
hudson.maven.path=/usr/java/apache-maven-2.2.1
hudson.maven.profile=$HUDSONDIR/profiles.xml.local.qa

# The JBoss settings
jboss.server.instance=default
jboss.bind.address=127.0.0.1

hudson.host=localhost
hudson.admin.port=8250
hudson.http.port=8280

hudson.mail.recipients=
hudson.mail.admin=yourname@yourdomain.com
hudson.smtp.host=localhost

apache-tomcat=5.5.27
sun-hudson=1.336
    
Run Hudson Setup
$ ant hudson-setup
Buildfile: build.xml

init:
     [echo] V1.0.0.Beta7

init-hudson:
     [echo] 
     [echo] hudson.root = /home/hudson/workspace/hudson/jboss-osgi
     [echo] hudson.home = /home/hudson/workspace/hudson/jboss-osgi/hudson-home
     [echo] 

...

hudson-setup:
     [copy] Copying 2 files to /home/.../hudson/jboss-osgi/apache-tomcat
     ...
     [echo] 
     [echo] *************************************
     [echo] * Hudson setup successfully         *
     [echo] * ant hudson-start                  *
     [echo] *************************************
     [echo] 
    
Run Hudson Start
$ ant hudson-start
Buildfile: build.xml

init:
     [echo] V1.0.0.Beta7

init-hudson:
     [echo] 
     [echo] hudson.root = /home/hudson/workspace/hudson/jboss-osgi
     [echo] hudson.home = /home/hudson/workspace/hudson/jboss-osgi/hudson-home
     [echo] 

hudson-start:
     [echo] 
     [echo] *************************************
     [echo] * Hudson started successfully       *
     [echo] * http://localhost:8280/hudson      *
     [echo] *************************************
     [echo] 

BUILD SUCCESSFUL
    
Run Hudson Stop
$ ant hudson-stop
Buildfile: build.xml

init:
     [echo] V1.0.0.Beta7

init-hudson:
     [echo] 
     [echo] hudson.root = /home/hudson/workspace/hudson/jboss-osgi
     [echo] hudson.home = /home/hudson/workspace/hudson/jboss-osgi/hudson-home
     [echo] 

hudson-stop:
     [echo] 
     [echo] *************************************
     [echo] * Hudson stopped successfully       *
     [echo] * ant hudson-start                  *
     [echo] *************************************
     [echo] 

BUILD SUCCESSFUL
    

The JBoss OSGi Runtime is an OSGi container onto which components, services and applications can be deployed.

Preconfigured profiles, contain OSGi bundles that logically work together. A profile can be bootstrapped either as a standalone server or embedded in some other environment. With a startup time of less than 600ms, the runtime can be easily be bootstrapped from within plain JUnit4 test cases.

The JBoss OSGi Runtime has an integration layer for the underlying OSGi frameworks. It comes with a choice of Apache Felix, Eclipse Equinox or JBoss Microcontainer.

Through local and remote management capabilities the JBoss OSGi Runtime can be provisioned with new or updated bundles. Similar to JBossAS it supports hot-deployment by dropping bundles into the 'deploy' folder. Management of the runtime is provided through a Web Console

Integration of the JBoss Microcontainer as an OSGi service allows you to write your applications in a POJO programming model without much "pollution" of OSGi specific API - the MC will do the wiring for you. JBoss OSGi also comes with an implementation of Blueprint Service (RFC-124), which standardizes this idea and takes it further.

Great care has been taken about testability of deployed components and services. The Husky Test Framework allows you to write plain JUnit tests that do not have a requirement on a specific test runner nor need to extend any specific test base class. Access to the Runtime has been abstracted sufficiently that you can run the same test case against an embedded (bootstrapped from within the test case) as well as a remote instance of the Runtime. You can run your OSGi tests from Maven, Ant, Eclipse or any other test runner that supports JUnit4.

JBoss OSGi Runtime can be installed as a JBossAS service with abstractions of the available OSGi services. The JBoss OSGi testsuite in fact runs the same set of tests against the embedded, standalone and AS integrated instance of the Runtime

The current JBoss OSGi Runtime feature set includes

  • Embedded and Standalone usage - The runtime can be bootstrapped as standalone container with a startup time of less than 2 sec in its default configuration or embedded in some other container environment.
  • Various Runtime Profiles - It comes with the preconfigured profiles 'Minimal', 'Default', 'Web', 'All'. Setting up a new profile is a mater of creating a new directory and putting some bundles in it.
  • Hot Deployement - Similar to JBossAS there is a deployment scanner that scans the 'deploy' folder for new or removed bundles.
  • Multiple OSGi Frameworks - The Installer can setup the JBoss OSGi Runtime using Felix, Equinox or JBossMC.
  • Local and Remote JMX Support - There is local as well as remote JSR160 support for JMX.
  • JNDI Support - Components can access the JNDI InitialContext as a service from the registry.
  • JTA Support - Components can interact with the JTA TransactionManager and UserTransaction service.
  • SAX/DOM Parser Support - The Runtime comes with an implementation of an XMLParserActivator which provides access to a SAXParserFactory and DocumentBuilderFactory.
  • JAXB Support - There is a bundle that provides JAXB support.
  • HttpService and WebApp Support - HttpService and WebApp support is provided by Pax Web.
  • ConfigAdmin Support - ConfigAdmin support is provided by the Apache Felix Configuration Admin Service.
  • EventAdmin Support - EventAdmin support is provided by the Apache Felix Event Admin Service.
  • Provisioning - Bundle provisioning can be done through the JMX based Runtime Managment Interface.
  • Logging System - The logging bridge writes OSGi LogEntries to the configured logging framework (e.g. Log4J).
  • Microcontainer Support - The Microcontainer service allows bundles to contain a *-beans.xml descriptor, which can be used for component wiring and injection of base services. It also comes with a set of deployers - so instead of simply installing a bundle to the underlying OSGi framework it passes to the chain of deployers which each deal with a specific aspect of bundle deployment.
  • Blueprint Container Support - The Blueprint Container service allows bundles to contain standard blueprint descriptors, which can be used for component wiring and injection of blueprint components. The idea is to use a plain POJO programming model and let Blueprint do the wiring for you. There should be no need for OSGi API to "pollute" your application logic.

A runtime profile is a collection bundles that logically work together. The OSGi runtime configuration contains the list of bundles that are installed/started automatically. You can start create you own profile by setting up a new directory with your specific set of bundles.

A runtime profile can be started using the -c comand line option.

$ bin/run.sh -c minimal
=========================================================================

  JBossOSGi Bootstrap Environment

  OSGI_HOME: /home/tdiesler/jboss-osgi-1.0.0.Beta7/runtime

  JAVA: /usr/java/jdk1.6/bin/java

  JAVA_OPTS: -Dprogram.name=run.sh ...

=========================================================================

12:10:48,713 INFO  JBossOSGi Core Framework - 1.0.0.Alpha4
12:10:49,089 INFO  Bundle INSTALLED: Bundle{osgi.cmpn:4.2.0.200908310645}
12:10:49,188 INFO  Bundle INSTALLED: Bundle{org.apache.felix.log:1.0.0}
12:10:49,282 INFO  Bundle INSTALLED: Bundle{jboss-osgi-common:1.0.2}
12:10:49,313 INFO  Bundle INSTALLED: Bundle{jboss-osgi-hotdeploy:1.0.2}
12:10:50,047 INFO  Bundle STARTED: Bundle{jboss-osgi-hotdeploy:1.0.2}
12:10:50,050 INFO  Bundle STARTED: Bundle{system.bundle:0.0.0}
12:10:50,076 INFO  JBossOSGi Runtime booted in 1.357sec
    
Minimal Profile

The 'minimal' profile provides logging and hot-deployment.

The following bundles are installed:

  • org.osgi.compendium.jar - OSGi compendium API
  • jboss-osgi-common.jar - JBoss OSGi common services
  • org.apache.felix.log.jar - Apache LogService
  • jboss-osgi-hotdeploy.jar - JBoss OSGi hot deployment service
Default Profile

The 'default' profile extends the 'minimal' profile by JNDI and JMX

These additional bundles are installed:

  • org.apache.aries.jmx.jar - Apache Aries JMX services
  • org.apache.felix.eventadmin.jar - Apache Event Admin service
  • jboss-osgi-common-core.jar - JBoss Common Core functionality
  • jboss-osgi-jmx.jar - JBoss OSGi JMX service
  • jboss-osgi-jndi.jar - JBoss OSGi JNDI service
Web Profile

The 'web' profile extends the 'default' profile by HttpService and ConfigAdmin

These additional bundles are installed:

  • org.apache.felix.configadmin.jar - Apache Config Admin service
  • pax-web-jetty-bundle.jar - Pax Web HttpService
  • pax-web-extender-war.jar - Pax Web WebApp Extender
  • jboss-osgi-webconsole.jar - JBoss OSGi Web Console
All Profile

The 'all' profile extends the 'web' profile by SAX/DOM, JAXB, JBossXB and Microcontainer

These additional bundles are installed:

  • jboss-osgi-apache-xerces.jar - Apache Xerces support
  • jboss-osgi-jaxb.jar - JAXB support
  • jboss-osgi-jta.jar - JTA support
  • jboss-osgi-xml-binding.jar - XML Binding (JBossXB) support
  • jboss-osgi-microcontainer.jar - Microcontainer support
  • jboss-osgi-blueprint.jar - Blueprint Container support

Starting from 1.0.0.Beta4 JBoss OSGi provides integration for our native Microcontainer based OSGi Framework. When deployed in JBossAS this Framework will eventually allow us to integrate with components from other programming models. (i.e. OSGi services can access MC beans, EJB3 can access OSGi services and vice versa)

JBossMC integration can be configured through an XML beans configuration in the JBoss OSGi Runtime.



cat server/default/conf/jboss-osgi-bootstrap.xml 

<deployment xmlns="urn:jboss:bean-deployer:2.0">

  <!-- The OSGiFramework -->
  <bean name="OSGiBundleManager" class="org.jboss.osgi.framework.bundle.OSGiBundleManager">
  <property name="properties">
    ...
  </property>
  </bean>
...
</deployment>
    

In the JBossAS integration we also use JBoss Microcontainer beans configuration.

JBoss OSGi provides integration for the Apache Felix OSGi Framework and some of its core services

The Apache Felix integration can be configured through properties in the JBoss OSGi Runtime.

cat conf/jboss-osgi-framework.properties 

# Properties to configure the Framework
org.osgi.framework.storage=${osgi.server.home}/data/osgi-store
org.osgi.framework.storage.clean=onFirstInit

# Hot Deployement
org.jboss.osgi.hotdeploy.scandir=${osgi.server.home}/bundles

...

# Bundles that need to be installed with the Framework automatically 
org.jboss.osgi.spi.framework.autoInstall=\
  file://${osgi.home}/server/minimal/bundles/org.osgi.compendium.jar

# Bundles that need to be started automatically 
org.jboss.osgi.spi.framework.autoStart=\
   file://${osgi.home}/server/minimal/bundles/org.apache.felix.log.jar \
   file://${osgi.home}/server/minimal/bundles/jboss-osgi-common.jar \
   file://${osgi.home}/server/minimal/bundles/jboss-osgi-hotdeploy.jar
    

In the JBossAS integration we use JBoss Microcontainer beans configuration.



cat server/default/deployers/osgi.deployer/META-INF/osgi-deployers-jboss-beans.xml 

<deployment xmlns="urn:jboss:bean-deployer:2.0">

  <!-- The OSGiFramework -->
  <bean name="jboss.osgi:service=Framework" class="org.jboss.osgi.felix.FelixIntegration">
    <property name="properties">
       ...
    </property>
    <property name="autoStart">
     <list elementClass="java.net.URL">
      ...
     </list>
    </property>
  </bean>
  ...
</deployment>
    

The following is a description of the configuration properties for the Apache Felix integration.

KeyValueDescription
org.osgi.framework.storage .../osgi-store OSGi Framework storage area
org.osgi.framework.storage.clean onFirstInit Clean the storage area on first init
org.osgi.service.http.port 8090 The default Http Service port
felix.cm.dir .../osgi-configadmin Config Admin Service storage area
org.osgi.framework.system.packages.extra javax.management, javax.xml... Packages provided by the OSGi System ClassLoader
org.jboss.osgi.deferred.start true Bundles can be deployed in any order

JBoss OSGi also provides basic integration for the Eclipse Equinox OSGi Framework.

Equinox integration can be configured through properties in the JBoss OSGi Runtime.



cat conf/jboss-osgi-framework.properties 

# Properties to configure the Framework
org.osgi.framework.storage=${osgi.server.home}/data/osgi-store
org.osgi.framework.storage.clean=onFirstInit

# Hot Deployement
org.jboss.osgi.hotdeploy.scandir=${osgi.server.home}/bundles

...

# Bundles that need to be installed with the Framework automatically 
org.jboss.osgi.spi.framework.autoInstall=\
 file://${osgi.home}/server/minimal/deploy/org.eclipse.osgi.services.jar \
 file://${osgi.home}/server/minimal/deploy/org.eclipse.osgi.util.jar

# Bundles that need to be started automatically 
org.jboss.osgi.spi.framework.autoStart=\
   file://${osgi.home}/server/minimal/bundles/org.apache.felix.log.jar \
   file://${osgi.home}/server/minimal/bundles/jboss-osgi-common.jar \
   file://${osgi.home}/server/minimal/bundles/jboss-osgi-hotdeploy.jar
    

In the JBossAS integration we use JBoss Microcontainer beans configuration.



<deployment xmlns="urn:jboss:bean-deployer:2.0">

  <!-- The OSGiFramework -->
  <bean name="jboss.osgi:service=Framework" class="org.jboss.osgi.equinox.EquinoxIntegration">
    <property name="properties">
       ...
    </property>
    <property name="autoStart">
     <list elementClass="java.net.URL">
      ...
     </list>
    </property>
  </bean>
  ...
</deployment>
    

The JBoss OSGi Service Provider Interface (SPI) is the integration point for:

The latest version of the JBoss OSGi SPI.

Bootstrapping JBoss OSGi

The OSGiBootstrap provides an OSGiFramework through a OSGiBootstrapProvider.

A OSGiBootstrapProvider is discovered in two stages

  1. Read the bootstrap provider class name from a system property
  2. Read the bootstrap provider class name from a resource file

In both cases the key is the fully qalified name of the org.jboss.osgi.spi.framework.OSGiBootstrapProvider interface.

The following code shows how to get the default OSGiFramework from the OSGiBootstrapProvider.



    OSGiBootstrapProvider bootProvider = OSGiBootstrap.getBootstrapProvider();
    OSGiFramework framework = bootProvider.getFramework();
    Bundle bundle = framework.getSystemBundle();
    

The OSGiBootstrapProvider can also be configured explicitly. The OSGiFramework is a named object from the configuration.



    OSGiBootstrapProvider bootProvider = OSGiBootstrap.getBootstrapProvider();
    bootProvider.configure(configURL);
    
    OSGiFramework framework = bootProvider.getFramework();
    Bundle bundle = framework.getSystemBundle();
    

The JBoss OSGi SPI comes with a default bootstrap provider:

OSGiBootstrapProvider implementations that read their configurtation from some other source are possible, but currently not part of the JBoss OSGi SPI.

Using the SPI from within JBossAS deployed components

If you need access to the OSGi Framework from within an JBossAS deployed component (e.g. servlet, ejb, mbean) you would not bootstrap JBoss OSGi through the SPI. Instead, you would inject the already bootstrapped OSGi Framework instance into your component.

JBoss OSGi provides standard org.osgi.jmx management. Additional to that we provide an MBeanServer service and a few other extensions through the org.jboss.osgi.jmx API

Accessing the Management Objects

If you work with the JBoss OSGi runtime abstraction you get access to these managed objects through OSGiRuntime.

If you install JBoss OSGi in an already existing JBossAS instance you also get access to the Managed Objects through the JBoss provided JMX Console (http://localhost:8080/jmx-console).

Note

The JMX Console is not part of the JBoss OSGi Runtime.

JBoss OSGi comes with JUnit test support as part of the SPI provided org.jboss.osgi.testing package. There are two distinct test scenarios that we support:

  • Embedded OSGi Framework
  • Remote OSGi Framework

The remote scenario can actually be separated again in:

  • Standalone JBoss OSGi Runtime
  • JBoss OSGi Runtime running in JBossAS

A test case that takes advantage of the OSGi runtime abstration that transparently handles the various remote scenarios.

The test case bootstraps the OSGi Runtime, installes/starts the bundle, asserts the bundle state and finally shuts down the runtime again. Please note, this is a plain JUnit4 test case that transparently handles embedded/remote nature of the runtime.



public class SimpleTestCase extends OSGiTest
{
   @Test
   public void testSimpleBundle() throws Exception
   {
      // Get the default runtime
      OSGiRuntime runtime = getDefaultRuntime();
      
      try
      {
         // Install the bundle
         OSGiBundle bundle = runtime.installBundle("example-simple.jar");
         // Start the bundle
         bundle.start();
         assertBundleState(Bundle.ACTIVE, bundle.getState());
         // Uninstall the bundle
         bundle.uninstall();
      }
      finally
      {
         // Shutdown the runtime 
         runtime.shutdown();
      }
   }
}
      

To run the test in embedded mode (which is the default) you would execute your test runner like this



      [tdiesler@tddell example]$ mvn -Dtest=SimpleTestCase test
      ...
      Running org.jboss.test.osgi.example.simple.SimpleTestCase
      Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.361 sec
      

To run the test against the remote JBoss OSGi Runtime you would execute your test runner like this



      [tdiesler@tddell example]$ mvn -Dtarget.container=runtime -Dtest=SimpleTestCase test
      ...
      Running org.jboss.test.osgi.example.simple.SimpleTestCase
      Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.303 sec
     

In the runtime console you should see



      12:44:30,960 INFO  [jboss-osgi-common] Installed: example-simple [8]
      12:44:31,081 INFO  [example-simple] Start: example-simple [8]
      12:44:31,089 INFO  [example-simple] Stop: example-simple [8]
      12:44:31,095 INFO  [jboss-osgi-common] Uninstalled: example-simple [8]
     

Due to classloading restrictions it is not possible to interact with the services that get registered in the OSGi Framework directly. Instead, there must be some means for the bundle under test to communicate with the test case that lives outside the Framework. The approach of OSGi testing based on remote log messages is covered in Non intrusive OSGi Bundle Testing.

The next section explains how to write a plain JUnit test that is then executed within the OSGi Runtime.

The test case does everthing identical to SimpleTestCase, but only executes the code in the test method when Husky injected the BundleContext.



public class SimpleHuskyTestCase
{
   @ProvideContext
   public BundleContext context;
   ...
   @Test
   public void testSimpleBundle() throws Exception
   {
      // Tell Husky to run this test method within the OSGi Runtime
      if (context == null)
         BridgeFactory.getBridge().run();
      
      // Stop here if the context is not injected
      assumeNotNull(context);
      
      // Get the SimpleService reference
      ServiceReference sref = context.getServiceReference(SimpleService.class.getName());
      assertNotNull("SimpleService Not Null", sref);
      
      // Access the SimpleService 
      SimpleService service = (SimpleService)context.getService(sref);
      assertEquals("hello", service.echo("hello"));
   }
}
      

Running this test is also no different from SimpleTestCase.

In the runtime console you should see



      13:29:15,924 INFO  [jboss-osgi-common] Installed: example-simple-husky [16]
      13:29:15,972 INFO  [example-simple-husky] Start: example-simple-husky [16]
      13:29:15,981 INFO  [jboss-osgi-husky] Test-Package [org.jboss.test.osgi.example.simple] in bundle: example-simple-husky [16]
      13:29:16,160 INFO  [example-simple-husky] echo: hello
      13:29:16,191 INFO  [example-simple-husky] Stop: example-simple-husky [16]
      13:29:16,196 INFO  [jboss-osgi-common] Uninstalled: example-simple-husky [16]
     

To learn more about the magic of the BridgeFactory have a look at Husky Test Framework which comes next.

A common pattern in OSGi is that a bundle contains some piece of meta data that gets processed by some other infrastructure bundle that is installed in the OSGi Framework. In such cases the well known Extender Pattern is often being used. JBoss OSGi offeres a differnet approach to address this problem which is covered by the Extender Pattern vs. Lifecycle Interceptor post in the JBoss OSGi Diary.

Extending an OSGi Bundle
  1. Extender registers itself as BundleListener
  2. Bundle gets installed/started
  3. Framework fires a BundleEvent
  4. Extender picks up the BundleEvent (e.g. STARTING)
  5. Extender reads metadata from the Bundle and does its work

There is no extender specific API. It is a pattern rather than a piece of functionality provided by the Framework. Typical examples of extenders are the Blueprint or Web Application Extender.

Client code that installs, starts and uses the registered endpoint could look like this.



// Install and start the Web Application bundle
Bundle bundle = context.installBundle("mywebapp.war");
bundle.start();
// Access the Web Application
String response = getHttpResponse("http://localhost:8090/mywebapp/foo");
assertEquals("ok", response);
    

This seemingly trivial code snippet has a number of issues that are probably worth looking into in more detail

  • The WAR might have missing or invalid web metadata (i.e. an invalid WEB-INF/web.xml descriptor)
  • The WAR Extender might not be present in the system
  • There might be multiple WAR Extenders present in the system
  • Code assumes that the endpoint is available on return of bundle.start()

Most Blueprint or WebApp bundles are not useful if their Blueprint/Web metadata is not processed. Even if they are processed but in the "wrong" order a user might see unexpected results (i.e. the webapp processes the first request before the underlying Blueprint app is wired together).

As a consequence the extender pattern is useful in some cases but not all. It is mainly useful if a bundle can optionally be extended in the true sense of the word.

Intercepting the Bundle Lifecycle

If the use case requires the notion of "interceptor" the extender pattern is less useful. The use case might be such that you would want to intercept the bundle lifecycle at various phases to do mandatory metadata processing.

An interceptor could be used for annotation processing, byte code weaving, and other non-optional/optional metadata processing steps. Typically interceptors have a relative order, can communicate with each other, veto progress, etc.

Lets look at how multiple interceptors can be used to create Web metadata and publish endpoints on the HttpService based on that metadata.

Here is how it works

  1. The Wep Application processor registers two LifecycleInterceptors with the LifecycleInterceptorService
  2. The Parser interceptor declares no required input and WebApp metadata as produced output
  3. The Publisher interceptor declares WebApp metadata as required input
  4. The LifecycleInterceptorService reorders all registered interceptors according to their input/output requirements and relative order
  5. The WAR Bundle gets installed and started
  6. The Framework calls the LifecycleInterceptorService prior to the actual state change
  7. The LifecycleInterceptorService calls each interceptor in the chain
  8. The Parser interceptor processes WEB-INF/web.xml in the invoke(int state, InvocationContext context) method and attaches WebApp metadata to the InvocationContext
  9. The Publisher interceptor is only called when the InvocationContext has WebApp metadata attached. If so, it publishes the endpoint from the WebApp metadata
  10. If no interceptor throws an Exception the Framework changes the Bundle state and fires the BundleEvent.

Client code is identical to above.



// Install and start the Web Application bundle
Bundle bundle = context.installBundle("mywebapp.war");
bundle.start();
// Access the Web Application
String response = getHttpResponse("http://localhost:8090/mywebapp/foo");
assertEquals("ok", response);
    

The behaviour of that code however, is not only different but also provides a more natural user experience.

  • Bundle.start() fails if WEB-INF/web.xml is invalid
  • An interceptor could fail if web.xml is not present
  • The Publisher interceptor could fail if the HttpService is not present
  • Multiple Parser interceptors would work mutually exclusiv on the presents of attached WebApp metadata
  • The endpoint is guaranteed to be available when Bundle.start() returns

The general idea is that each interceptor takes care of a particular aspect of processing during state changes. In the example above WebApp metadata might get provided by an interceptor that scans annotations or by another one that generates the metadata in memory. The Publisher interceptor would not know nor care who attached the WebApp metadata object, its task is to consume the WebApp metadata and publish endpoints from it.

For details on howto provide and register liefecycle interceptors have a look at the Lifecycle Interceptor Example.

JBoss OSGi Husky is a OSGi Test Framework that allows you to run plain JUnit4 test cases from within an OSGi Framework. That the test is actually executed in the the OSGi Framework is transparent to your test case. There is no requirement to extend a specific base class nor do you need a special test runner. Your OSGi tests execute along side with all your other (non OSGi specific) test cases in Maven, Ant, or Eclipse.

Some time ago I was looking for ways to test bundles that are deployed to a remote instance of the JBoss OSGi Runtime. I wanted the solution to also work with an OSGi Framework that is bootstrapped from within a JUnit test case.

The basic problem is of course that you cannot access the artefacts that you deploy in a bundle directly from your test case, because they are loaded from different classloaders.

Former releases of JBoss OSGi used an approach which is documented in Non intrusive OSGi Bundle Testing and is still available. Although the remote logging approach worked for simple scenarios, it does not allow for fine grained interaction with the OSGi Framework (i.e. access to the registry). An additional problem was the asynchronous nature of LogEntry delivery.

For this release however, I revisited the problem and added a few more requirements.

  • Test cases SHOULD be plain JUnit4 POJOs
  • There SHOULD be no requirement to extend a specific test base class
  • There MUST be no requirement on a specific test runner (i.e. MUST run with Maven)
  • There SHOULD be a minimum test framework leakage into the test case
  • The test framework MUST support embedded and remote OSGi runtimes with no change required to the test
  • The same test case MUST be executable from outside as well as from within the OSGi Framework
  • There SHOULD be a pluggable communication layer from the test runner to the OSGi Framework
  • The test framework MUST NOT depend on OSGi Framework specific features
  • There MUST be no automated creation of test bundles required by the test framework

The next section explains the solution that now comes as JBoss OSGi Husky

JBoss OSGi Husky has client side interceptor that fields the test request to an embedded/remote OSGi Framework where the test case is then actually executed.

Here is how it works

  1. A Bridge intercepts a test and determines the FQN of the test case and the test method from the call stack. It then delegates the execution to the same (or another) test in and isolated test environment. An isolated test environment is one that does not have the same class loading space as the test itself.
  2. A Bridge is associated with an Invoker. Invokers may be arbitarily complex. Local 'in proccess' invokers are possible just as well as remote invokers.
  3. The Invoker sends the Request to a Connector in the isolated test environment.
  4. A Connector has associated PackageListeners that are responsible for processing test cases for their respective test packages.
  5. A PackageListeners delegates the Request to a test Runner, typicaly this would be a JUnitRunner.
  6. The Runner injects the Context into the test case and returns a Response, which the Connector returns to the Invoker.
  7. The Bridge finally translates potential Failures that may be contained in the Result, to test failures on the client side.

The JBoss OSGi jboss-osgi-husky.jar bundle registers the Connectors. The JMXConnector is always registered. The SocketConnector is registered when the appropriate configuration options are set. It then registers the HuskyExtender, which is a BundleListener that inspects every incomming bundle for the Test-Package manifest header. The Extender creates a PackageListener for every package in the 'Test-Package' manifest header and registers them with the available Connectors.

In the target OSGi Framework, which is the one that has the jboss-osgi-husky.jar bundle installed, you set these properties

KeyValueDescription
org.jboss.osgi.husky.runtime.connector.host localhost The Husky socket connector host poperty
org.jboss.osgi.husky.runtime.connector.port 5401 The Husky socket connector port poperty

Both properties must be set for the SocketConnector to be available.

On the client side, you must configure the Invoker you want to use.

KeyValueDescription
org.jboss.osgi.husky.Invoker org.jboss.osgi.husky.internal.OSGiInvoker The client side Husky Invoker

This particular invoker will also look for the 'org.jboss.osgi.husky.runtime.connector.host' and 'org.jboss.osgi.husky.runtime.connector.port' properties and if available will use a socket invocation.

In a typical Husky test you have

For OSGi, the descriminator would be the BundleContext that gets injected by the 'in container' test Runner

The interceptor would be a call to one of the Bridge.run() variants.



public class SimpleHuskyTestCase
{
   @ProvideContext
   public BundleContext context;
   ...
   @Test
   public void testSimpleBundle() throws Exception
   {
      // Tell Husky to run this test method within the OSGi Runtime
      if (context == null)
         BridgeFactory.getBridge().run();
      
      // Stop here if the context is not injected
      assumeNotNull(context);
      
      // Get the SimpleService reference
      ServiceReference sref = context.getServiceReference(SimpleService.class.getName());
      assertNotNull("SimpleService Not Null", sref);
      
      // Access the SimpleService 
      SimpleService service = (SimpleService)context.getService(sref);
      assertEquals("hello", service.echo("hello"));
   }
}
    

The bundle that contains the test case must have the Test-Package manifest header configured. Here is the aQute Bnd Tool configuration for doing so.

Bundle-SymbolicName: example-simple-husky

Bundle-Activator: org.jboss.test.osgi.example.simple.bundle.SimpleActivator

Private-Package: org.jboss.test.osgi.example.simple.bundle

# Export the package that contains tthe test case
Export-Package: org.jboss.test.osgi.example.simple

# Tell Husky that there are test cases in this package
Test-Package: org.jboss.test.osgi.example.simple
    

The JBoss OSGi jboss-osgi-blueprint.jar bundle provides together with org.apache.aries.blueprint.jar access to the Blueprint extender service.

The Blueprint Container service allows bundles to contain standard blueprint descriptors, which can be used for component wiring and injection of blueprint components. The idea is to use a plain POJO programming model and let Blueprint do the wiring for you. There should be no need for OSGi API to "pollute" your application logic.

The Blueprint API is divided into the Blueprint Container and Blueprint Reflection packages.

The pax-web-jetty-bundle.jar bundle from the OPS4J Pax Web project provides access to the HttpService.

An example of how a bundle uses the HttpService to register servlet and resources is given in HttpService Example.

The HttpService is configured with these properties.

KeyValueDescription
org.osgi.service.http.port 8090 The property that sets the port the HttpService binds to

The service is registered with the Framework under the name

The JBoss OSGi jboss-osgi-jmx.jar bundle activator discovers and registers the MBeanServer with the framework. By default, it also sets up a remote connector at:

service:jmx:rmi://localhost:1198/jndi/rmi://localhost:1090/osgi-jmx-connector

The JMX Service is configured with these properties.

KeyValueDescription
org.jboss.osgi.jmx.host localhost The property that sets the host that the JMXConnector binds to
org.jboss.osgi.jmx.rmi.port 1198 The property that sets the port that the JMXConnector binds to
org.jboss.osgi.jmx.rmi.registry.port 1090 The property that sets the port that the RMI Registry binds to

Here is the complete list of services that this bundle provides

The JBoss OSGi jboss-osgi-jndi.jar bundle activator creates and registers the InitialContext with the framework.

The JNDI Service is configured with these properties.

KeyValueDescription
org.jboss.osgi.jndi.host localhost The property that sets the naming server host
org.jboss.osgi.jndi.rmi.port 1098 The property that sets the naming server RMI port
org.jboss.osgi.jndi.port 1099 The property that sets the naming server port

Here is the complete list of services that this bundle provides

The JBoss OSGi jboss-osgi-jta.jar bundle registers two services with framework.

Among others the JTA Service can be configured with these properties.

KeyValueDescription
com.arjuna.ats.arjuna.objectstore.objectStoreDir ${server.data.dir}/tx-object-store The property that sets the transaction object store directory

For details please refer to the JBossTM documentation.

The JBoss OSGi Microcontainer Service gives access to the JBoss Microcontainer Kernel. The service is registered with the Framework under the name.

Here is an example of how an OSGi component can access an arbitrary MC bean.



public class SomeService
{
   public String callSomeBean(String msg)
   {
      ServiceReference sref = context.getServiceReference(MicrocontainerService.class.getName());
      MicrocontainerService mcService = (MicrocontainerService)context.getService(sref);
      SomeBean bean = (SomeBean)mcService.getRegisteredBean("SomeBean");
      return bean.echo(msg);
   }
}
    

The ServiceLoader, deployed as jboss-osgi-serviceloader.jar bundle, is a Lifecycle Interceptor that automatically registers services declared in META-INF/services.

For more information, please hava a look at ServiceLoader and how it relates to OSGi.

The pax-web-extender-war.jar bundle from the OPS4J Pax Web project provides WAR processing functionality.

Deploying a WAR onto JBoss OSGi

You should have a war file compliant with Servlet specs. Additionally, the war file must have the necessary OSGi manifest headers.

  • Bundle-ManifestVersion: 2 - This header defines that the bundle follows the rules of R4 specification.
  • Bundle-SymbolicName - This header specifies a unique, non-localizable name for this bundle.

There are also a number of other OSGi manifest headers that are processed by the WAR Extender. Please have a look at OSGify your WAR for details.

An example of how a bundle uses the WAR Extender to register servlet and resources is given in WebApp Example.

JBoss OSGi comes with a number of examples that demonstrate supported functionality and show best practices. All examples are part of the binary distribution and tightly integrated in our Maven Build Process and Hudson QA Environment.

The examples can be either run against an embedded OSGi framework or against the remote OSGi Runtime. Here is how you build and run the against the embedded framework.

[tdiesler@tddell example]$ mvn test
 
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.jboss.test.osgi.example.webapp.WebAppInterceptorTestCase
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 14.417 sec
...

Tests run: 25, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1 minute 31 seconds
[INFO] Finished at: Tue Dec 08 11:15:08 CET 2009
[INFO] Final Memory: 35M/139M
[INFO] ------------------------------------------------------------------------
    

To run the examples against a remote OSGi Runtime, you need to provide the target container that the runtime should connect to. This can be done with the target.container system property.

    mvn -Dtarget.container=runtime test
    

Suported target container values are:

  • runtime
  • jboss501
  • jboss510
  • jboss600
  • jboss601

To run the examples against a different OSGi Framework, you need to define the framework system property.

    mvn -Dframework=felix test
    

Suported framework values are:

  • jbossmc
  • equinox
  • felix

The example-event.jar bundle uses the EventAdmin service to send/receive events.



public void testEventHandler() throws Exception
{
  TestEventHandler eventHandler = new TestEventHandler();
  
  // Register the EventHandler
  Dictionary param = new Hashtable();
  param.put(EventConstants.EVENT_TOPIC, new String[] { TOPIC });
  context.registerService(EventHandler.class.getName(), eventHandler, param);
  // Send event through the the EventAdmin
  ServiceReference sref = context.getServiceReference(EventAdmin.class.getName());
  EventAdmin eventAdmin = (EventAdmin)context.getService(sref);
  eventAdmin.sendEvent(new Event(TOPIC, null));
  
  // Verify received event
  assertEquals("Event received", 1, eventHandler.received.size());
  assertEquals(TOPIC, eventHandler.received.get(0).getTopic());
}
    

The example-blueprint.jar bundle contains a number of components that are wired together and registerd as OSGi service through the Blueprint Container Service.

The example uses this simple blueprint descriptor



<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" ...>
  
  <bean id="beanA" class="org.jboss.test.osgi.example.blueprint.bundle.BeanA">
    <property name="mbeanServer" ref="mbeanService"/>
  </bean>
  
  <service id="serviceA" ref="beanA" interface="org.jboss.test.osgi.example.blueprint.bundle.ServiceA">
  </service>
  
  <service id="serviceB" interface="org.jboss.test.osgi.example.blueprint.bundle.ServiceB">
    <bean class="org.jboss.test.osgi.example.blueprint.bundle.BeanB">
       <property name="beanA" ref="beanA"/>
    </bean>
  </service>
  
  <reference id="mbeanService" interface="javax.management.MBeanServer"/>

</blueprint>
    

The Blueprint Container registers two services ServiceA and ServiceB. ServiceA is backed up by BeanA, ServiceB is backed up by the anonymous BeanB. BeanA is injected into BeanB and the MBeanServer gets injected into BeanA. Both beans are plain POJOs. There is no BundleActivator neccessary to register the services.

The example test verifies the correct wiring like this



@Test
public void testServiceA() throws Exception
{
  ServiceReference sref = context.getServiceReference(ServiceA.class.getName());
  assertNotNull("ServiceA not null", sref);
  
  ServiceA service = (ServiceA)context.getService(sref);
  MBeanServer mbeanServer = service.getMbeanServer();
  assertNotNull("MBeanServer not null", mbeanServer);
}
    


@Test
public void testServiceB() throws Exception
{
  ServiceReference sref = context.getServiceReference(ServiceB.class.getName());
  assertNotNull("ServiceB not null", sref);
  
  ServiceB service = (ServiceB)context.getService(sref);
  BeanA beanA = service.getBeanA();
  assertNotNull("BeanA not null", beanA);
}
    

The example-http.jar bundle contains a Service that registeres a servlet and a resource with the HttpService.



ServiceTracker tracker = new ServiceTracker(context, HttpService.class.getName(), null);
tracker.open();
HttpService httpService = (HttpService)tracker.getService();
if (httpService == null)
   throw new IllegalStateException("HttpService not registered");
Properties initParams = new Properties();
initParams.setProperty("initProp", "SomeValue");
httpService.registerServlet("/servlet", new EndpointServlet(context), initParams, null);
httpService.registerResources("/file", "/res", null);
    

The test then verifies that the registered servlet context and the registered resource can be accessed.

The example-jmx.jar bundle tracks the MBeanServer service and registers a pojo with JMX. It then verifies the JMX access.



public class FooServiceActivator implements BundleActivator
{
   public void start(BundleContext context)
   {
      ServiceTracker tracker = new ServiceTracker(context, MBeanServer.class.getName(), null)
      {
         public Object addingService(ServiceReference reference)
         {
            MBeanServer mbeanServer = (MBeanServer)super.addingService(reference);
            registerMBean(mbeanServer);
            return mbeanServer;
         }
         @Override
         public void removedService(ServiceReference reference, Object service)
         {
            unregisterMBean((MBeanServer)service);
            super.removedService(reference, service);
         }
      };
      tracker.open();
   }
   public void stop(BundleContext context)
   {
      ServiceReference sref = context.getServiceReference(MBeanServer.class.getName());
      if (sref != null)
      {
         MBeanServer mbeanServer = (MBeanServer)context.getService(sref);
         unregisterMBean(mbeanServer);
      }
   }
   ...
}
    


public void testMBeanAccess() throws Exception
{
  FooMBean foo = (FooMBean)MBeanProxy.get(FooMBean.class, MBEAN_NAME, runtime.getMBeanServer());
  assertEquals("hello", foo.echo("hello"));
}
    

The example-jta.jar bundle gets the javax.transaction.UserTransaction service and registers a transactional user object (i.e. one that implements Synchronization) with the javax.transaction.TransactionManager service. It then verifies that modifications on the user object are transactional.



Transactional txObj = new Transactional();
ServiceReference userTxRef = context.getServiceReference(UserTransaction.class.getName());
assertNotNull("UserTransaction service not null", userTxRef);
UserTransaction userTx = (UserTransaction)context.getService(userTxRef);
assertNotNull("UserTransaction not null", userTx);
userTx.begin();
try
{
   ServiceReference tmRef = context.getServiceReference(TransactionManager.class.getName());
   assertNotNull("TransactionManager service not null", tmRef);
   
   TransactionManager tm = (TransactionManager)context.getService(tmRef);
   assertNotNull("TransactionManager not null", tm);
   
   Transaction tx = tm.getTransaction();
   assertNotNull("Transaction not null", tx);
   
   tx.registerSynchronization(txObj);
   
   txObj.setMessage("Donate $1.000.000");
   assertNull("Uncommited message null", txObj.getMessage());
   
   userTx.commit();
}
catch (Exception e)
{
   userTx.setRollbackOnly();
}
assertEquals("Donate $1.000.000", txObj.getMessage());
    


class Transactional implements Synchronization
{
  public void afterCompletion(int status)
  {
     if (status == Status.STATUS_COMMITTED)
        message = volatileMessage;
  }
  
  ...      
}
    

The interceptor example deployes a bundle that contains some metadata and an interceptor bundle that processes the metadata and registeres an http endpoint from it. The idea is that the bundle does not process its own metadata. Instead this work is delegated to some specialized metadata processor (i.e. the interceptor).

Each interceptor is itself registered as a service. This is the well known Whiteboard Pattern.



public class InterceptorActivator implements BundleActivator
{
   public void start(BundleContext context)
   {
      LifecycleInterceptor publisher = new PublisherInterceptor();
      LifecycleInterceptor parser = new ParserInterceptor();
      
      // Add the interceptors, the order of which is handles by the service
      context.registerService(LifecycleInterceptor.class.getName(), publisher, null);
      context.registerService(LifecycleInterceptor.class.getName(), parser, null);
   }
}
    


public class ParserInterceptor extends AbstractLifecycleInterceptor
{
   ParserInterceptor()
   {
      // Add the provided output
      addOutput(HttpMetadata.class);
   }
   public void invoke(int state, InvocationContext context)
   {
      // Do nothing if the metadata is already available  
      HttpMetadata metadata = context.getAttachment(HttpMetadata.class);
      if (metadata != null)
         return;
      // Parse and create metadta on STARTING
      if (state == Bundle.STARTING)
      {
          VirtualFile root = context.getRoot();
          VirtualFile propsFile = root.getChild("/http-metadata.properties");
          if (propsFile != null)
          {
             log.info("Create and attach HttpMetadata");
             metadata = createHttpMetadata(propsFile);
             context.addAttachment(HttpMetadata.class, metadata);
          }
      }
   }
   ...
}
    


public class PublisherInterceptor extends AbstractLifecycleInterceptor
{
   PublisherInterceptor()
   {
      // Add the required input
      addInput(HttpMetadata.class);
   }
   public void invoke(int state, InvocationContext context)
   {
      // HttpMetadata is guaratied to be available because we registered
      // this type as required input
      HttpMetadata metadata = context.getAttachment(HttpMetadata.class);
      // Register HttpMetadata on STARTING 
      if (state == Bundle.STARTING)
      {
         String servletName = metadata.getServletName();
         // Load the endpoint servlet from the bundle
         Bundle bundle = context.getBundle();
         Class servletClass = bundle.loadClass(servletName);
         HttpServlet servlet = (HttpServlet)servletClass.newInstance();
         // Register the servlet with the HttpService
         HttpService httpService = getHttpService(context, true);
         httpService.registerServlet("/servlet", servlet, null, null);
      }
      // Unregister the endpoint on STOPPING 
      else if (state == Bundle.STOPPING)
      {
         log.info("Unpublish HttpMetadata: " + metadata);
         HttpService httpService = getHttpService(context, false);
         if (httpService != null)
            httpService.unregister("/servlet");
      }
   }
}
    

The example-webapp.war archive is an OSGi Bundle and a Web Application Archive (WAR) at the same time. Similar to HTTP Service Example it registers a servlet and resources with the WebApp container. This is done through a standard web.xml descriptor.



<web-app xmlns="http://java.sun.com/xml/ns/javaee" ... version="2.5">

  <display-name>WebApp Sample</display-name>

  <servlet>
    <servlet-name>servlet</servlet-name>
    <servlet-class>org.jboss.test.osgi.example.webapp.bundle.EndpointServlet</servlet-class>
    <init-param>
      <param-name>initProp</param-name>
      <param-value>SomeValue</param-value>
    </init-param>
  </servlet>

  <servlet-mapping>
    <servlet-name>servlet</servlet-name>
    <url-pattern>/servlet</url-pattern>
  </servlet-mapping>

</web-app>
    

The associated OSGi manifest looks like this.

Manifest-Version: 1.0
Bundle-Name: example-webapp
Bundle-ManifestVersion: 2
Bundle-SymbolicName: example-webapp
Bundle-ClassPath: .,WEB-INF/classes
Import-Package: org.osgi.service.http,org.ops4j.pax.web.service,javax.servlet,javax.servlet.http
    

The test verifies that we can access the servlet and some resources.



@Test
public void testResourceAccess() throws Exception
{
  assertEquals("Hello from Resource", getHttpResponse("/message.txt"));
}
@Test
public void testServletAccess() throws Exception
{
  assertEquals("Hello from Servlet", getHttpResponse("/servlet?test=plain"));
}
    

The example-xml-binding.jar bundle unmarshalls an XML document through the UnmarshallerService. This example is very similar to the JAXB Example. However, it uses JBossXB to do the unmarshalling.



ServiceReference sref = context.getServiceReference(UnmarshallerService.class.getName());
UnmarshallerService unmarshaller = (UnmarshallerService)context.getService(sref);
Bundle bundle = context.getBundle();
URL xsdurl = bundle.getEntry("booking.xsd");
URL xmlurl = bundle.getEntry("booking.xml");
unmarshaller.registerSchemaLocation("http://org.jboss.test.osgi.jbossxb.simple/booking.xsd", xsdurl.toExternalForm());
unmarshaller.addClassBinding(CourseBooking.NAMESPACE_XML_SIMPLE, CourseBooking.class);
CourseBooking booking = (CourseBooking)unmarshaller.unmarshal(xmlurl.toExternalForm());
    

Resources

Authors

We offer free support through the JBoss OSGi User Forum.

Please note, that posts to this forum will be dealt with at the community's leisure. If your business is such that you need to rely on qualified answers within a known time frame, this forum might not be your preferred support channel.

For professional support please go to JBoss Support Services.