JBoss.orgCommunity Documentation
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
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-xml-jaxb.jar bundle gets the JAXBContext from the JAXBService and unmarshalls an XML document using JAXB
ServiceReference sref = context.getServiceReference(JAXBService.class.getName());
if (sref == null)
throw new IllegalStateException("JAXBService not available");
JAXBService service = (JAXBService)context.getService(sref);
JAXBContext jaxbContext = service.newJAXBContext(getClass().getPackage().getName());
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
URL resURL = context.getBundle().getResource("booking.xml");
JAXBElement rootElement = unmarshaller.unmarshal(resURL.openStream());
assertNotNull("root element not null", rootElement);
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"));
}
Please note that access to the MBeanServer from the test case is part of the OSGiRuntime abstraction.
The BundleStateTestCase uses JMX to control the bundle state through the BundleStateMBean. The testBundleStateMBean lists the available bundles over JMX.
public void testBundleStateMBean() throws Exception
{
BundleStateMBean bundleState = getRuntime().getBundleStateMBean();
assertNotNull("BundleStateMBean not null", bundleState);
TabularData bundleData = bundleState.listBundles();
assertNotNull("TabularData not null", bundleData);
assertFalse("TabularData not empty", bundleData.isEmpty());
}
The testUpdateBundle updates a bundle in the framework over JMX. It starts by installing a bundle via the FrameworkMBean. This bundle exports the package org.jboss.test.osgi.example.jmx.bundle.update1
public void testUpdateBundle() throws Exception
{
FrameworkMBean fw = getRuntime().getFrameworkMBean();
BundleStateMBean bs = getRuntime().getBundleStateMBean();
// Install and start a bundle via JMX that exports a package
URL bundleURL = getTestArchiveURL("example-jmx-update1.jar");
long bundleId = fw.installBundle(bundleURL.toString());
fw.startBundle(bundleId);
// Obtain the exported packages through JMX
assertEquals("[org.jboss.test.osgi.example.jmx.bundle.update1;0.0.0]",
Arrays.toString(bs.getExportedPackages(bundleId)));
Subsequently the bundle is updated through FrameworkMBean.updateBundledFromURL() with a revision that exports the package the package org.jboss.test.osgi.example.jmx.bundle.update2
URL updatedURL = getTestArchiveURL("example-jmx-update2.jar");
fw.updateBundleFromURL(bundleId, updatedURL.toString());
The StartLevelTestCase uses JMX to control bundle and framework start levels. The beginning of the testStartLevelMBean uses JMX to set the initial bundle start level to 2 and then installs a new bundle in the framework through JMX.
Once installed, it finds the bundle ID of the newly installed bundle through JMX, by obtaining the TabularData from listBundles(). The test then tries to start the bundle, but this doesn't actually start the bundle yet as the framework start level is still at 1. Increasing the start level of the bundle to 5 should keep the bundle in the INSTALLED state. Finally the framework start level is increased to 10 which will bring the bundle in the ACTIVE state.
public void testStartLevelMBean() throws Exception
{
FrameworkMBean fw = runtime.getFrameworkMBean();
fw.setInitialBundleStartLevel(2);
Assert.assertEquals(1, fw.getFrameworkStartLevel());
OSGiBundle bundle = runtime.installBundle("any_bundle.jar");
BundleStateMBean bs = runtime.getBundleStateMBean();
TabularData td = bs.listBundles();
long bundleId = -1;
for (CompositeData row : (Collection<CompositeData>)td.values())
{
if (bundle.getSymbolicName().equals(row.get("SymbolicName")))
{
bundleId = Long.parseLong(row.get("Identifier").toString());
break;
}
}
assertTrue("Could not find test bundle through JMX", bundleId != -1);
fw.startBundle(bundleId);
assertEquals(2, bs.getStartLevel(bundleId));
fw.setBundleStartLevel(bundleId, 5);
assertEquals(5, bs.getStartLevel(bundleId));
waitForBundleState("INSTALLED", bs, bundleId);
fw.setFrameworkStartLevel(10);
waitForBundleState("ACTIVE", bs, bundleId);
...
The example-jndi.jar bundle gets the InitialContext service and registers a string with JNDI. It then verifies the JNDI access.
ServiceReference sref = context.getServiceReference(InitialContext.class.getName());
if (sref == null)
throw new IllegalStateException("Cannot access the InitialContext");
InitialContext iniContext = (InitialContext)context.getService(sref);
iniCtx.createSubcontext("test").bind("Foo", new String("Bar"));
public void testJNDIAccess() throws Exception
{
InitialContext iniCtx = runtime.getInitialContext();
String lookup = (String)iniCtx.lookup("test/Foo");
assertEquals("JNDI bound String expected", "Bar", lookup);
// Uninstall should unbind the object
bundle.uninstall();
try
{
iniCtx.lookup("test/Foo");
fail("NameNotFoundException expected");
}
catch (NameNotFoundException ex)
{
// expected
}
}
Please note that access to the InitialContext from the test case is part of the OSGiRuntime abstraction.
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 ServiceLoader example uses three bundles - example-serviceloader-api.jar, example-serviceloader-impl.jar, example-serviceloader-client.jar. The implementation bundle contains a traditional service defined in META-INF/services. This service definition gets picked up by the ServiceLoader Interceptor and is automatically registered with the OSGi Framework.
For details and more background information, please hava a look at ServiceLoader and how it relates to OSGi.
The example-xml-parser.jar bundle gets a DocumentBuilderFactory/SAXParserFactory respectivly and unmarshalls an XML document using that parser.
ServiceReference sref = context.getServiceReference(DocumentBuilderFactory.class.getName());
if (sref == null)
throw new IllegalStateException("DocumentBuilderFactory not available");
DocumentBuilderFactory factory = (DocumentBuilderFactory)context.getService(sref);
factory.setValidating(false);
DocumentBuilder domBuilder = factory.newDocumentBuilder();
URL resURL = context.getBundle().getResource("example-xml-parser.xml");
Document dom = domBuilder.parse(resURL.openStream());
assertNotNull("Document not null", dom);
ServiceReference sref = context.getServiceReference(SAXParserFactory.class.getName());
if (sref == null)
throw new IllegalStateException("SAXParserFactory not available");
SAXParserFactory factory = (SAXParserFactory)context.getService(sref);
factory.setValidating(false);
SAXParser saxParser = factory.newSAXParser();
URL resURL = context.getBundle().getResource("example-xml-parser.xml");
SAXHandler saxHandler = new SAXHandler();
saxParser.parse(resURL.openStream(), saxHandler);
assertEquals("content", saxHandler.getContent());