<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9210566578097047576</id><updated>2012-01-29T13:24:57.198-08:00</updated><category term='OSGi'/><category term='rfc 119'/><category term='java'/><category term='distributed osgi'/><category term='ServiceLoader'/><category term='ajax'/><category term='ServiceMix'/><category term='RFC 167'/><category term='JSR311'/><category term='JAX-RS'/><category term='EMF Java text adventure'/><category term='Apache Camel'/><category term='apache cxf'/><category term='java.util.ServiceLoader'/><category term='Equinox'/><category term='IRC'/><category term='jboss'/><category term='GMF'/><category term='services'/><category term='OSGi Java cardgame Eclipse Equinox'/><category term='eclipse EMF'/><category term='Jersey'/><category term='Spring-DM'/><category term='google calendar'/><category term='c905'/><title type='text'>&lt;coderthoughts /&gt;</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>22</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-8112532715448106308</id><published>2011-08-29T06:27:00.000-07:00</published><updated>2011-08-29T06:27:46.898-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='java.util.ServiceLoader'/><category scheme='http://www.blogger.com/atom/ns#' term='RFC 167'/><category scheme='http://www.blogger.com/atom/ns#' term='ServiceLoader'/><title type='text'>java.util.ServiceLoader in OSGi</title><content type='html'>One of the bigger stumbling blocks for migrating an existing application to OSGi has been java.util.ServiceLoader and its predecessors (the various FactoryFinders that can be found in technologies such as JAXP, JAXRS, etc...).&lt;br /&gt;&lt;br /&gt;ServiceLoader is a class provided by the JRE aimed at loading pluggable implementations of something. For those who need a little refreshing of the brain, here's what typical ServiceLoader usage looks like... Let's say you have an SPI called MySPI with a single method doit():&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public abstract class MySPI {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public abstract String doit();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;For some reason SPI providers are often realized as abstract classes, so that's what I'm doing here too. Obviously using an interface is also possible...&lt;br /&gt;You load the SPI implementations by calling ServiceLoader.load(). To obtain the actual implementation objects you iterate over the result of that call:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;ServiceLoader&lt;myspi&gt; ldr = ServiceLoader.load(MySPI.class);&lt;/myspi&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;for (MySPI spiObject : ldr) {&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; spiObject.doit(); // invoke my SPI object&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;So why is this such a big problem in OSGi? The problem is that the JRE's ServiceLoader is non-modular by design. It relies on a single classloader that has visibility over everything in your system that can possibly contain an implemetation of the service requested. The load() method as used above uses the ThreadContextClassLoader (TCCL) to find all the ServiceLoader implementations in the system. There are a number of issues with this from an OSGi perspective.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The value of the TCCL is generally not defined in an OSGi context.&lt;/li&gt;&lt;li&gt;The implementations are found by looking for a resource in META-INF/services. Every jar that contains an implementation of a service (e.g. my org.acme.MySPI implementation) should advertise this by putting a META-INF/service/org.acme.MySPI file in its jar file.&lt;/li&gt;&lt;li&gt;Once the above resources have been found, the contents of them are parsed to obtain the class names of implementations, normally these classes reside in an implementation package, something like org.acme.impl.MySPIImpl... ServiceLoader will instantiate the classes found using their no-arg constructor.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Issue 2 clearly poses an issue if this resource is to be obtained through the classloading rules set by OSGi as OSGi wires a package import only to precisely one exporter. This gives great predictability (you know exactly where you're getting a package from) but it doesn't work if you need to get the same resource in the same package from multiple bundles. And even if you were able to get all the META-INF/services resources in your system you would still have the issue that the classes listed in there are generally private implementation classes that you don't want to export outside of a bundle. To load and instantiate them from a consuming bundle you'd also have to import these private implementation packages which would be totally horrible (issue 3).&lt;br /&gt;&lt;br /&gt;So how should we do this in OSGi? Clearly the best answer is: use the OSGi service registry. It doesn't suffer from any of the problems ServiceLoader has: no need to expose and import private packges, no need to expose in a single package across multiple bundles. Plus the OSGi Service Registry supports a dynamic service lifecycle: services can come can go, additional suitable services can appear, existing services can get replaced. Furthermore, the OSGi ServiceRegistry supports a rich language of selecting services among possible candidates by using LDAP filters that work over service properties, while ServiceLoader simply gives you a snapshot of all the known service implementations at a particular point in time.&lt;br /&gt;&lt;br /&gt;However - you might be looking at some code that you'd like to use as-is. You might not have the luxury of using the OSGi Service Registry right now. Maybe you don't have the source code so you can't modify it or maybe you're just focusing on other things and don't want to modify the code. You just like to use the same code that works with ServiceLoader outside of OSGi, in OSGi...&lt;br /&gt;&lt;br /&gt;For this use-case I've started looking at a solution. The solution is based on the premise that you don't want or can't modify the code that uses ServiceLoader. Since this code is almost certainly non-OSGi you will have to turn it into an OSGi bundle by either wrapping it (put the original jar unmodified inside an OSGi wrapper bundle) or by modifying the MANIFEST.MF file to add things like Bundle-SymbolicName and Import-Package statements. Since you're modifying this file anyway, it's not a big deal to add an extra header in there to identify the bundle as a ServiceLoader-user. The header is called SPI-Consumer. The simplest form is:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; SPI-Consumer: *&lt;/div&gt;Which means that this bundle is identified as an SPI consumer with no restrictions. This means that when code such as&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&amp;nbsp; ServiceLoader&lt;myspiprovider&gt; ldr = ServiceLoader.load(MySPIProvider.class);&lt;/myspiprovider&gt;&lt;/span&gt;&lt;/div&gt;is encountered, something special will have to be done. What needs to be done is that the TCCL is set to the bundle class loader of the bundle that contains the implementation of MySPIProvider for the duration of the load() call. It needs to be the classloader of the bundle itself, so it has visibility of the private packages which are needed for ServiceLoader to do its business.&lt;br /&gt;&lt;br /&gt;In my implementation, which is available in the &lt;a href="http://aries.apache.org/modules/spi-fly.html"&gt;Apache Aries project&lt;/a&gt;, the consumer byte-code is slightly modified so that &lt;i&gt;for the duration of the ServiceLoader.load() call&lt;/i&gt; the TCCL is set to the classloader of the providing bundle(s).&lt;br /&gt;&lt;br /&gt;How do we know that a bundle is providing an implementation for use with java.util.ServiceLoader? Similar to the consumer side, this code was most likely not written as an OSGi bundle, so it either needs to be wrapped or its MANIFEST.MF needs to be enhanced with Bundle-SymbolicName etc. Here we can identify the bundle as an SPI provider by adding:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; SPI-Provider: *&lt;/div&gt;This means that all the implementations found in META-INF/services are considered as SPI providers. (You can also provide a subset by enumerating all the SPI names provided, instead of specifying &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;*&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Specifying this header does two things:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It marks the bundle as an SPI provider for use with bundles that are marked as SPI consumer.&lt;/li&gt;&lt;li&gt;It registers the implementations found in META-INF/services in the OSGi Service Registry so that they can be used from there too.&lt;/li&gt;&lt;/ol&gt;&lt;b&gt;Try it out!&lt;/b&gt;&lt;br /&gt;Ok - let's try it out. For that you need:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;An OSGi Framework - I'm going to use JBoss AS7.&lt;/li&gt;&lt;li&gt;An SPI Consumer, Provider and the SPI itself.&lt;/li&gt;&lt;li&gt;The Aries SPI-Fly project which implements the functionality associated with the SPI-Consumer and SPI-Provider Manifest headers.&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Start the OSGi Framework&lt;/b&gt;&lt;br /&gt;I'm taking JBoss AS7 which contains a fully compliant OSGi 4.2 Core Framework. Get version 7.0.1 from here: &lt;a href="http://www.jboss.org/jbossas/downloads/"&gt;http://www.jboss.org/jbossas/downloads/&lt;/a&gt;&lt;br /&gt;Download and unzip it, then run bin/standalone.sh (or standalone.bat):&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;...&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;11:49:45,016 INFO&amp;nbsp; [org.jboss.as.deployment] (MSC service thread 1-12) Started FileSystemDeploymentService for directory /Users/davidb/jboss/jboss-as-web-7.0.1.Final/standalone/deployments&lt;br /&gt;11:49:45,028 INFO&amp;nbsp; [org.jboss.as] (Controller Boot Thread) JBoss AS 7.0.1.Final "Zap" started in 1671ms - Started 93 of 148 services (55 services are passive or on-demand)&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;BTW the AS7 appserver started up in 1671ms on my plain mac!&lt;br /&gt;&lt;b&gt; &lt;/b&gt;&lt;br /&gt;&lt;b&gt;Get some example bundles&lt;/b&gt;&lt;br /&gt;I'm going to use some of the example bundles from the Aries SPI-Fly project...&lt;br /&gt;&lt;br /&gt;You need a SPI Consumer. For instance &lt;a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/src/main/java/org/apache/aries/spifly/examples/client2/impl/Activator.java"&gt;this one that uses the SPI from the Bundle Activator&lt;/a&gt;:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;public class Activator implements BundleActivator {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void start(BundleContext context) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ServiceLoader&lt;spiprovider&gt; ldr = ServiceLoader.load(SPIProvider.class);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for (SPIProvider spiObject : ldr) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println(spiObject.doit()); // invoke the SPI object&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void stop(BundleContext context) throws Exception {}&lt;/spiprovider&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;}&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;I have a separate bundle &lt;a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-spi-bundle/src/main/java/org/apache/aries/spifly/mysvc/SPIProvider.java"&gt;that contains the SPI itself&lt;/a&gt;: &lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;public abstract class SPIProvider {&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public abstract String doit();&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;I used an abstract class here instead of an interface as that seems to be common practise with SPIs used with ServiceLoader.&lt;br /&gt;&lt;br /&gt;And I have an SPI Provider bundle &lt;a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/src/main/java/org/apache/aries/spifly/mysvc/impl2/SPIProviderImpl.java"&gt;with an implementation&lt;/a&gt;:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;package org.apache.aries.spifly.mysvc.impl2;&lt;br /&gt;import org.apache.aries.spifly.mysvc.SPIProvider;&lt;br /&gt;&lt;br /&gt;public class SPIProviderImpl extends SPIProvider {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public String doit() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return "Doing it too!";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/span&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/div&gt;&lt;br /&gt;The provider bundle has the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;SPI-Provider: *&lt;/span&gt; Manifest header in it and the consumer bundle has the&amp;nbsp;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;SPI-Consumer: *&lt;/span&gt; header.&amp;nbsp; &lt;br /&gt;As the SPI-Fly project hasn't been released yet, these bundles can't be downloaded from Maven yet. You can build them yourself from here: &lt;a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples"&gt;http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples&lt;/a&gt;&lt;br /&gt;You can also download them from these locations where I put my built artifacts:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/consumer-bundle.jar"&gt;consumer-bundle.jar&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/spi-bundle.jar"&gt;spi-bundle.jar&lt;/a&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/provider-bundle.jar"&gt;provider-bundle.jar&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;b&gt;Add OSGi-ServiceLoader support&lt;/b&gt;&lt;br /&gt;The Apache Aries SPI Fly component provides two ways to add ServiceLoader support to an OSGi Framework:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Dynamically by using the OSGi 4.3 Weaving Hooks. With this you can install the above bundles as-is in the OSGi Framework and the treatment of the ServiceLoader.load() calls will happen on the fly.&lt;/li&gt;&lt;li&gt;Statically by transforming the bundle Jar file before installing it in the OSGi framework. This mechanism doesn't need the 4.3 Weaving Hooks.&lt;/li&gt;&lt;/ol&gt;As not all OSGi frameworks support the Weaving Hooks yet, I'll be showing the static mechanism here. If you want to try the dynamic mechanism see the &lt;a href="http://aries.apache.org/modules/spi-fly.html"&gt;Aries SPI-Fly docs&lt;/a&gt; for more info.&lt;br /&gt;The static mechanism consists of a tool that transforms your bundles inserting the bytecode needed to set the TCCL when ServiceLoader.load() is called. You can build the tool yourself from here: &lt;a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-tool"&gt;http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-tool&lt;/a&gt; or download the copy that I built from here: &lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/spifly-static-tool.jar"&gt;spifly-static-tool.jar&lt;/a&gt; .&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; java -jar spifly-static-tool.jar consumer-bundle.jar &lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; [SPI Fly Static Tool] Processing: consumer-bundle.jar&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;When it's finished you'll find a new bundle called &lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;consumer-bundle_spifly.jar&lt;/span&gt;&lt;/span&gt; in the same directory. This bundle has the ServiceLoader.load() calls treated so that the TCCL is set appropriately for it to work.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Use it at Runtime&lt;/b&gt;&lt;br /&gt;Our transformed client bundle has an additional dependency on some SPI-fly classes which are used to appropriately set the TCCL once ServiceLoader.load() is called. This bundle can be found here: &lt;a href="http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-bundle"&gt;http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-bundle&lt;/a&gt; and I have created a pre-built version as well: &lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/spifly-static-bundle.jar"&gt;spifly-static-bundle.jar&lt;/a&gt; .&lt;br /&gt;&lt;br /&gt;Deploy them into your OSGi Framework. AS7 supports a variety of deployment mechanisms, &lt;a href="https://docs.jboss.org/author/display/AS7/Helloworld+OSGi+quickstart"&gt;via maven&lt;/a&gt;, &lt;a href="https://docs.jboss.org/author/display/AS7/Management+Clients"&gt;AS7 Web Console or Command Line&lt;/a&gt;, &lt;a href="https://docs.jboss.org/author/display/AS7/OSGi+subsystem+configuration"&gt;Felix OSGi Console or JMX&lt;/a&gt;. For the sake of simplicity I'm using deployment by copying to the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;standalone/deployments&lt;/span&gt; folder here. Note that although deployment by copying is extremely simple, it does require attention to the order as the bundles found in the deployments folder are instantly started.&lt;br /&gt;&lt;br /&gt;So I'm deploying:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/spifly-static-bundle.jar"&gt;spifly-static-bundle.jar&lt;/a&gt; (provides runtime support)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/spi-bundle.jar"&gt;spi-bundle.jar&lt;/a&gt; (our SPI class)&lt;/li&gt;&lt;li&gt;&lt;a href="http://dl.dropbox.com/u/1151396/spi-fly/provider-bundle.jar"&gt;provider-bundle.jar&lt;/a&gt; (our implementation)&lt;/li&gt;&lt;li&gt;consumer-bundle_spifly.jar (the one that we created from by running spifly-static-tool above)&lt;/li&gt;&lt;/ul&gt;On the console we'll see various log messages appear, including these from our SPI Consumer:&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;span style="font-size: x-small;"&gt;12:44:27,480 INFO&amp;nbsp; [stdout] (MSC service thread 1-3) *** Result from invoking the SPI directly: &lt;br /&gt;12:44:27,483 INFO&amp;nbsp; [stdout] (MSC service thread 1-3) Doing it too!&lt;/span&gt;&lt;/div&gt;&lt;br /&gt;Which shows that our ServiceLoader Consumer bundle is now working property inside OSGi.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Conclusion &lt;/b&gt;&lt;br /&gt;This example uses java.util.ServiceLoader provider and consumer implementations as-is in OSGi. Using the OSGi Service Registry is clearly a nicer solution, but you simply don't always have the option to change the code from using ServiceLoader to using the OSGi ServiceRegistry. That's where OSGi ServiceLoader support may come in useful.&lt;br /&gt;&lt;br /&gt;It can already be used today in any compliant OSGi 4.2 Framework, but in that case consumer bundles need to be transformed before they are installed. If you have a 4.3 (or later) framework the transformation can happen on the fly which means that the whole process becomes more user-friendly: just install ServiceLoader support in the Framework and deploy your bundles - no static tool is needed in that case.&lt;br /&gt;&lt;br /&gt;ServiceLoader support is being turned in an OSGi Specification scheduled to be part of the upcoming OSGi Enterprise Release (due early 2012). You can read more details about it in RFC 167 in the Early Access Draft here: &lt;a href="http://www.osgi.org/download/osgi-early-draft-2011-05.pdf"&gt;http://www.osgi.org/download/osgi-early-draft-2011-05.pdf&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-8112532715448106308?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/8112532715448106308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=8112532715448106308' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/8112532715448106308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/8112532715448106308'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2011/08/javautilserviceloader-in-osgi.html' title='java.util.ServiceLoader in OSGi'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-5109091649038136818</id><published>2010-11-11T07:20:00.000-08:00</published><updated>2010-11-11T09:47:07.496-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss'/><title type='text'>Using OSGi in JBoss AS7</title><content type='html'>Recently the first milestone of JBoss Application Server 7 has been released which now comes with OSGi support built-in!&lt;br /&gt;&lt;br /&gt;&lt;b style="font-family: Arial,Helvetica,sans-serif;"&gt;OSGi Support in JBoss AS7 what does that mean? &lt;/b&gt;&lt;br /&gt;It means that AS7 comes with an OSGi framework inside that you can use to deploy your OSGi bundles in. Besides providing standards-compliant OSGi functionality, the OSGi framework in AS7 will also provide integration with non-OSGi components (such as JavaEE ones) deployed in the Application Server. This is realized by the JBoss OSGi framework that was developed from the  ground up with a specific focus to provide the best possible  integration with AS7. The framework is mostly the work of Thomas Diesler, although I have also been involved in writing bits of it over the past few months...&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Arial,Helvetica,sans-serif;"&gt;&lt;b&gt;Let's give it a go!&lt;/b&gt;&lt;/div&gt;So if you're curious, here's a quick demo on how to develop a 'hello world' bundle using Eclipse and deploy it into AS7. But first start by downloading an AS7 build from here: &lt;a href="http://www.jboss.org/jbossas/downloads"&gt;http://www.jboss.org/jbossas/downloads&lt;/a&gt;. Unzip it somewhere on disk and start it up by running:&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp; bin/standalone.sh &lt;/div&gt;&lt;br /&gt;&lt;div style="font-family: Arial,Helvetica,sans-serif;"&gt;&lt;b&gt;Develop your OSGi bundle&lt;/b&gt;&lt;/div&gt;You can develop your bundle any way you like. For little projects or simply to get started I always use the Eclipse PDE that's part of the &lt;a href="http://www.eclipse.org/downloads/"&gt;'classic' distribution&lt;/a&gt;. For more serious projects I normally use maven with the &lt;a href="http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html"&gt;Felix Bundle Plugin&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Here I simply follow the PDE wizard to develop my new TestBundle. Go File | New Project | Plug-in Project, specifying a standard OSGi Framework as the target platform:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyQShhRVI/AAAAAAAAAR8/oAgnJeq2s4E/s1600/shot1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyQShhRVI/AAAAAAAAAR8/oAgnJeq2s4E/s400/shot1.png" width="377" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Select the 'Hello OSGi Bundle' template:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvyQ4OPk6I/AAAAAAAAASA/VA5-jGRjXC0/s1600/shot2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="400" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvyQ4OPk6I/AAAAAAAAASA/VA5-jGRjXC0/s400/shot2.png" width="378" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And after hitting 'Finish' you've got a simple OSGi bundle, which has an Activator provided by the template:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyRukEXDI/AAAAAAAAASE/J8plZAWSmME/s1600/shot3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="371" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyRukEXDI/AAAAAAAAASE/J8plZAWSmME/s640/shot3.png" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;For this posting I'm not making any changes to the OSGi bundle code, so let's just simply File | Export it as a deployable plugin:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvySO1q-2I/AAAAAAAAASI/DlACAh3trpM/s1600/shot4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="301" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNvySO1q-2I/AAAAAAAAASI/DlACAh3trpM/s400/shot4.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;This puts the OSGi bundle in the directory that you specified in the wizard. In my case the bundle is called &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;TestBundle_1.0.0.jar&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Arial,Helvetica,sans-serif;"&gt;&lt;b&gt;Now deploy in JBoss AS7!&lt;/b&gt;&lt;/div&gt;I started AS7 somewhere using the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;bin/standalone.sh&lt;/span&gt; script and it has started up:&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;...&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;13:47:22,633 INFO&amp;nbsp; [org.jboss.as.deployment] (pool-1-thread-2) Started FileSystemDeploymentService for directory /home/davidb/temp/as7/jboss-7.0.0.Alpha1/standalone/deployments&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;13:47:22,636 INFO&amp;nbsp; [org.jboss.as.server] (pool-1-thread-1) JBoss AS 7.0.0.Alpha1 "Halloween" started in 3627ms. - Services [Total: 150, On-demand: 7. Started: 143]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Ok, so it has started up. A little bit higher in the log you'll also see:&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;13:47:21,007 INFO&amp;nbsp; [osgi] Activating OSGi Subsystem&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Good - OSGi is there.&lt;br /&gt;Now I copy my &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;TestBundle_1.0.0.jar&lt;/span&gt; into the &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;.../jboss-7.0.0.Alpha1/standalone/deployments&lt;/span&gt; folder and a moment later I can see the following message in the log:&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;13:52:24,116 INFO&amp;nbsp; [stdout] (pool-1-thread-2) Hello World!!&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;This is the message printed by our OSGi Bundle Activator running in AS7!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Arial,Helvetica,sans-serif;"&gt;&lt;b&gt;Control the framework through JMX&lt;/b&gt;&lt;/div&gt;The OSGi Framework in JBoss AS7 does not currently come with a console application - you can control it through JMX. So let's fire up jconsole, use &lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;service:jmx:rmi:///jndi/rmi://127.0.0.1:1090/jmxrmi&lt;/span&gt;&lt;/span&gt; to connect to the Remote Process:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNv40AEP9rI/AAAAAAAAASM/6X5mk4gp32U/s1600/shot5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="346" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/TNv40AEP9rI/AAAAAAAAASM/6X5mk4gp32U/s400/shot5.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;In the MBean tab you will find the org.osgi tree which allows us to control the OSGi framework through a JMX API standardized in the Enterprise Expert Group. You can find the listBundles() API in the bundleState operations:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNv40tgClZI/AAAAAAAAASQ/GWtJt2FA7tE/s1600/shot6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="208" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/TNv40tgClZI/AAAAAAAAASQ/GWtJt2FA7tE/s320/shot6.png" width="320" /&gt;&lt;/a&gt;&lt;/div&gt;Yes, our TestBundle is there, and it has bundle ID 6.&lt;br /&gt;&lt;br /&gt;Let's now uninstall it using the framework MBean:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/TNv6zWFSbmI/AAAAAAAAASU/h_j2G4RnEh8/s1600/shot7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="275" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/TNv6zWFSbmI/AAAAAAAAASU/h_j2G4RnEh8/s400/shot7.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;Hit the uninstallBundle JMX API with the bundle ID (6) and you'll see that the bundle gets stopped and then uninstalled.&lt;br /&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;14:14:00,041 INFO&amp;nbsp; [stdout] (RMI TCP Connection(13)-127.0.0.1) Goodbye World!!&lt;/span&gt;&lt;br style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" /&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;14:14:00,057 INFO&amp;nbsp; [org.jboss.osgi.framework.bundle.AbstractBundle] (RMI TCP Connection(13)-127.0.0.1) Bundle uninstalled: TestBundle:1.0.0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;You can also control the OSGi framework through a webconsole, which can currently get installed via a separate installer, we're working on making the webconsole available in the AS7 distributable in a future milestone.&lt;br /&gt;&lt;br /&gt;&lt;div style="font-family: Arial,Helvetica,sans-serif;"&gt;&lt;b&gt;Conclusion&lt;/b&gt;&lt;/div&gt;OSGi support is now available from the very first milestone of JBoss AS7. Give it a try!&lt;br /&gt;If you have any questions, reach us on the forums: &lt;a href="http://community.jboss.org/en/jbossosgi"&gt;http://community.jboss.org/en/jbossosgi&lt;/a&gt; or via JIRA: &lt;a href="https://jira.jboss.org/browse/JBOSGI"&gt;https://jira.jboss.org/browse/JBOSGI&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;More in-depth details can be found in Thomas Diesler's blog post: &lt;a href="http://jbossosgi.blogspot.com/2010/11/jboss-as7-osgi-integration.html"&gt;http://jbossosgi.blogspot.com/2010/11/jboss-as7-osgi-integration.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-5109091649038136818?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/5109091649038136818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=5109091649038136818' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5109091649038136818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5109091649038136818'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2010/11/using-osgi-in-jboss-as7.html' title='Using OSGi in JBoss AS7'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JTmY6gtOOjs/TNvyQShhRVI/AAAAAAAAAR8/oAgnJeq2s4E/s72-c/shot1.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-1667317833134111654</id><published>2010-06-17T02:34:00.000-07:00</published><updated>2010-06-17T02:34:28.915-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><title type='text'>Eek! Java reflection can cause NoClassDefFoundError after 15 calls</title><content type='html'>Now this came as a bit of a surprise to me. Reading this &lt;a href="http://www.mail-archive.com/users@felix.apache.org/msg07724.html"&gt;thread on the Felix mailing list&lt;/a&gt;...&lt;br /&gt;&lt;br /&gt;I can't believe this: when using reflection after about 15 calls, the internal behaviour of the Sun JRE changes. With the changed behaviour comes in a package dependency on the sun.reflect package! When running under OSGi you are very much in charge of what comes in via the classloader so a new package that comes in over the same code path after about 15 calls is bad news - you really want to know your package dependencies up front.&lt;br /&gt;The solution is obviously to import that sun.reflect package in your affected bundle but how are you supposed to know? Let's say you have a test suite that properly tests your code and everything is looking nice. I would imagine that a testsuite doesn't test every method call 15 times to make sure its still ok. Moreover, how do we know it's 15? There could be another optimization lurking in the JRE that requires 300 calls in order for it to be exposed.&lt;br /&gt;Another problem with importing sun.reflect is that it's implementation specific. Do other implementations have a similar issue?&lt;br /&gt;&lt;br /&gt;Here's a message to all JVM implementors: optimize all you can but please don't change the package dependency graph after a number of invocations. At the very least make your implementation fail fast, not after &lt;i&gt;x&lt;/i&gt; method calls.&lt;br /&gt;&lt;br /&gt;There is a bug for this at sun: &lt;a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265952"&gt;http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265952&lt;/a&gt;&lt;br /&gt;Accepted in 2005 but still open...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-1667317833134111654?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/1667317833134111654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=1667317833134111654' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1667317833134111654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1667317833134111654'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2010/06/eek-java-reflection-can-cause.html' title='Eek! Java reflection can cause NoClassDefFoundError after 15 calls'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-3287136601858913109</id><published>2009-11-18T03:28:00.000-08:00</published><updated>2009-11-18T05:58:42.092-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='services'/><title type='text'>Altering OSGi Service Lookups with Service Registry Hooks</title><content type='html'>&lt;div lang="en-GB"&gt;OSGi Services are &lt;i&gt;&lt;b&gt;great&lt;/b&gt;&lt;/i&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;. They can solve many problems very elegantly. Take for instance SPI patterns. SPI patterns are used to make implementations pluggable. This type of pattern outside OSGi often comes with a bunch of external configuration (e.g. in META-INF/services) that makes it hard to manage and gives you a once-off chance to choose your implementation that you're stuck with for the rest of the VM lifecycle. OSGi Services provide a much more elegant way to solve this.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;/div&gt;In many cases clients simply want to use some functionality and don't particularly care how that functionality was created. When using OSGi Services the functionality is looked up in the OSGi Service Registry which is a directory of these. Services can be looked up by implemented interface (like a phone book) or by provided attribute (like the yellow pages). How the service got there is not interesting to the client and he's not involved in that. What if the service gets replaced while the client is active? No problem, the OSGi Service Programming model is actually built around this dynamicity. You don't need to stop service consumers when you are replacing or updating the service, they are automatically rebound.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;The Service Registry also provides us with an attribute based selection mechanism which is very nice if there are multiple implementations to choose from. As an example, take a system where every available printer is represented in the Service Registry as a Service that implements the &lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: Courier New,monospace;"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;org.acme.Printer&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt; interface. Each printer will have additional properties registered that help with the selection:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR2s5E6sI/AAAAAAAAARc/2HRdqC9jsnk/s1600/img1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR2s5E6sI/AAAAAAAAARc/2HRdqC9jsnk/s400/img1.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;When looking for a Printer OSGi allows you to use an LDAP-style filter to select the printer you want. So if you want a printer that can do A3 you would use this LDAP filter:&lt;br /&gt;&lt;/div&gt;&lt;div class="code-western" lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;  &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;(&amp;amp;(objec&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: Courier New,monospace; font-size: small;"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;tClass=org.acme.Printer)(paper-&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; font-size: small;"&gt;size=A3))&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;&lt;br /&gt;or if you want a printer in a location that starts with 'b' you do this:&lt;br /&gt;&lt;/div&gt;&lt;div class="code-western" lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;  &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;(&amp;amp;(objectClass=org.acme.Printer)(location=b*))&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;br /&gt;This is all great and you can build your system on this using either plain OSGi code or component frameworks such as OSGi Blueprint or OSGi Declarative Services. However sometimes you may want to tweak the properties at a later date without having to rewrite your system. Assume that in your organisation all of a sudden the office numbering has changed, which would really mean that you'd have to update the 'location' attribute of all the printers. Or maybe you want to add some additional metadata to existing printers that influence the selection process, like their energy rating.  &lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;Service Registry Hooks provide the building blocks that make this possible. &lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;I wrote a little bundle called &lt;i&gt;&lt;b&gt;ServiceJockey&lt;/b&gt;&lt;/i&gt; that uses these to manipulate service registrations and how they are looked up. BTW you can find details at the end of this posting about getting ServiceJockey, which is open source and available freely under the Apache License.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;Service Registry Hooks: what are they?&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;div lang="en-GB"&gt;One of the driving factors for Service Registry Hooks (an OSGi standard introduced in the 4.2 Core specification) was the Remote Services (Distributed OSGi) work done in the OSGi Alliance. One of the things that implementations of the Remote Service spec need to know is what kind of services consumers are looking for so that they can go out to a discovery system to see if it might be available remotely. Eagerly registering all available remote services is clearly not scalable so we need a smarter mechanism to allow for &lt;i&gt;transparent&lt;/i&gt;&lt;span style="font-style: normal;"&gt; on demand discovery of remote services. This is what the &lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;b&gt;ListenerHook &lt;/b&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;and &lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;b&gt;FindHook &lt;/b&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;provide us. Together they allow us to find out what service consumers are requesting making us to only look in a remote Discovery system for those that are relevant to the current framework. &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;Another problem was frequently brought up. What if you have an existing bundle that doesn't know anything about Remote Services? What is the default behaviour? Should all service lookups all of a sudden always include remote services? The Remote Services spec does include a special property that you can add to your filter to influence this (&lt;/span&gt;&lt;/span&gt;&lt;span style="font-family: Courier New,monospace; font-size: small;"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;service.imported&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;) but you clearly don't want to break up all your existing service consumers to add this extra property to their lookup filters.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;It turned out that there wasn't really a valid answer to that question applicable to all cases. If you're working on a system only using Remote Services for a select set of services it may make sense to default all other services consumers to &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="font-weight: normal;"&gt;not&lt;/span&gt;&lt;/i&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt; use Remote Services. On the other hand, maybe if you're building a Cloud-based infrastructure where services can move freely from one container to another the default behaviour could be that you do allow remote services for just about anything.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;Since there isn't really a one-size fits-all here the Service Registry Hooks make it possible to provide a &lt;/span&gt;&lt;/span&gt;&lt;i&gt;&lt;span style="font-weight: normal;"&gt;policy&lt;/span&gt;&lt;/i&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt; for this problem in a separate bundle. You can create &lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;b&gt;EventHooks &lt;/b&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;and &lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;b&gt;FindHooks &lt;/b&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;to influence what services consumers can see. This effectively allows you to add extra conditions to the lookup of service consumers without the need to modify those consumers.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;Finally, &lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;b&gt;EventHooks &lt;/b&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;and &lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;b&gt;FindHooks&lt;/b&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;, together with the a &lt;/span&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;b&gt;ServiceListener&lt;/b&gt;&lt;/span&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;, allow you to proxy Service Registrations, where you provide a second registration of the same object with modified properties, hiding the original.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;The ability to provide an alternate registration on the fly and the possibility to impose extra conditions on existing service consumer lookups is what I built ServiceJockey around.&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR4vKkr7I/AAAAAAAAARk/jBGn1vJTTRs/s1600/img2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR4vKkr7I/AAAAAAAAARk/jBGn1vJTTRs/s640/img2.png" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;ServiceJockey doesn't replace the Service Object, it only effectively replaces the registration in the Service Registry plus it can put additional constraints on client lookups. But you could go further. If you wanted to actually replace or shadow the real service object and do some work when somebody uses it (maybe log it or something) you could put an additional object between the consumer and the original service object.&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;Because the Service Registry Hooks effectively modify some basic behaviour of the framework (the visibility of Services) they should really be started early in the Framework lifecycle. You can achieve that by giving them a low start level.  &lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-style: normal; font-weight: normal;"&gt;The Service Registry Hooks are available in Felix 1.8.0 and Equinox 3.5 and newer versions of these.&lt;br /&gt;&lt;/div&gt;&lt;b&gt;&lt;span style="font-size: large;"&gt;&lt;br /&gt;Altering Service Registrations&lt;/span&gt;&lt;/b&gt; &lt;br /&gt;&lt;div lang="en-GB"&gt;Let's start with a bundle that consumes (uses) a Printer. It isn't interested in which printer, as long as it can print A4:&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;  BundleContext context = ... &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: #3f7f5f;"&gt;// from Activator.start()&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;  Filter filter = context.createFilter(&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;&lt;span style="color: #2a00ff;"&gt;&amp;nbsp; "(&amp;amp;(objectClass=org.acme.Printer)(paper-size=A4))"&lt;/span&gt;&lt;span style="color: black;"&gt;);        &lt;/span&gt;&lt;/span&gt; &lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: #0000c0;"&gt;st&lt;/span&gt;&lt;span style="color: black;"&gt; = &lt;/span&gt;&lt;span style="color: #7f0055;"&gt;&lt;b&gt;new&lt;/b&gt;&lt;/span&gt;&lt;span style="color: black;"&gt; ServiceTracker(context, filter, &lt;/span&gt;&lt;span style="color: #7f0055;"&gt;&lt;b&gt;null&lt;/b&gt;&lt;/span&gt;&lt;span style="color: black;"&gt;) {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: #7f0055;"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/span&gt;&lt;span style="color: black;"&gt; Object addingService(ServiceReference ref) {&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: #3f7f5f;"&gt;// print out some information on the printer&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: #3f7f5f;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // or use the printer&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: #7f0055;"&gt;&lt;b&gt;return&lt;/b&gt;&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;span style="color: #7f0055;"&gt;&lt;b&gt;super&lt;/b&gt;&lt;/span&gt;&lt;span style="color: black;"&gt;.addingService(ref);&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;&amp;nbsp; }            &lt;/span&gt; &lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;  };&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;  &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: #0000c0;"&gt;st&lt;/span&gt;&lt;span style="color: black;"&gt;.open();&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;When I run this it's reporting both printers that I have in my system:&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;Printer:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;objectClass: [org.acme.Printer]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;service.id: 27&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;name: p1&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;location: b283&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;capabilities: [Double-sided]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;paper-size: [A3, A4]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt; &lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;Printer:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;objectClass: [org.acme.Printer]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;service.id: 28&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;name: p7&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;location: a12&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;capabilities: [Colour, Staple]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="color: black; font-size: small;"&gt;paper-size: [A4, Letter]&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;So the client has arbitrary access to both of them. Now lets see if you can modify the client behaviour so that it only uses a printer with an Energy Rating &amp;lt; 50.  &lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;But hold on, we don't even know about energy ratings yet! We need to add this information to the Printer registration &lt;b&gt;without changing the bundle(s) that register the Printers.&lt;/b&gt;&lt;span style="font-weight: normal;"&gt; Let's user Service Jockey to do this.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;Service Jockey uses the OSGi Extender Model. This means that it is driven from a data file in a bundle.  &lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;So I've got a bundle (called PrinterJockey) that contains META-INF/sj.xml:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;service-jockey&lt;/span&gt;&lt;span style="color: teal;"&gt; &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;xmlns&lt;/span&gt;&lt;span style="color: black;"&gt;=&lt;/span&gt;&lt;span style="color: #2a00ff;"&gt;&lt;i&gt;"http://www.coderthoughts.org/schemas/sj/v1.0.0"&lt;/i&gt;&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;proxy-registration&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;rule&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;service-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt;(&lt;/span&gt;&lt;span style="color: #2a00ff;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: black;"&gt;(objectClass=org.acme.Printer)(name=p1))&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;/service-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;add-property&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;key&lt;/span&gt;&lt;span style="color: black;"&gt;=&lt;/span&gt;&lt;span style="color: #2a00ff;"&gt;&lt;i&gt;"energy-rating"&lt;/i&gt;&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt;75&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;/add-property&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp; &lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;&amp;lt;/rule&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp; &lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;rule&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;service-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt;(&lt;/span&gt;&lt;span style="color: #2a00ff;"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color: black;"&gt;(objectClass=org.acme.Printer)(name=p7))&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;/service-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp;&amp;nbsp;      &lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;add-property&lt;/span&gt; &lt;span style="color: #7f007f;"&gt;key&lt;/span&gt;&lt;span style="color: black;"&gt;=&lt;/span&gt;&lt;span style="color: #2a00ff;"&gt;&lt;i&gt;"energy-rating"&lt;/i&gt;&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt;50&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;add-property&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp; &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;/rule&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;/proxy-registration&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;service-jockey&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;I will have to tell the Service-Jockey that my bundle contains configuration for it, for that I'm adding a header to the Manifest:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: maroon;"&gt;Service-Jockey&lt;/span&gt;&lt;span style="color: black;"&gt;: META-INF/sj.xml&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;Lets run the Printer Consumer bundle  together with Service Jockey and my 'Printer Jockey' configuration bundle that contains the sj.xml file:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;id    State   Level Bundle&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;0 ACTIVE&amp;nbsp;  0     org.eclipse.osgi_3.5.1.R35x_v20090827&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;1 ACTIVE&amp;nbsp;  1     org.coderthoughts.servicejockey_0.0.1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;2 ACTIVE&amp;nbsp;  1     PrinterJockey_1.0.0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;3 ACTIVE  10    Printers_1.0.0&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;In my case the Printers bundle both registers and consumes the Printers, but that's because its a little test bundle. It's not really relevant. Note that the Printers bundle has a higher start level than the Jockey ones and therefore starts later.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;When I run my system again I can see that it's doing some work. Now my client reports these services:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;Printer:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;objectClass: [org.acme.Printer]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;service.id: 30&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;name: p1&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;location: b283&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;capabilities: [Double-sided]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;paper-size: [A3, A4]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;energy-rating: 75&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;.ServiceJockey: Proxied&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;Printer:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;objectClass: [org.acme.Printer]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;service.id: 32&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;name: p7&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;location: a12&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;capabilities: [Colour, Staple]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;paper-size: [A4, Letter]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;energy-rating: 50&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;.ServiceJockey: Proxied&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;Aha! I've got my energy rating in there! That's good. Remember that these are alternative registrations. The original ones haven't changed, they're just hidden.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt; Altering Client Side Lookups &lt;/span&gt;&lt;br /&gt;&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;Now I want my client to only use the one that is rated &amp;lt;= 50.  &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;That's done by adding another rule to the Service Jockey configuration. A rule that adds an extra filter to any matching lookup in a consumer.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span lang="en-GB"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;&lt;span lang="en-GB"&gt;service-jockey&lt;/span&gt;&lt;/span&gt;&lt;span lang="en-GB"&gt; &lt;/span&gt;&lt;span style="color: #7f007f;"&gt;&lt;span lang="en-GB"&gt;xmlns&lt;/span&gt;&lt;/span&gt;&lt;span style="color: black;"&gt;&lt;span lang="en-GB"&gt;=&lt;/span&gt;&lt;/span&gt;&lt;span style="color: #2a00ff;"&gt;&lt;span lang="en-GB"&gt;&lt;i&gt;"http://www.coderthoughts.org/schemas/sj/v1.0.0"&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: teal;"&gt;&lt;span lang="en-GB"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&lt;b&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;restrict-visibility&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp;    &lt;/span&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;rule&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &lt;b&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;bsn&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt;.*&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;bsn&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &lt;b&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;service-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt;(objectClass=org.acme.Printer)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;service-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt; &lt;/span&gt;&lt;/span&gt;&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&amp;nbsp; &lt;b&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;add-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;span style="color: black;"&gt;(energy-rating&lt;/span&gt;&lt;span style="color: #2a00ff;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: black;"&gt;=50)&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;add-filter&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp; &lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;&amp;lt;/rule&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;/restrict-visibility&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #3f7f7f;"&gt;proxy-registration&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;nbsp; ...&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&amp;nbsp;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;proxy-registration&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: teal;"&gt;&lt;span style="color: #3f7f7f;"&gt;/service-jockey&lt;/span&gt;&lt;span style="color: teal;"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;You can specify a regular expression to say what the Bundle Symbolic Name of the consumer bundle(s) is that the rule applies to. In my case .* matches anything. So it applies to any bundle that has OSGi Service consumers. By the way, there's also a &lt;span style="font-family: Courier New,monospace;"&gt;&lt;bver&gt;&lt;/bver&gt;&lt;/span&gt; tag.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="margin-bottom: 0in;"&gt;&lt;span lang="en-GB" style="font-size: small;"&gt;You specify to what services the additional filter applies with the &lt;/span&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Courier New,monospace;"&gt;&lt;span lang="en-GB"&gt;&lt;service-filter&gt; &lt;/service-filter&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span lang="en-GB"&gt;tag and you specify the additional filter in &lt;/span&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Courier New,monospace;"&gt;&lt;span lang="en-GB"&gt;&lt;add-filter&gt;&lt;/add-filter&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span lang="en-GB"&gt;. In my case I'm adding the &lt;/span&gt;&lt;span style="color: black;"&gt;&lt;span style="font-family: Courier New,monospace;"&gt;&lt;span lang="en-GB"&gt;(energy-rating&amp;lt;=50) &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span lang="en-GB"&gt;condition to any Printer Service returned to a consumer (the condition looks a bit dodgy because of the XML escaping of the '&amp;lt;' sign - you could use a CDATA section instead...).&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;Note that the Service Filter applies to any service returned to a service consumer, regardless of the filter used by the client itself looks like.  &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;Let's run the client again:&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;Printer:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;objectClass: [org.acme.Printer]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;service.id: 32&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;name: p7&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;capabilities: [Colour, Staple]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;location: a12&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;paper-size: [A4, Letter]&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;energy-rating: 50&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace; margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;span style="color: black;"&gt;.ServiceJockey: Proxied&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div align="left" lang="en-GB" style="margin-bottom: 0in;"&gt;&lt;span style="font-size: small;"&gt;Bingo - it only selects the service I wanted it to select. Based on additional properties and criteria listed in my Service Jockey configuration file. I did not have to modify the Printer Service registration bundle or the Consumer bundle...&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;&lt;br /&gt;Is it overkill?&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;Seems like a lot of work for just adding a service property, is there not a lighter way to at least add values to Service Registrations? Well at least that was my initial impression. However looking at the ServiceJockey bundle, it's only 14kb. Besides that there is currently no other way to achieve this...  &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size: small;"&gt;&lt;b&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt; Service Jockey - ride your service interactions&lt;/span&gt;&lt;/b&gt; &lt;/span&gt;&lt;br /&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;I didn't show any of the code to actually use the EventHook and the FindHook. Have a look at the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockey/src/org/coderthoughts/servicejockey/HidingEventHook.java"&gt;HidingEventHook&lt;/a&gt; and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockey/src/org/coderthoughts/servicejockey/HidingFindHook.java"&gt;HidingFindHook&lt;/a&gt; source code for that.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: small;"&gt;You can check out the source (Apache License) as an Eclipse Project &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockey"&gt;from SVN here&lt;/a&gt;. If you fancy modifying the code, I would recommend you also &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/ServiceJockeyTest"&gt;get the tests project&lt;/a&gt; and add new ones. The test bundle depend on the &lt;a href="http://code.google.com/p/mockito/"&gt;Mockito bundle&lt;/a&gt; for its mock objects...&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;  &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;I'm sure the Service Jockey isn't perfect, because I only wrote it during an airplane flight so feel free to send patches :)&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;I also put a &lt;a href="http://coderthoughts.googlecode.com/files/org.coderthoughts.servicejockey_0.0.1.jar"&gt;binary download of ServiceJockey here&lt;/a&gt;.&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: small;"&gt;Also in the source tree, Eclipse projects for the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/Printers"&gt;Printers&lt;/a&gt; and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/ServiceJockey/PrinterJockey"&gt;PrinterJockey&lt;/a&gt; example bundles. &lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div lang="en-GB"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-style: normal;"&gt;&lt;span style="font-weight: normal;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: x-small;"&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-3287136601858913109?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/3287136601858913109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=3287136601858913109' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/3287136601858913109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/3287136601858913109'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2009/11/altering-osgi-service-lookups-with.html' title='Altering OSGi Service Lookups with Service Registry Hooks'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JTmY6gtOOjs/SwPR2s5E6sI/AAAAAAAAARc/2HRdqC9jsnk/s72-c/img1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-4283149120371669809</id><published>2009-10-06T17:33:00.000-07:00</published><updated>2009-10-06T17:33:48.563-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c905'/><category scheme='http://www.blogger.com/atom/ns#' term='google calendar'/><title type='text'>A single Calendar from Desktop to Phone</title><content type='html'>This year I upgraded the really old smartphone I had to a Sony Ericsson C905. It almost looks like I'm one of the last people left who doesn't have an iPhone and doesn't want one (I'm resisting the voluntary lock-in ;).&lt;br /&gt;I'm really happy with the C905 because it has all the features I wanted (Wifi, GPS, 8MP camera, loads of memory) but it still looks like a phone and it's not too big.&lt;br /&gt;&lt;br /&gt;One problem I was starting to run into was that I had too many calendars going. I had the calendar in my email client (Thunderbird + &lt;a href="http://www.mozilla.org/projects/calendar/lightning/"&gt;Lightning&lt;/a&gt;), I had my Google Calendar and now the phone also has a calendar. I really wanted a single calendar that works on all of my devices. I had already been playing with the &lt;a href="https://addons.mozilla.org/en-US/thunderbird/addon/4631"&gt;Google Calendar Provider&lt;/a&gt; for Lightning and quite liked it. If only there was something like that for the phone...&lt;br /&gt;&lt;br /&gt;After a little digging I found out that Google supports the Exchange ActiveSync protocol. And my Sony Ericsson (and many other models too) supports it as well! It's not very well documented so I decided to write down the steps I took to get it all working.&lt;br /&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;1. Go to the Organiser and select &lt;i&gt;Synchronization&lt;/i&gt;.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvfXo-2mgI/AAAAAAAAAQs/u-7L9sDVOTg/s1600-h/screen1.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvfXo-2mgI/AAAAAAAAAQs/u-7L9sDVOTg/s200/screen1.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;2. Create a new Account.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SsvfZuRAhSI/AAAAAAAAAQ0/hJYT0u-c-xc/s1600-h/screen2.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SsvfZuRAhSI/AAAAAAAAAQ0/hJYT0u-c-xc/s200/screen2.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;3. Select &lt;i&gt;Exchange ActiveSync&lt;/i&gt; and give it a name, I called mine 'Google'.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvfbxrZ09I/AAAAAAAAAQ8/4HctwSqsIlE/s1600-h/screen3.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvfbxrZ09I/AAAAAAAAAQ8/4HctwSqsIlE/s200/screen3.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;4. In the &lt;i&gt;General&lt;/i&gt; tab, enter as the server address: https://m.google.com also provide your Google user name and password. I didn't provide any other information here.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/SsvfhI1ZwJI/AAAAAAAAARE/hxXDTK2Dkio/s1600-h/screen5.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/SsvfhI1ZwJI/AAAAAAAAARE/hxXDTK2Dkio/s200/screen5.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;5. On the applications tab I only selected &lt;i&gt;Calendar&lt;/i&gt;, although the Contancts and Email integration probably also work.&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvfigEOd0I/AAAAAAAAARM/RHXNnUQ0SKc/s1600-h/screen6.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvfigEOd0I/AAAAAAAAARM/RHXNnUQ0SKc/s200/screen6.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;6. Now it asks me to delete all my contacts &lt;i&gt;and&lt;/i&gt; calendar items. Very annoying because I don't want to sync my contacts, but anyway, for best results you have to clean them all. So I backed up my contacts previously and select clean here. I can restore my contacts later...&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/Ssvfkg_o_HI/AAAAAAAAARU/jwLwZcap5wM/s1600-h/screen7.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/Ssvfkg_o_HI/AAAAAAAAARU/jwLwZcap5wM/s200/screen7.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Ok - I'm all set up!&lt;br /&gt;Let's create a calendar event on the phone:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SsvdUccpsLI/AAAAAAAAAQE/MfXflQ4_D0c/s1600-h/picb.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SsvdUccpsLI/AAAAAAAAAQE/MfXflQ4_D0c/s200/picb.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Synchronize with Google...&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvdWo_aXWI/AAAAAAAAAQM/G-v19ZkYg74/s1600-h/pica.bmp" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvdWo_aXWI/AAAAAAAAAQM/G-v19ZkYg74/s200/pica.bmp" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;I think you can even do this automatically, but I haven't enabled that because I don't want my phone bill to explode :)&lt;br /&gt;&lt;br /&gt;Let's have a look at Google mail:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SsvemmHojdI/AAAAAAAAAQU/_-4pVPKB4ws/s1600-h/picc.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SsvemmHojdI/AAAAAAAAAQU/_-4pVPKB4ws/s400/picc.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;Yep, it's there! &lt;br /&gt;Now let's see in Thunderbird Lightning:&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_JTmY6gtOOjs/SsvfIgRX81I/AAAAAAAAAQk/eK210kOTSNI/s1600-h/picd.JPG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/SsvfIgRX81I/AAAAAAAAAQk/eK210kOTSNI/s400/picd.JPG" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;And in all three modes I have read/write access. Aaaaah, nice!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-4283149120371669809?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/4283149120371669809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=4283149120371669809' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/4283149120371669809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/4283149120371669809'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2009/10/single-calendar-from-desktop-to-phone.html' title='A single Calendar from Desktop to Phone'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JTmY6gtOOjs/SsvfXo-2mgI/AAAAAAAAAQs/u-7L9sDVOTg/s72-c/screen1.bmp' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-5994872353641221526</id><published>2009-07-07T02:38:00.000-07:00</published><updated>2009-07-07T03:08:06.377-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='IRC'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache Camel'/><title type='text'>An IRC alerter written using Apache Camel and Java</title><content type='html'>IRC is a great tool if you have a bunch of people working on the same thing in different locations. One of the biggest problems I found with it is that I sometimes miss pings. I don’t always have the IRC window open, and although the client I use most (Trillian) has nice popups in the corner, I still sometimes miss a ping, simply because I’m away from my computer. I know there are some other IRC clients that can send you special alerts, but I just happen to like Trillian, so I didn’t want to abandon it just because I was missing one little thing...&lt;br /&gt;&lt;br /&gt;While being annoyed with this for a little while, I came across the &lt;a href="http://camel.apache.org/"&gt;Apache Camel&lt;/a&gt; &lt;a href="http://camel.apache.org/components.html"&gt;IRC component&lt;/a&gt;. Great - that should sort me out. I’ll just simply write a Camel route that connects to the IRC channel and sends me an alert of some sort when someone pings me.&lt;br /&gt;&lt;br /&gt;After running the Camel Maven archetype for Java projects:&lt;br /&gt;&lt;span style="font-size:90%;font-family: courier new;"&gt;&amp;gt;mvn archetype:generate -DarchetypeGroupId=org.apache.camel.archetypes -DarchetypeArtifactId=camel-archetype-java -DarchetypeVersion=2.0-SNAPSHOT -DarchetypeRepository=https://repository.apache.org/content/groups/snapshots-group&lt;/span&gt;&lt;br /&gt;I was able to get this going fairly quickly! The archetype creates a Maven Camel project for you with an example route in it. You can run it now with:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:90%;font-family: courier new;"&gt;mvn install camel:run&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next step: write a Camel route that reads my IRC messages for me.&lt;br /&gt;For this I’m simply using a URL in Camel to connect to the IRC server, e.g. I’m using the following to connect to the CXF IRC channel at irc.codehaus.org:&lt;br /&gt;&lt;span style="font-size:90%;font-family: courier new;"&gt;irc://my_user@irc.codehaus.org/#cxf?nickname=my_user&lt;/span&gt;&lt;br /&gt;Once logged in you simply receive all the messages sent over the channel, in the &lt;span style="font-family: courier new;"&gt;process()&lt;/span&gt; method of your Camel route, so I can configure my route something like this:&lt;br /&gt;&lt;span style="font-size:90%;font-family: courier new;"&gt;public void configure() {        &lt;br /&gt;&amp;nbsp;&amp;nbsp;String url =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"irc://my_user@irc.codehaus.org/#cxf?nickname=my_user";&lt;br /&gt;&amp;nbsp;&amp;nbsp;from(url).process(new Processor() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void process(Exchange ex) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String msg = ex.getIn().toString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(msg);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;The &lt;span style="font-family: courier new;"&gt;process()&lt;/span&gt; callback gets called whenever a message is sent over the IRC channel, so now we can do something with it!&lt;br /&gt;&lt;br /&gt;This is where I started experimenting :) I wrote a little &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/irc-alerter/src/main/java/org/coderthoughts/camel_irc/MyRouteBuilder.java"&gt;generic IRC client&lt;/a&gt; that can connect to any number of IRC channels and searches any messages coming in for a certain text string.&lt;br /&gt;If there is a match, it opens a dialog on my screen that will stay there until I click it away, now I don’t miss it any more! I’m also using the new Java 6 tray icon functionality, to show a little popup balloon.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SlMZ38F5q1I/AAAAAAAAAPY/Gl6eL-rDScs/s1600-h/camel-dialog.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 268px; height: 128px;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SlMZ38F5q1I/AAAAAAAAAPY/Gl6eL-rDScs/s400/camel-dialog.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5355652830688029522" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SlMZ3eMvwDI/AAAAAAAAAPQ/G87uYQb3gyM/s1600-h/camel-tray.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 268px; height: 107px;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SlMZ3eMvwDI/AAAAAAAAAPQ/G87uYQb3gyM/s400/camel-tray.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5355652822663675954" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I put the code (under the Apache license) in SVN over here: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/irc-alerter"&gt;http://coderthoughts.googlecode.com/svn/trunk/irc-alerter&lt;/a&gt;. No built executables yet, but you can simply check it out and run it with the following command:&lt;br /&gt;&lt;span style="font-size:90%;font-family: courier new;"&gt;mvn install camel:run -Dirc.alerter.channels=c:\path\to\channels.xml&lt;/span&gt;&lt;br /&gt;since it’s pure java, it should work on mac, linux etc too :) I used Maven 2.0.9 and Java 6.&lt;br /&gt;&lt;br /&gt;The format of the channels.xml file is as follows:&lt;br /&gt;&lt;span style="font-size:90%;font-family: courier new;"&gt;&lt;br /&gt;&amp;lt;channels&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;match string="davidb"/&amp;gt; &amp;lt;!-- the text you want to match --&gt;&lt;br /&gt;&amp;nbsp;&amp;lt;password file="c:/davidb/etc/camel-irc.pwd"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;channel name="cxf" user="my_user" nick="my_user" host="irc.codehaus.org"/&amp;gt; &lt;br /&gt;&amp;nbsp;&amp;lt;channel name="somechannel" user="davidb" nick="davidb_camel" host="some_host" port="6667"/&amp;gt;&lt;br /&gt;&amp;lt;/channels&amp;gt;&lt;/span&gt;&lt;br /&gt;So this one matches any message that contains 'davidb'. Obviously you’d put your match in there.&lt;br /&gt;For each channel you specify the channel name, username, nickname, hostname and optionally the port. You probably want to use a different nick name than the one you use from your ordinary IRC client.&lt;br /&gt;The passwords for the various servers are in a separate file, which you can put in an encrypted directory in your file system. These are properties files, your camel-irc.pwd file could look like this:&lt;br /&gt;&lt;span style="font-size:90%;font-family: courier new;"&gt;&lt;br /&gt;my_user@irc.codehaus.org=somepass&lt;br /&gt;someuser@somehost=someotherpass&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;This has been a lot of fun to do. I’m still amazed how easy it was to create this app using Camel, and I’ll never miss an IRC ping ever again! (Well, I hope).&lt;br /&gt;&lt;br /&gt;Source code - can be checked out from Subversion here: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/irc-alerter"&gt;http://coderthoughts.googlecode.com/svn/trunk/irc-alerter&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-5994872353641221526?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/5994872353641221526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=5994872353641221526' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5994872353641221526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5994872353641221526'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2009/07/irc-alerter-written-using-apache-camel.html' title='An IRC alerter written using Apache Camel and Java'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JTmY6gtOOjs/SlMZ38F5q1I/AAAAAAAAAPY/Gl6eL-rDScs/s72-c/camel-dialog.JPG' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-5207912974473736013</id><published>2009-05-08T02:33:00.000-07:00</published><updated>2009-05-08T14:13:30.781-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='distributed osgi'/><category scheme='http://www.blogger.com/atom/ns#' term='rfc 119'/><category scheme='http://www.blogger.com/atom/ns#' term='apache cxf'/><title type='text'>Questions from the RFC 119 webinar</title><content type='html'>There were quite a number of questions on the webinar and I didn't get to answer all of them, so I'm providing some answers here. For those of you who missed the webinar, you can still view &lt;a href="http://fusesource.com/require_registration_for?url=http://download.progress.com/open/adobe/prc/psc/042809_fuse_osgi_webinar/index.htm"&gt;the recorded version of the Distributed OSGi webinar here&lt;/a&gt;.&lt;br /&gt; The source code of the GreeterDemo that I used during the demo session can be found in the Apache CXF demos SVN here: &lt;a href="http://svn.apache.org/repos/asf/cxf/dosgi/trunk/samples/greeter"&gt; http://svn.apache.org/repos/asf/cxf/dosgi/trunk/samples/greeter&lt;/a&gt; for details on how to run the demo see the &lt;a href="http://cxf.apache.org/distributed-osgi-greeter-demo-walkthrough.html"&gt;demo walkthrough page&lt;/a&gt; on the CXF Distributed OSGi website.&lt;br /&gt;&lt;br /&gt;Below are the questions asked during the webinar, and an attempt to answer them. Feel free to send comments if I missed anything…&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Does it use RMI?&lt;/span&gt;&lt;br /&gt;A: The wire-protocol and data-binding used are not specified by Distributed OSGi. They are up to the implementation. The Apache CXF implementation used in the demo uses SOAP/HTTP but other implementations could be written to use other wire protocols, such as RMI.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Why don't you use JaxWS?&lt;/span&gt;&lt;br /&gt;A: It's all about choice for the developer. JaxWS is a specific programming model for Web Services in Java. Distributed OSGi is for those who like the OSGi Services programming model, and would like to stay within that programming model to distribute OSGi services. It's not incompatible with other programming models, such as JaxWS, JaxRS, CORBA etc and can be used alongside them.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can I used Distributed OSGi services from Visual Basic?&lt;/span&gt;&lt;br /&gt;A: Yes you can! Visual Basic programs can invoke on Web Services that are defined by WSDL. If you use the Apache CXF Distributed OSGi implementation it will generate a WSDL for your OSGi service which you can use from your Visual Basic client.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Is there an implementation of Distributed OSGi available in Open Source?&lt;/span&gt;&lt;br /&gt;A: Yes, the &lt;a href="http://cxf.apache.org/distributed-osgi.html"&gt;Apache CXF implementation&lt;/a&gt;, &lt;a href="http://www.eclipse.org/ecf/"&gt;Eclipse ECF implementation&lt;/a&gt; and &lt;a href="http://tuscany.apache.org/"&gt;Apache Tuscany implementation&lt;/a&gt; are all open source and freely available to everyone.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can I use Distributed OSGi services from an Ajax browser client?&lt;/span&gt;&lt;br /&gt;A: Yes, this is possible. I wrote an example of this on this blog a little while ago, see here: &lt;a href="http://coderthoughts.blogspot.com/2009/02/distributed-osgi-powered-ajax-webapp.html"&gt;http://coderthoughts.blogspot.com/2009/02/distributed-osgi-powered-ajax-webapp.html&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can i use cxf with equinox, specifically pre 3.5?&lt;/span&gt;&lt;br /&gt;A: The short answer is no, but you can use the current betas of Equinox 3.5 (builds since December). Any implementation of Distributed OSGi will need the Service Registry Hooks. Since this is a new API, it is only available in Equinox 3.5 and Apache Felix 1.4.1 or newer.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Why is it based on webservices?&lt;/span&gt;&lt;br /&gt;A: The design of Distributed OSGi is not based on Web Services and you may find implementations that don't use web services at all. The Apache CXF implementation is based on Web Services. Web services provide a standards-based approach for distributing software and allow integration with other (non-OSGi) systems. Although not required per se by an implementation of RFC 119, ‘Legacy integration' is one of the use cases that drove the design of Distributed OSGi, and by using Web Services this use-case can be addressed.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: You are using CXF for cross-jvm/osgi communication and mentioned that the rfc does not define the communication protocol. Does that mean that it is possible to implement a custom communication protocol?&lt;/span&gt;&lt;br /&gt;A: Absolutely!&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: How can I specify which protocol(s) to be used for some remote service?&lt;/span&gt;&lt;br /&gt;A: Use intents mechanism for that. The intents mechanism allows you to declaratively specify constraints on the remote service. In short, you can put a property on the service that you want to remote that declares using what binding/protocol you the service should be exposed. So you could do something like: osgi.remote.requires.intents=JMS which means that the service should only be exposed over JMS. If the Distribution Software (DSW) can't satisfy the intent, it will not be able to expose the service. Although some values (for SOAP and JMS) have been defined in the RFC for this property, the actual possible values depend on the Distribution Software implementation used.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: How do I set the discovery service dynamically without the remote-services.xml?&lt;/span&gt;&lt;br /&gt;A: Use an RFC 119 compliant Discovery implementation, these don't use the remote-services.xml files. We're currently working on one in the Apache CXF codebase that is based on Apache Zookeeper, but other implementations, such as the one based on SLP at Eclipse ECF are also available. &lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can I specify more than one protocol for one service? For example Web Services, CORBA, RMI, etc.&lt;/span&gt;&lt;br /&gt;A: If you specify the protocol to use using the osgi.remote.requires.intents mechanism as described above, you can only specify one. E.g. specifying that the service has to satisfy &lt;i&gt;both&lt;/i&gt; the RMI and CORBA intent at the same time is a contradiction, however you can register the same POJO twice with the OSGi Service registry, once for each protocol. &lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Is Discovery service a separate OSGi container?&lt;/span&gt;&lt;br /&gt;The RFC 119 Discovery architecture typically uses a centralized Discovery Server (potentially a replicated one, like we have with the CXF/Zookeeper implementation). The Discovery Service in the OSGi container provides a standardized interface to such a Discovery Server. The Distribution Software (DSW) interacts with the Discovery Service in the local OSGi service registry, which then communicates with the centralized Discovery Server. &lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Service Registry hooks- limiting visibility of service-This is similar to access control the service- what are your views on this? Thanks&lt;/span&gt;&lt;br /&gt;A: I forwarded this question to BJ Hargrave from IBM, the main author of the Service Registry Hooks RFC, he provided the following answer: "It is more about controlling visibility to services (but I guess some people may call that access [see http://blog.bjhargrave.com/2009/03/i-am-visible-but-am-i-accessible.html]). But the primary purpose is not to secure the system since without a Security Manager installed, there is no actual security. Its primary purpose is to control service visibility to influence the services a bundle sees so that it selects a desired service rather than another service. For example, the proxy for X rather than X. "&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: so if they [OSGi Services] don't implement any signature, how are they identified within the registry?&lt;/span&gt;&lt;br /&gt;A: OSGi Services don't need to implement a particular OSGi signature or interface, but they would typically implement an interface that relates to their purpose. In the presentation I mentioned a Purchase Service, which implements a foo.bar.PurchaseService interface. Normally services are looked up via their interface, but you can also look up services in the OSGi Service Registry purely on other properties that they might have.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: When you say 'Dynamically come and go', can you elaborate on that what does that really mean?&lt;/span&gt;&lt;br /&gt;A: Example: A running OSGi system. A consumer in that system might be using a service that satisfies its lookup criteria. While the system is running another service that also satisfies these criteria could arrive (either because a new bundle that creates this service is installed or because an existing bundle registers a new service). Depending on the consumer's requirements, it could now potentially be using both services, the old and the new one. At a later time one of the services might go away at which point the consumer will only be using the one that's left. All (properly developed) OSGi service consumers are capable of dealing with dynamics like this.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: is the WSDL, automatically generated by some tool?&lt;/span&gt;&lt;br /&gt;A: Yes, the Apache CXF DOSGi implementation does this dynamically at runtime, based to the Java interface of the service. You don't have to run a tool for this with CXF, although other implementations might require the running of a tool to achieve something like this.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: What does pojo mean?&lt;/span&gt;&lt;br /&gt;A: Plain Old Java Object.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: I suppose that the parameters of the service exposed  are of type 'primitive' like int, string, double, DateTime but cannot be of type by example SessionFactory ?&lt;/span&gt;&lt;br /&gt;A: It depends on the DOSGi implementation used. If you want your code to be suitable for a number of DOSGi implementations it's best to restrict the use of data types to primitives, collections and arrays. The CXF DOSGi implementation also supports classes and interfaces as part of the signature as long as they in turn are defined in terms of the data types mentioned.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can you highlight the security capabilities that are provided by distributed OSGi?&lt;/span&gt;&lt;br /&gt;and &lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can we secure the communication between remote OSGI servers (and authenticate, authorise access to the services)?&lt;/span&gt;&lt;br /&gt;A: Security is a Quality of Service that DOSGi implementations could provide. The intents mechanism can be used to only select remote services that are secure, e.g. by requesting the ‘confidentiality' intent, or by requesting a custom intent (such as AcmeSecurity) that your deployer defines with the appropriate security configuration. For more information see chapter 5.5.3 of RFC 119 (in &lt;a href= "http://www.osgi.org/download/osgi-4.2-early-draft3.pdf"&gt;http://www.osgi.org/download/osgi-4.2-early-draft3.pdf&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: How do local discovery services know about other discovery services?&lt;/span&gt;&lt;br /&gt;A: The Discovery Service registered in the OSGi container communicates with a centralized Discovery Server. This Discovery Service needs to be configured with the connection details of the central discovery server.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: what implementations of distributed OSGi will be available in Equinox 3.5?  Anything faster than webservices?&lt;/span&gt;&lt;br /&gt;A: Equinox 3.5 does not have any Distributed OSGi implementations, but Eclipse 3.5 provides the ECF implementation. I guess I'm wondering what really the performance difference is that you are looking for. Have you measured the performance of CXF webservices versus something else? Could you provide us with some target figures? I know that CXF performs pretty well…&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can you specify multiple instance endpoints for the same service?&lt;/span&gt;&lt;br /&gt;A: On the server side one remote service typically maps to one remote endpoint (although exceptions are possible, especially in case the service implements multiple interfaces some implementations will give you one endpoint per interface). If you want more endpoints you can always register more OSGi services for the same POJO (although I'm not sure what the use-case for that would be). &lt;br /&gt;On the consumer side, the consumer can be informed of multiple remote endpoints satisfying the OSGi Service Lookup criteria, in which case the consumer could get multiple client-side proxies to the remote services. &lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can you talk more about discovery with ZooKeeper and how that would work?&lt;/span&gt;&lt;br /&gt;A: In the CXF project I started implementing the RFC 119 Discovery implementation using Apache ZooKeeper as the underlying Discovery mechanism. ZooKeeper is based on a replicated central server that holds a virtual file system. Consumers connecting to the ZooKeeper server will all view the same filesystem which holds the discovery information. The way this works in practice is that you have your cluster (1 or more) of zookeeper servers running. The CXF Discovery implementation bundle is installed in your OSGi containers. It will communicate with the ZooKeeper server for the discovery information. The net effect is that the remote service consumers will see the remote services without needing any extra configuration.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Question : Is there other org.osgi.remote.type foreseen (e.g. POJO) ? What is the particularity of the type 'POJO' except its name ?&lt;/span&gt;&lt;br /&gt;A: The POJO configuration type is specific to CXF. There is one configuration type specified in RFC 119: SCA, which is optional. Using SCA as a way to configure Distributed OSGi will give you portability on the configuration level. CXF does not implement the SCA configuration type yet…&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Question : Will it be possible using 'blueprint or spring DM' to integrate remote OSGI with Spring XML file ?&lt;/span&gt;&lt;br /&gt;A: Yes - there is a demo that comes with the CXF DOSGi implementation that uses Spring-DM to create a remote OSGi service and on the consumer side it uses Spring-DM to create the consumer. Distributed OSGi is orthogonal to any OSGi component model and should therefore work fine with any of them. See here for the demo code: &lt;a href="http://svn.apache.org/repos/asf/cxf/dosgi/trunk/samples/spring_dm"&gt;http://svn.apache.org/repos/asf/cxf/dosgi/trunk/samples/spring_dm&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: is it possible to load balance across distributed OSGi bundles?&lt;/span&gt;&lt;br /&gt;A: Yes – if multiple remote instances exist the consumer could be informed about all of them: the consumer will get a service proxy for each remote instance. The consumer can use these to implement some load-balancing algorithm.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Why does a copy of the interface and class have to live on the client?&lt;/span&gt;&lt;br /&gt;A: Only the interface needs to be available to the service consumer. The implementation class does not. I think this is a very natural thing to do. If I write a consumer to a service, like a PurchaseService, I need to know the interface to that PurchaseService, how am I otherwise going to use this in a meaningful way? The interface is the &lt;i&gt;contract&lt;/i&gt; between the consumer and the service provider. This is also the case for local OSGi Services. They can't use an OSGi service if they don't have access to the interface.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: It seems like you are cheating if you get a copy of the interface and class in your client code prior to starting the demo.&lt;/span&gt;&lt;br /&gt;A: No I'm not cheating, only the interface is present in the consumer. When I run the consumer I can see that the value I'm entering in the consumer running in Felix is echoed in the Equinox server window. The implementation resides in Equinox.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Does distributed OSGi support multiple instance of the same service and thus software based failover rof services across remote services?&lt;/span&gt;&lt;br /&gt;A: Yes, see above.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: for properties is there a namespace type construct to prevent collisions? or is it inherent based on the scope of a service?&lt;/span&gt;&lt;br /&gt;A: No there isn't really a namespace (although the Distributed OSGi properties use the osgi. prefix), but it is good practice to qualify your own properties with your own organization just like a Java package, e.g. com.acme.myproperty to avoid clashes.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Are there standard properties of services, like names, that every OSGI service should define?&lt;/span&gt;&lt;br /&gt;A: No. Its absolutely fine to register a service with no properties. OSGi will typically add a few properties, like service.id and objectClass but as a service provider you don't need to worry about these.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: How does a Discovery service, choose if the service is deployed both locally and remotely?&lt;/span&gt;&lt;br /&gt;A: If a good match is available locally, DOSGi will not look for remote services. If you want to influence your match, forcing either a local or a remote match you can refine your lookup by adding extra criteria in the LDAP filter.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can the contract by dynamically found and transmitted remotely?&lt;/span&gt;&lt;br /&gt;A: You could write a system to do this, as bundles can be installed at any time in a running OSGi system, but the Distributed OSGi standard doesn't define this.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: With multiple instances of the same service, would they have the same endpoint, or could they have different endpoints?  How would a discovery pick which one?&lt;/span&gt;&lt;br /&gt;A: The will have different endpoints. Everything being equal Discovery will simply inform you about all of them. If your consumer only needs to use one of them, which one the consumer will get is undefined. If the consumer has a preference for a particular one it needs to refine its filter to only match that one. You can also have the consumer receive proxies to all remote instances.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Can you turn existing code (POJOs) into distributed OSGI services just using configuration (i.e. no java re-coding)?&lt;/span&gt;&lt;br /&gt;A: Yes, OSGi Blueprint/Spring-DM provide a lot of support here. You configure them with an XML file that will instantiate your POJO and register it as an OSGi service.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Are you familiar with the Paremus fabric, distributed OSGi runtime that uses SCA as the "wire-up" model? If so, does it use SCA in the way you described?&lt;/span&gt;&lt;br /&gt;A: I'm not really familiar with this product, but I have seen demos of it. The SCA part of RFC 119 is really about defining qualities of service for the distributed services. It's not used for wiring services, the OSGi Service registry is used for that.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Will both local and remote services be returned from a search?&lt;/span&gt;&lt;br /&gt;A: There is no difference in the API to use to obtain local services or remote services. However, if appropriate local matches are available, the system won't look for remote services. If you want all local and remote matches you can do lookups with and without the osgi.remote property. The osgi.remote property is only set on proxies for remote services.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(255, 153, 102); font-weight: bold;" &gt;Q: Are there any drawbacks, using OSGI distributed services that you may know of?&lt;/span&gt;&lt;br /&gt;A: None :) Seriously, yes remote computing is not the same as local computing. There are additional failure scenarios, additional latency, additional security impacts and so on. So if your system works perfect with all your services locally, keep it that way. However, if you want to distribute your OSGi services, then RFC 119 is a good way to do it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-5207912974473736013?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/5207912974473736013/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=5207912974473736013' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5207912974473736013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5207912974473736013'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2009/05/questions-from-rfc-119-webinar.html' title='Questions from the RFC 119 webinar'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-2514390053129282700</id><published>2009-04-21T07:49:00.000-07:00</published><updated>2009-04-21T07:52:49.566-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='distributed osgi'/><category scheme='http://www.blogger.com/atom/ns#' term='apache cxf'/><title type='text'>Webinar and demo of Distributed OSGi (RFC 119) on Tue April 28</title><content type='html'>Just to let y'all know... I'll be doing a webinar and demo of Distributed Services OSGi on Tuesday April 28. &lt;br /&gt;Distributed OSGi is a major new feature in the OSGi 4.2 specification. For more info, see &lt;a href="http://fusesource.com/resources/video-archived-webinars"&gt;http://fusesource.com/resources/video-archived-webinars&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The demo will be done using the Apache CXF Reference Implementation.&lt;br /&gt;Registration and attendance is free :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-2514390053129282700?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/2514390053129282700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=2514390053129282700' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/2514390053129282700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/2514390053129282700'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2009/04/webinar-and-demo-of-distributed-osgi.html' title='Webinar and demo of Distributed OSGi (RFC 119) on Tue April 28'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-2729769142800579666</id><published>2009-02-09T08:00:00.000-08:00</published><updated>2009-02-09T08:34:42.056-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='distributed osgi'/><category scheme='http://www.blogger.com/atom/ns#' term='rfc 119'/><category scheme='http://www.blogger.com/atom/ns#' term='apache cxf'/><title type='text'>A Distributed OSGi Powered AJAX WebApp</title><content type='html'>A pretty cool new way of using Distributed OSGi services is from a non-OSGi environment. In the previous post I used an OSGi AuctionService from an OSGi AuctionConsumer in a different VM. &lt;br /&gt;This posting is about using that OSGi service from a non-OSGi consumer. From an AJAX-powered web application that's running in the browser!&lt;br /&gt;&lt;br /&gt;I'm writing the webapp using the Google Web Toolkit (GWT). Both the webapp as well as my OSGi service are hosted in the same OSGi container. With AJAX you can create a rich client application running inside the browser. So no Servlet/JSP style server-side execution. The webapp runs on the client machine. GWT makes creating the AJAX app easy: you write them in plain old Java. No need to sidestep into Javascript as GWT turns the Java into a Javascript AJAX app and all (well, almost) of the browser specific madness is taken care of by GWT too. &lt;br /&gt;&lt;br /&gt;As with any website, the end-users users simply open it in their browser. No client install, no browser plugins. Any modern browser supports this stuff natively. Remote communication, such as obtaining a list of available items, listing a new item, placing a bid, is done via SOAP/HTTP calls directly on my OSGi AuctionService (the one from &lt;a href="http://coderthoughts.blogspot.com/2009/02/distributed-osgi-simple-example.html"&gt;the previous posting&lt;/a&gt;) which has been remoted using the &lt;a href="http://cwiki.apache.org/confluence/display/CXF/Distributed+OSGi"&gt;Apache CXF-based Distributed OSGi Reference Implementation&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;To do all this I'm adding a component called &lt;i&gt;Pax Web Extender&lt;/i&gt; to my OSGi container, on top of the Distributed OSGi bundles. Pax Web Extender turns the OSGi container into a web container which means that I can deploy a .WAR file into it. It internally leverages the OSGi HTTP Service. (Spring DM Server can do this too BTW).&lt;br /&gt;&lt;br /&gt;My OSGi container is configured like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SYcaZK1ArNI/AAAAAAAAAOI/dnkHOhklfsc/s1600-h/osgi_blog2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 206px;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SYcaZK1ArNI/AAAAAAAAAOI/dnkHOhklfsc/s400/osgi_blog2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5298232506330426578" /&gt;&lt;/a&gt;&lt;br /&gt;Besides the CXF Distributed OSGi and Pax-Web-Extender Bundles I have my AuctionService and AuctionInterface bundles deployed (see previous posting for the details), plus the .WAR file that holds my AJAX webapp.&lt;br /&gt;&lt;br /&gt;The reason I can invoke my OSGi Service is because it was made available remotely over a standards-based mechanism. Distributed OSGi itself doesn't prescribe how you do your remoting, it only says that you can use standards-based wire protocols and bindings if you wish and that this can make OSGi Services available to non-OSGi consumers.&lt;br /&gt;&lt;br /&gt;The CXF implementation of Distributed OSGi turns an OSGi Service into a Web Service that is accessible via SOAP/HTTP. GWT can already natively make remote HTTP invocations. I didn't actually even care looking for a SOAP library for GWT. SOAP is just XML and GWT has some pretty good XML parsing functionality. &lt;br /&gt;The last thing that I need to worry about (besides security, but that's a separate topic) is the browser's single-origin policy. But hey: I've deployed my webapp in the same runtime as my OSGi Service so as long as I make them available over the same port there's no problem! I achieved this by sharing the same OSGi HTTP Service by both the Distributed OSGi runtime and the Pax Web Extender. Thanks to the OSGi Services architecture!&lt;br /&gt;&lt;br /&gt;So I'm taking the AuctionService of the previous posting, but first of all I want to expose it on a slightly simpler URL. The default URL of http://localhost:9000/org/coderthoughts/auction/AuctionService is just a bit too long, let's use /auction as the context instead. I also want to make it use the OSGi HTTP Service (which CXF doesn't do by default). I can do both in one shot by setting the  &lt;span style="font-size:85%;font-family: courier new;"&gt;osgi.remote.configuration.pojo.httpservice.context&lt;/span&gt; service property to the value /auction. The HTTP Service that comes with CXF runs by default on port 8080 (you can change this by setting the &lt;span style="font-size:85%;font-family: courier new;"&gt;org.osgi.service.http.port&lt;/span&gt; system property) so my Web Service now runs on http://localhost:8080/auction. Change the activator that registers the service to the following:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;public class Activator implements BundleActivator {&lt;br /&gt;&amp;nbsp;&amp;nbsp;private ServiceRegistration sr;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void start(BundleContext context) throws Exception {        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dictionary props = new Hashtable();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;props.put("osgi.remote.interfaces", "*");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;props.put("osgi.remote.configuration.type", "pojo");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;props.put("osgi.remote.configuration.pojo.httpservice.context",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"/auction");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sr = context.registerService(AuctionService.class.getName(), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new AuctionServiceImpl(), props);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void stop(BundleContext context) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sr.unregister();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;To double check that the service is operational on the new address, simply open http://localhost:8080/auction?wsdl and see if the WSDL appears again (&lt;a href="http://1.bp.blogspot.com/_JTmY6gtOOjs/SYcSGF6femI/AAAAAAAAAOA/IBoboNiHDDU/s1600-h/wsdl.jpeg"&gt;just like in the previous posting&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;I won't go into all the detail of writing the GWT app here. Look at the code in &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/gwt_auctionsite"&gt;the SVN project&lt;/a&gt; for all of that. &lt;br /&gt;I constructed my GWT application the usual way by creating a starting point with the projectCreator and applicationCreator scripts that come with GWT. This creates a nice Eclipse project skeleton for GWT from which I can start hacking my AJAX site in Plain Old Java :)&lt;br /&gt;&lt;br /&gt;All you need to do in the GWT app to invoke on the Distributed OSGi service is:&lt;ul&gt;&lt;li&gt;figure out where it's running, since the Web Service is running on the same host and port as where the AJAX app is served from on the /auction context, I can construct the Web Service URL as follows:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;webServiceURL = "http://" + Window.Location.getHost() + "/auction/";&lt;/span&gt;&lt;br /&gt;&lt;li&gt;create a SOAP message, just a bit of XML really.&lt;br /&gt;&lt;li&gt;send it via a HTTP POST request to the WebService using a &lt;span style="font-size:85%;font-family: courier new;"&gt;com.google.gwt.http.client.RequestBuilder&lt;/span&gt; that comes with GWT.&lt;br /&gt;&lt;li&gt;register a callback object to process the SOAP (XML) response. &lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;All of this happens in the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/gwt_auctionsite/AuctionSite/src/org/coderthoughts/osgi/client/AuctionSite.java"&gt;AuctionSite class&lt;/a&gt; which is the webapp's main class. There are a few more classes such as data classes and a few dialogs. Check out the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/gwt_auctionsite"&gt;project from SVN&lt;/a&gt; to see them all...&lt;br /&gt;&lt;br /&gt;To prepare my webapp (.WAR file) for running with the Pax Web Extender I'm turning it into an OSGi bundle by adding the following &lt;span style="font-size:85%;font-family: courier new;"&gt;META-INF/MANIFEST.MF&lt;/span&gt; file:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;Manifest-Version: 1.0&lt;br /&gt;Bundle-ManifestVersion: 2&lt;br /&gt;Bundle-SymbolicName: AuctionSite&lt;br /&gt;Bundle-Version: 1.0.0&lt;br /&gt;Webapp-Context: auctionsite&lt;/span&gt;&lt;br /&gt;The Webapp-Context is important as this tells the Pax Web Extender where to register the webapp.&lt;br /&gt;&lt;br /&gt;I also have to add a &lt;span style="font-size:85%;font-family: courier new;"&gt;WEB-INF/web.xml&lt;/span&gt; file to the .WAR as this is the signature file that the Pax-Web Extender needs in order to spot a bundle as a webapp. However, I don't really need to put anything in it. My webapp consists only of static files (no servlets etc.) as far as the webserver is concerned, so my web.xml is simply:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;&amp;lt;web-app&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;display-name&amp;gt;AuctionSite&amp;lt;/display-name&amp;gt;&lt;br /&gt;&amp;lt;/web-app&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;GWT comes with scrips that you run to turn your Java into AJAX.&lt;br /&gt;I wrote a &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/gwt_auctionsite/AuctionSite/build.xml"&gt;tiny little ant script&lt;/a&gt; that calls the GWT script and then creates a .WAR file from my GWT project in Eclipse. &lt;br /&gt;&lt;br /&gt;Now lets boot is all up! I'm using Equinox 3.5M5 with the Multi Bundle Distribution of CXF-DOSGi.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;To install the Multi Bundle Distribution of CXF-DOSGi, &lt;a href="http://people.apache.org/repo/m2-snapshot-repository/org/apache/cxf/dosgi/cxf-dosgi-ri-multibundle-distribution/1.0-SNAPSHOT/cxf-dosgi-ri-multibundle-distribution-1.0-SNAPSHOT.zip"&gt;download it from here&lt;/a&gt;. Then unzip it in your Felix/Equinox installation dir.&lt;br /&gt;Take the contents of the &lt;b&gt;felix.config.properties.append&lt;/b&gt; or &lt;b&gt;equinox.config.ini.append&lt;/b&gt; and append it to the configuration file of your OSGi container.&lt;br /&gt;This will automatically load all the DOSGi bundles when starting up the OSGi container.&lt;br /&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Start Equinox with the DOSGi Multi Bundle configuration to make it load all the DOSGi bundles at startup.&lt;br /&gt;&lt;li&gt;Then install and start the &lt;a href="http://repository.ops4j.org/maven2/org/ops4j/pax/web-extender/pax-web-ex-war/0.4.0/pax-web-ex-war-0.4.0.jar"&gt;PAX Web Extender from here&lt;/a&gt; and your AuctionIntf and AuctionImpl bundles.&lt;br /&gt;&lt;li&gt;Finally install and start the AuctionSite.war file, just like any other bundle in OSGi.&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;You should now have the following bundles running in Equinox:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;osgi&gt; ss&lt;br /&gt;Framework is launched.&lt;br /&gt;id      State       Bundle&lt;br /&gt;0       ACTIVE      org.eclipse.osgi_3.5.0.v20090127-1630&lt;br /&gt;1       ACTIVE      org.eclipse.osgi.services_3.2.0.v20081205-1800&lt;br /&gt;2       ACTIVE      org.apache.geronimo.specs.geronimo-annotation_1.0_spec_1.1.1&lt;br /&gt;3       ACTIVE      org.apache.geronimo.specs.geronimo-activation_1.1_spec_1.0.2&lt;br /&gt;4       ACTIVE      org.apache.geronimo.specs.geronimo-javamail_1.4_spec_1.2.0&lt;br /&gt;5       ACTIVE      org.apache.geronimo.specs.geronimo-ws-metadata_2.0_spec_1.1.2&lt;br /&gt;6       ACTIVE      com.springsource.org.apache.commons.logging_1.1.1&lt;br /&gt;7       ACTIVE      com.springsource.org.jdom_1.0.0&lt;br /&gt;8       ACTIVE      org.springframework.bundle.spring.core_2.5.5&lt;br /&gt;9       ACTIVE      org.springframework.bundle.spring.beans_2.5.5&lt;br /&gt;10      ACTIVE      org.springframework.bundle.spring.context_2.5.5&lt;br /&gt;11      ACTIVE      com.springsource.org.aopalliance_1.0.0&lt;br /&gt;12      ACTIVE      org.springframework.bundle.spring.aop_2.5.5&lt;br /&gt;13      ACTIVE      org.springframework.bundle.osgi.io_1.1.2&lt;br /&gt;14      ACTIVE      org.springframework.bundle.osgi.core_1.1.2&lt;br /&gt;15      ACTIVE      org.springframework.bundle.osgi.extender_1.1.2&lt;br /&gt;16      ACTIVE      org.ops4j.pax.web.service_0.5.1&lt;br /&gt;17      ACTIVE      org.apache.servicemix.specs.locator-1.1.1&lt;br /&gt;18      ACTIVE      org.apache.servicemix.bundles.jaxb-impl_2.1.6.1&lt;br /&gt;19      ACTIVE      org.apache.servicemix.bundles.wsdl4j_1.6.1.1&lt;br /&gt;20      ACTIVE      org.apache.servicemix.bundles.xmlsec_1.3.0.1&lt;br /&gt;21      ACTIVE      org.apache.servicemix.bundles.wss4j_1.5.4.1&lt;br /&gt;22      ACTIVE      org.apache.servicemix.bundles.xmlschema_1.4.2.1&lt;br /&gt;23      ACTIVE      org.apache.servicemix.bundles.asm_2.2.3.1&lt;br /&gt;24      ACTIVE      org.apache.servicemix.bundles.xmlresolver_1.2.0.1&lt;br /&gt;25      ACTIVE      org.apache.servicemix.bundles.neethi_2.0.4.1&lt;br /&gt;26      ACTIVE      org.apache.servicemix.bundles.woodstox_3.2.7.1&lt;br /&gt;27      ACTIVE      org.apache.cxf.cxf-bundle-minimal_2.2.0.SNAPSHOT&lt;br /&gt;28      ACTIVE      org.apache.servicemix.specs.saaj-api-1.3_1.1.1&lt;br /&gt;29      ACTIVE      org.apache.servicemix.specs.stax-api-1.0_1.1.1&lt;br /&gt;30      ACTIVE      org.apache.servicemix.specs.jaxb-api-2.1_1.1.1&lt;br /&gt;31      ACTIVE      org.apache.servicemix.specs.jaxws-api-2.1_1.1.1&lt;br /&gt;32      ACTIVE      cxf-dosgi-ri-discovery-local_1.0.0.SNAPSHOT&lt;br /&gt;33      ACTIVE      cxf-dosgi-ri-dsw-cxf_1.0.0.SNAPSHOT&lt;br /&gt;34      ACTIVE      org.ops4j.pax.web.extender.war_0.4.0&lt;br /&gt;35      ACTIVE      AuctionIntf_1.0.0&lt;br /&gt;36      ACTIVE      AuctionImpl_1.0.0&lt;br /&gt;37      ACTIVE      AuctionSite_1.0.0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Open the webapp at &lt;a href="http://localhost:8080/auctionsite/AuctionSite.html"&gt;http://localhost:8080/auctionsite/AuctionSite.html&lt;/a&gt;. This kicks off the AJAX app in your browser. When you hit the reload button it makes a SOAP/HTTP invocation on the server to list all the items and get their details. The SOAP messages it sends map to the &lt;span style="font-size:85%;font-family: courier new;"&gt;AuctionService.getItemIDs()&lt;/span&gt; and &lt;span style="font-size:85%;font-family: courier new;"&gt;AuctionService.getItems()&lt;/span&gt; APIs. (At the bottom of the screen you see the SOAP message as it comes back form the server)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/SZBRsr7sQgI/AAAAAAAAAOY/DKvM3cx71mc/s1600-h/step1.jpeg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 259px;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/SZBRsr7sQgI/AAAAAAAAAOY/DKvM3cx71mc/s400/step1.jpeg" border="0" alt=""id="BLOGGER_PHOTO_ID_5300826589564256770" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Let's list a new item...&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/SZBRs8AfizI/AAAAAAAAAOg/tgDlcY_FGtg/s1600-h/step2.jpeg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 259px;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/SZBRs8AfizI/AAAAAAAAAOg/tgDlcY_FGtg/s400/step2.jpeg" border="0" alt=""id="BLOGGER_PHOTO_ID_5300826593879362354" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After filling in the Javascript popup, hit OK.&lt;br /&gt;It now sends the following SOAP message from the browser to the server:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;&amp;lt;soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;soap:Body&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;ns1:listNewItem xmlns:ns1="http://auction.coderthoughts.org/"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ns1:arg0&amp;gt;Joke&amp;lt;/ns1:arg0&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ns1:arg1&amp;gt;1000&amp;lt;/ns1:arg1&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;ns1:arg2&amp;gt;2009-02-09T15:48:22-00:00&amp;lt;/ns1:arg2&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/ns1:listNewItem&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/soap:Body&amp;gt;&lt;br /&gt;&amp;lt;/soap:Envelope&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Once the remote invocation has completed, the list of items is updated again, which is the same as hitting the reload button.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SZBRszjyi8I/AAAAAAAAAOo/PgKEC7s0Wfc/s1600-h/step3.jpeg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 259px;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SZBRszjyi8I/AAAAAAAAAOo/PgKEC7s0Wfc/s400/step3.jpeg" border="0" alt=""id="BLOGGER_PHOTO_ID_5300826591611489218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I can still access the web service from my remote OSGi client that I developed in the previous post. The same remote OSGi service can obviously be shared among many consumers. When I run the AuctionClient bundle I'm also seeing my newly listed item.&lt;br /&gt;Before I can do that I need to update the &lt;span style="font-size:85%;font-family: courier new;"&gt;remote-services.xml&lt;/span&gt; file in the consumer bundle with the new location of the Web Service (when using Discovery this is not needed BTW):&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;&amp;lt;service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;service-description&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;provide interface="org.coderthoughts.auction.AuctionService"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;property name="osgi.remote.interfaces"&amp;gt;*&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;property name="osgi.remote.configuration.type"&amp;gt;pojo&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;property name="osgi.remote.configuration.pojo.address"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;b&gt;http://localhost:9000/auction&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/service-description&amp;gt;&lt;br /&gt;&amp;lt;/service-descriptions&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now run the AuctionClient bundle:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;Items available for auction:&lt;br /&gt;  &amp;nbsp;1:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Doll $&amp;nbsp;&amp;nbsp;7.99 Sat Dec 05 08:00:00 GMT 2009&lt;br /&gt;  10:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Joke $&amp;nbsp;10.00 Mon Feb 09 15:48:12 GMT 2009&lt;br /&gt; &amp;nbsp;2: Empty sheet of paper $&amp;nbsp;12.00 Tue May 11 21:10:00 IST 2010&lt;br /&gt; &amp;nbsp;3:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Bike shed $126.75 Tue Sep 15 08:12:00 IST 2009&lt;/span&gt;&lt;br /&gt;You can get all the code in this posting under the Apache license from the google code project. It's all in SVN at &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/gwt_auctionsite"&gt;http://coderthoughts.googlecode.com/svn/trunk/gwt_auctionsite&lt;/a&gt; and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_auctionsite"&gt;http://coderthoughts.googlecode.com/svn/trunk/osgi_auctionsite&lt;/a&gt; .&lt;br /&gt;Since it's all Eclipse projects, the easiest way to get it by doing the SVN checkout of the projects directly in Eclipse. It will give you a workspace that looks like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SYcaaUBRDBI/AAAAAAAAAOQ/gH-8ngnub1E/s1600-h/workspace.jpeg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 284px;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SYcaaUBRDBI/AAAAAAAAAOQ/gH-8ngnub1E/s400/workspace.jpeg" border="0" alt=""id="BLOGGER_PHOTO_ID_5298232525977619474" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-2729769142800579666?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/2729769142800579666/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=2729769142800579666' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/2729769142800579666'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/2729769142800579666'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2009/02/distributed-osgi-powered-ajax-webapp.html' title='A Distributed OSGi Powered AJAX WebApp'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JTmY6gtOOjs/SYcaZK1ArNI/AAAAAAAAAOI/dnkHOhklfsc/s72-c/osgi_blog2.png' height='72' width='72'/><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-5739084354481876042</id><published>2009-02-03T04:36:00.000-08:00</published><updated>2009-02-05T01:32:40.784-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='distributed osgi'/><category scheme='http://www.blogger.com/atom/ns#' term='rfc 119'/><title type='text'>Distributed OSGi – A Simple Example</title><content type='html'>One of the exciting things to come out in 2009 is the new version of the  OSGi Specification. It's called 4.2 but should really have been called 5.0 since there's a lot of new stuff in it. Distributed OSGi for example. (You can find a preview of the Distributed OSGi specification in this doc: &lt;a href="http://www.osgi.org/download/osgi-4.2-early-draft2.pdf"&gt;http://www.osgi.org/download/osgi-4.2-early-draft2.pdf&lt;/a&gt; look for RFC 119 from page 196).&lt;br /&gt;Distributed OSGi enables the distribution of OSGi Services across VMs and provides a standardized way of doing this. It doesn't specify what protocol or binding should be used on the wire for the inter-VM communication, that's up to the implementation. What it does specify is which standard properties you set on your OSGi Service to declare that it should be remoted.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SYcSGIMyDhI/AAAAAAAAAN4/51ThRLI_qVM/s1600-h/dosgi_blog1.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 200px;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SYcSGIMyDhI/AAAAAAAAAN4/51ThRLI_qVM/s400/dosgi_blog1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5298223383114288658" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;The Service Implementation side&lt;/span&gt;&lt;br /&gt;So here it is in its simplest form. Take an OSGi Auction Service that implements the following Java Interface:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;public interface AuctionService {&lt;br /&gt;&amp;nbsp;&amp;nbsp;int listNewItem(String description, int startPriceInCents, Date expiryDate);&lt;br /&gt;&amp;nbsp;&amp;nbsp;void bid(int itemID, String user, int priceInCents)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Collection&amp;lt;Integer&amp;gt; getItemIDs(String query);&lt;br /&gt;&amp;nbsp;&amp;nbsp;Collection&amp;lt;AuctionItem&amp;gt; getItems(int ... id);&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;With this you can list a new item, bid on an item, search for items and get item details given the item's IDs.&lt;br /&gt;I've got a noddy little implementation of this service here: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_auctionsite/AuctionImpl/src/org/coderthoughts/auction/impl/AuctionServiceImpl.java"&gt;AuctionServiceImpl.java&lt;/a&gt;.&lt;br /&gt;Note that I designed the interface to be suitable for distribution with the technology that I'll be using. This means that I don't rely on pass-by-reference for my method arguments and return types. In most cases distribution technologies will use pass-by-value so I made my design compatible with that.&lt;br /&gt;I have put this interface in a separate bundle called AuctionIntf so that it can be used by both the service implementor and the consumer.&lt;br /&gt;&lt;br /&gt;Now I want make this service available to my remote OSGi service consumers. The &lt;i&gt;only thing&lt;/i&gt; I have to do for this is set a property on the OSGi service when I register it in the Activator with the local OSGi Service Registry: &lt;span style="font-size:85%;font-family: courier new;"&gt;osgi.remote.interfaces=*&lt;/span&gt;.  This means that I declare &lt;i&gt;all&lt;/i&gt; the interfaces passed to &lt;span style="font-size:85%;font-family: courier new;"&gt;bundleContext.registerService()&lt;/span&gt; are suitable for remoting. In my case I'm only passing one interface. (You could list individual interfaces instead of passing '*' if you only want to expose a subset).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;public class Activator implements BundleActivator {&lt;br /&gt;&amp;nbsp;&amp;nbsp;private ServiceRegistration sr;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void start(BundleContext context) throws Exception {        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Dictionary props = new Hashtable();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;props.put("osgi.remote.interfaces", "*");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sr = context.registerService(AuctionService.class.getName(), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new AuctionServiceImpl(), props);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void stop(BundleContext context) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sr.unregister();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;I'm putting the service implementation and the Activator in a bundle called AuctionImpl which uses the AuctionService interface from the AuctionIntf bundle.&lt;br /&gt;&lt;br /&gt;Any my Distributed Service is done!&lt;br /&gt;Well... as long as I've got an implementation of Distributed OSGi running as well. &lt;br /&gt;&lt;br /&gt;Lets do an experiment with the server side running on Equinox and the client side running on Felix. For Equinox you need version 3.5 built in December 2008 or later. For Felix you need version 1.4.1 or newer.&lt;br /&gt;I'm using the Distributed OSGi DSW Reference Implementation which is based on Apache CXF and have the single-bundle version of that installed.&lt;br /&gt;&lt;br /&gt;It's currently not yet released, but &lt;a href="http://people.apache.org/repo/m2-snapshot-repository/org/apache/cxf/dosgi/cxf-dosgi-ri-singlebundle-distribution/1.0-SNAPSHOT/cxf-dosgi-ri-singlebundle-distribution-1.0-SNAPSHOT.jar"&gt;you can get a snapshot version via Maven from here&lt;/a&gt; (the bundle name is &lt;span style="font-size:85%;font-family: courier new;"&gt;cxf-dosgi-ri-singlebundle-distribution&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;Install this, plus its dependencies in Equinox and you'll get the following bundles:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;osgi&gt; ss&lt;br /&gt;&lt;br /&gt;Framework is launched.&lt;br /&gt;id State       Bundle&lt;br /&gt;0 ACTIVE      org.eclipse.osgi_3.5.0.v20081201-1815&lt;br /&gt;1 ACTIVE      org.eclipse.osgi.services_3.2.0.v20081205-1800&lt;br /&gt;2 ACTIVE      javax.servlet_2.5.0.v200806031605&lt;br /&gt;3 ACTIVE      cxf-dosgi-ri-singlebundle-distribution&lt;br /&gt;4 ACTIVE      AuctionIntf_1.0.0&lt;br /&gt;5 ACTIVE      AuctionImpl_1.0.0&lt;/span&gt;&lt;br /&gt;The distributed OSGi bundle depends on a few API's from the OSGi compendium spec, which are found in the org.eclipse.osgi.services bundle, which in turn depends on the javax.servlet bundle.&lt;br /&gt;&lt;br /&gt;Hey, my service is now remoted using Web Services and available via SOAP over HTTP! CXF even automatically generates a WSDL: http://localhost:9000/org/coderthoughts/auction/AuctionService?wsdl should look like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/SYcSGF6femI/AAAAAAAAAOA/IBoboNiHDDU/s1600-h/wsdl.jpeg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 400px; height: 308px;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/SYcSGF6femI/AAAAAAAAAOA/IBoboNiHDDU/s400/wsdl.jpeg" border="0" alt=""id="BLOGGER_PHOTO_ID_5298223382500702818" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This in itself is very useful. I can use this WSDL to invoke my OSGi service using any technology that can make Web Services invocations. So I could write a C++ or even a Visual Basic client for it! (In the next posting I'll use it from a remote AJAX Web client: calling a remote OSGi service straight from a browser!)&lt;br /&gt;&lt;br /&gt;There is another way of finding out whether your service has been made available remotely, which works independently of the middleware used. Look in the OSGi Service Registry for services implementing the &lt;span style="font-size:85%;font-family: courier new;"&gt;org.osgi.service.discovery.ServicePublication&lt;/span&gt; interface:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;&lt;br /&gt;osgi&gt; services (objectClass=*Publication)&lt;br /&gt;{org.osgi.service.discovery.ServicePublication}={&lt;br /&gt;&amp;nbsp;&amp;nbsp;osgi.remote.endpoint.location=http://localhost:9000/org/coderthoughts/auction/AuctionService,&lt;br /&gt;&amp;nbsp;&amp;nbsp;service.properties={...},&lt;br /&gt;&amp;nbsp;&amp;nbsp;service.interface=[org.coderthoughts.auction.AuctionService], &amp;nbsp;&amp;nbsp;service.id=34}&lt;br /&gt;Registered by bundle: cxf-dosgi-ri-singlebundle-distribution [3]&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;When a Distributed OSGi implementation is remotely exposing a service, it will in turn register a service implementing the ServicePublication interface to advertise the remote service to any available Discovery mechanisms.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;The Consumer side&lt;/span&gt;&lt;br /&gt;My &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_auctionsite/AuctionClient/src/org/coderthoughts/auction/client/Activator.java"&gt;consumer code&lt;/a&gt; looks like a very ordinary OSGi Service Consumer: a little bit messy :) In real life I'd rather use something like the OSGi Blueprint (aka Spring-DM), iPOJO or Guice-OSGi, but in this posting I'd like to keep things a little focused so I'll go with writing plain OSGi service consumer code using a ServiceTracker. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;public class Activator implements BundleActivator {&lt;br /&gt;&amp;nbsp;&amp;nbsp;private ServiceTracker st;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void start(final BundleContext bc) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;st = new ServiceTracker(bc, AuctionService.class.getName(), null) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;@Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public Object addingService(ServiceReference reference) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Object svc = bc.getService(reference);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (svc instanceof AuctionService) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printServiceInfo((AuctionService) svc);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;                &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return super.addingService(reference);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}            &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;st.open();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected void printServiceInfo(AuctionService auctionService) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println("Items available for auction:");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Collection&amp;lt;Integer&amp;gt; ids = auctionService.getItemIDs(null);&lt;br /&gt;        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int[] idArray = toIntArray(ids);        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (AuctionItem item : auctionService.getItems(idArray)) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.printf("%3d: %25s $%,6.2f %tc\n", &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item.getID(), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item.getDescription(), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(item.getPriceInCents() / 100.0), &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item.getExpires());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}        &lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private int[] toIntArray(Collection&amp;lt;Integer&amp;gt; ids) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// details omitted&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void stop(BundleContext bc) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;st.close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;As soon as the ServiceTracker gets a callback that the AuctionService is there, it prints out some details about the items available for auction. &lt;br /&gt;Euh yeah, but where are the distribution bits in the consumer? Correct – there aren't any! A remote OSGi service is looked up exactly the same way as a local OSGi service. The simple fact that the client is expressing an interest in the service is enough for Distributed OSGi to go out and check for the existence of the service as a remote service. Behind the scenes it asynchronously checks any registered OSGi Remote Service Discovery services. &lt;br /&gt;In this posting I'm not using a Discovery Service yet. I'm using a alternate, more static way, of specifying where my remote service is. Via a &lt;span style="font-size:85%;font-family: courier new;"&gt;remote-services.xml&lt;/span&gt; file. This file sits by default in the &lt;span style="font-size:85%;font-family: courier new;"&gt;OSGI-INF/remote-service&lt;/span&gt; directory of a bundle:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;&amp;lt;service-descriptions xmlns="http://www.osgi.org/xmlns/sd/v1.0.0"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;service-description&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;provide interface="org.coderthoughts.auction.AuctionService"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;property name="osgi.remote.interfaces"&amp;gt;*&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;property name="osgi.remote.configuration.type"&amp;gt;pojo&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;property name="osgi.remote.configuration.pojo.address"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;http://localhost:9000/org/coderthoughts/auction/AuctionService&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/property&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;lt;/service-description&amp;gt;&lt;br /&gt;&amp;lt;/service-descriptions&amp;gt;&lt;/span&gt;&lt;br /&gt;The format of this file is specified by Distributed OSGi. &lt;span style="font-size:85%;font-family: courier new;"&gt;osgi.remote.interfaces&lt;/span&gt; is the same standard property that I set on the service. The &lt;span style="font-size:85%;font-family: courier new;"&gt;osgi.remote.configuration.pojo.address&lt;/span&gt; is a CXF-specific one that simply denotes where my remote service is running. &lt;br /&gt;&lt;br /&gt;So let's run the consumer in Felix. &lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;-&gt; ps&lt;br /&gt;START LEVEL 1&lt;br /&gt;   ID   State         Level  Name&lt;br /&gt;[   0] [Active     ] [    0] System Bundle (1.4.1)&lt;br /&gt;[   1] [Active     ] [    1] Apache Felix Shell Service (1.0.2)&lt;br /&gt;[   2] [Active     ] [    1] Apache Felix Shell TUI (1.0.2)&lt;br /&gt;[   3] [Active     ] [    1] Apache Felix Bundle Repository (1.2.1)&lt;br /&gt;[   4] [Active     ] [    1] OSGi R4 Compendium Bundle (4.1.0)&lt;br /&gt;[   5] [Active     ] [    1] Distributed OSGi Distribution Software Single-Bundle Distribution&lt;br /&gt;[   6] [Active     ] [    1] AuctionIntf (1.0.0)&lt;br /&gt;[   7] [Active     ] [    1] AuctionClient (1.0.0)&lt;/span&gt;&lt;br /&gt;Besides the default Felix system, shell and bundle repository bundles, I've also installed &lt;a href="http://www.apache.org/dist/felix/org.osgi.compendium-1.2.0.jar"&gt;the OSGi Compendium bundle&lt;/a&gt; which contains some OSGi standard interfaces that we need. &lt;br /&gt;&lt;br /&gt;After the client bundle has started it will receive a callback that the (remote) AuctionService it wants to use is available and it will print out some information about the available items:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;Items available for auction:&lt;br /&gt;  1:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Doll $&amp;nbsp;&amp;nbsp;7.99 Sat Dec 05 08:00:00 GMT 2009&lt;br /&gt;  2: Empty sheet of paper $&amp;nbsp;12.00 Tue May 11 21:10:00 IST 2010&lt;br /&gt;  3:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Bike shed $126.75 Tue Sep 15 08:12:00 IST 2009&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can also see the remote service represented in the local service registry the same way a local service is. It is registered on demand by the DSW bundle:&lt;br /&gt;&lt;span style="font-size:85%;font-family: courier new;"&gt;-&gt; services 5&lt;br /&gt;... &lt;br /&gt;objectClass = org.coderthoughts.auction.AuctionService&lt;br /&gt;osgi.remote = true&lt;br /&gt;osgi.remote.configuration.type = pojo&lt;br /&gt;osgi.remote.configuration.pojo.address = http://localhost:9000/org/coderthoughts/auction/AuctionService&lt;br /&gt;osgi.remote.interfaces = *&lt;br /&gt;service.id = 30&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There's our remote service! Just like an ordinary OSGi service. An extra property is set on client side proxies: &lt;span style="font-size:85%;font-family: courier new;"&gt;osgi.remote&lt;/span&gt;. This can be used to find out whether a service that you are dealing with is remote or not. It can also be used to change the default lookup behaviour. By default remote services are always included in the service lookups, but this default can be tweaked by registering a FindHook/EventHook, which is a new feature of the Service Registry specified in RFC 126. &lt;br /&gt;&lt;br /&gt;You can find the full implementation of the AuctionIntf, AuctionImpl and AuctionClient OSGi bundles as Eclipse projects in SVN: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_auctionsite"&gt;http://coderthoughts.googlecode.com/svn/trunk/osgi_auctionsite&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next posting: an AJAX client UI written using the Google Web Toolkit (GWT).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Links&lt;/span&gt;&lt;br /&gt;OSGi Specs: &lt;a href="http://www.osgi.org/Specifications"&gt;http://www.osgi.org/Specifications&lt;/a&gt;&lt;br /&gt;Disributed OSGi Reference Implementation based on Apache CXF: &lt;a href="http://cwiki.apache.org/confluence/display/CXF/Distributed+OSGi"&gt;http://cwiki.apache.org/confluence/display/CXF/Distributed+OSGi&lt;/a&gt;&lt;br /&gt;Apache CXF Home page: &lt;a href="http://cxf.apache.org"&gt;http://cxf.apache.org&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-5739084354481876042?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/5739084354481876042/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=5739084354481876042' title='23 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5739084354481876042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5739084354481876042'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2009/02/distributed-osgi-simple-example.html' title='Distributed OSGi – A Simple Example'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JTmY6gtOOjs/SYcSGIMyDhI/AAAAAAAAAN4/51ThRLI_qVM/s72-c/dosgi_blog1.png' height='72' width='72'/><thr:total>23</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-2269876998283210263</id><published>2008-11-26T03:31:00.000-08:00</published><updated>2008-11-26T04:39:17.034-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>A Web Site Concentrator to ease AJAX development</title><content type='html'>Every now and then I do a little AJAX development and there is one thing that I always find frustrating during the development of AJAX apps: &lt;span style="font-weight: bold; font-style: italic;"&gt;The browser's single-origin policy&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Now I agree that for non-development cases this policy is absolutely justified, but during development it's a big PITA.&lt;br /&gt;Let's say you are developing an AJAX app that calls into a back-end app over XML and you want to debug both. Like in my case I have a little GWT app that invokes on a Distributed OSGi Service running on the back end in CXF over port 9000. Ultimately you'd deploy it all over a single server, so there wouldn't really be a problem in production, but during development my GWT app is running on &lt;span style="font-family: courier new;font-size:85%;"&gt;http://localhost:8888/org.coderthoughts.dinner.DinnerClient&lt;/span&gt; while my CXF back end runs on &lt;span style="font-family: courier new;font-size:85%;"&gt;http://localhost:9000/org/apache/cxf/dosgi/samples/springdm/DinnerService&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If I try to invoke my back-end app in this setup my AJAX app complains:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/SS048tMNNLI/AAAAAAAAANo/TVe0Y0V3COg/s1600-h/before.JPG"&gt;&lt;img style="cursor: pointer; width: 400px; height: 285px;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/SS048tMNNLI/AAAAAAAAANo/TVe0Y0V3COg/s400/before.JPG" alt="" id="BLOGGER_PHOTO_ID_5272933354294817970" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Since your front-end and back-end are probably written in different languages chances are that they have different development and debugging environments, which makes adhering to the single-origin hard to do. Besides, you don't want to go through the hassle of rebuilding your .WAR files (or whatever format you might use) for every single change. I know MSIE has some options to alleviate this, but they don't always work. Also using JSON as a data format gives you some options to bypass the problem, but that only works if your data format is JSON. In my case it's XML.&lt;br /&gt;&lt;br /&gt;Enter ProxyServlet. There's a few of these around, but they're generally a bit raw. I particularly liked &lt;a href="http://frank.spieleck.de/servlets.jsp"&gt;the one from Frank Spieleck&lt;/a&gt; because it has no dependencies, so is simple to use.&lt;br /&gt;Typically these ProxyServlets forward requests from one context to another location, so to build the multi-site concentrator that you need to make the browser think both of them come from the same source you need at least two of them. In my case I need the following proxy settings:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;org.coderthoughts.dinner.DinnerClient =&gt; &lt;span style="font-family: courier new;font-size:85%;"&gt;http://localhost:8888/org.coderthoughts.dinner.DinnerClient&lt;/span&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;org =&gt; &lt;span style="font-family: courier new;font-size:85%;"&gt;http://localhost:9000/org&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;To ease this task I wrote a little commandline wrapper around Frank's ProxyServlet that simply generates a .WAR file with the right name to set the root context, the correct bits in the web.xml etc... So I generate 2 .WAR files:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;java -jar genwebproxy.jar \&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;&amp;nbsp;&amp;nbsp;org.coderthoughts.dinner.DinnerClient \&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;&amp;nbsp;&amp;nbsp;http://localhost:8888/org.coderthoughts.dinner.DinnerClient&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;&amp;nbsp;&amp;nbsp;Created: org.coderthoughts.dinner.DinnerClient.war&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;java -jar genwebproxy.jar org http://localhost:9000/org&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;&amp;nbsp;&amp;nbsp;Created: org.war&lt;/span&gt;&lt;br /&gt;and simply deploy them into Tomcat by copying them into the webapps directory. These are standard .WAR files so you can deploy them into any Webapp container. I've also put the &lt;a href="http://wiki.ops4j.org/confluence/display/ops4j/Pax+Web+Extender+-+War#PaxWebExtender-War-osgify"&gt;PAX-WEB&lt;/a&gt; headers in, in case you want to deploy them into that.&lt;br /&gt;&lt;br /&gt;Et voilà. It works!&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/SS048glb9LI/AAAAAAAAANw/m1qMlinLm1k/s1600-h/after.JPG"&gt;&lt;img style="cursor: pointer; width: 400px; height: 285px;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/SS048glb9LI/AAAAAAAAANw/m1qMlinLm1k/s400/after.JPG" alt="" id="BLOGGER_PHOTO_ID_5272933350910981298" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note that I access my AJAX app using the Tomcat port of 8080 instead of the GWT debug port of 8888. I did have to change the port number at with my webapp makes an invocation from 9000 to 8080 as well, but that's easy.&lt;br /&gt;&lt;br /&gt;With Frank's permission I put the ProxyServlet plus my wrapping code for the &lt;span style="font-family: courier new;"&gt;genwebproxy.jar&lt;/span&gt; file in SVN here: &lt;span style="font-family: courier new;font-size:85%;"&gt;http://coderthoughts.googlecode.com/svn/trunk/GenWebProxy&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can &lt;a href="http://coderthoughts.googlecode.com/files/genwebproxy.jar"&gt;download the binary from here&lt;/a&gt;, the command line is:&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;"&gt;java -jar genwebproxy.jar [context_root] [real_location]&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-2269876998283210263?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/2269876998283210263/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=2269876998283210263' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/2269876998283210263'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/2269876998283210263'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2008/11/every-now-and-then-i-do-little-ajax.html' title='A Web Site Concentrator to ease AJAX development'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JTmY6gtOOjs/SS048tMNNLI/AAAAAAAAANo/TVe0Y0V3COg/s72-c/before.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-3596246851957264677</id><published>2008-04-28T09:30:00.000-07:00</published><updated>2008-11-13T12:53:47.283-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='GMF'/><category scheme='http://www.blogger.com/atom/ns#' term='ServiceMix'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse EMF'/><title type='text'>An OSGi Bundle dependency visualizer in GMF</title><content type='html'>When working with larger OSGi-base systems you sometimes wonder: why are all these bundles there? What other bundles does my bundle depend on at runtime? Is there any duplication going on?&lt;br /&gt;For a project that I was working on I started manually creating Bundle Dependency diagrams. This got me thinking: it should be really easy to create a tool that does this for me using &lt;a href="http://www.eclipse.org/modeling/emf/"&gt;EMF&lt;/a&gt; (Eclipse Modeling Framework) and &lt;a href="http://www.eclipse.org/modeling/gmf/"&gt;GMF&lt;/a&gt; (the Graphical Modeling Framework from Eclipse).&lt;br /&gt;&lt;br /&gt;As always in Model Driven Development, you start with a model. In my case a model of how OSGi Bundles relate to each other.&lt;br /&gt;Having the model, you just fill it with data, then use some GMF goodness to display is graphically.&lt;br /&gt;&lt;br /&gt;Sounds easy? It was easy! With well under 300 lines of manually written code (and a whole bunch of EMF/GMF generated stuff) the end result looks like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SA9OZ8UpIjI/AAAAAAAAAI8/qZHyky6P9wM/s1600-h/smx_kernel2.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SA9OZ8UpIjI/AAAAAAAAAI8/qZHyky6P9wM/s400/smx_kernel2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5192455102977876530" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In this picture I ran the visualizer in the &lt;a href="http://servicemix.apache.org/SMX4KNL/index.html"&gt;ServiceMix 4 Kernel&lt;/a&gt; to see what was in it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Main Components&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There are two components in this application:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A bundle that you drop into an OSGi container, when you start the bundle it will walk all the other bundles in the OSGi container and figure out what their dependencies are. The result is stored in an EMF model which is stored a file called &lt;span style="font-family:courier new;"&gt;save.bundles&lt;/span&gt; at the end of the process.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;An Eclipse GMF application that can create a visual diagram from the save.bundles file and allows you to tweak the resulting picture any way you like.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;How it's done&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The model&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;1. Start with a simple model of a Framework (representing the OSGi Container) which contains bundles. Each bundle in turn could be importing stuff from other bundles:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/SA9HWMUpIdI/AAAAAAAAAIM/72EaYyLiNvk/s1600-h/model.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/SA9HWMUpIdI/AAAAAAAAAIM/72EaYyLiNvk/s400/model.JPG" alt="" id="BLOGGER_PHOTO_ID_5192447341971972562" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Here are the model details:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/SA9HWcUpIeI/AAAAAAAAAIU/hhxxEzKETn8/s1600-h/model2.jpg"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/SA9HWcUpIeI/AAAAAAAAAIU/hhxxEzKETn8/s400/model2.jpg" alt="" id="BLOGGER_PHOTO_ID_5192447346266939874" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;After generating the genmodel file from my model file (I only modified the base package in the genmodel package properties) I generated the model code so I could start coding with it. (See &lt;a href="http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with.html"&gt;here&lt;/a&gt; for an earlier post that outlines how to generate code from an EMF model).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Introspector Bundle&lt;/span&gt;&lt;br /&gt;To create a model instance with the bundle dependency data, I've written an self-contained OSGi bundle that I can drop on any OSGi runtime (well, at this point I only tried Equinox and Felix). When the bundle is started it walks all the active bundles in the container and figures out what other bundles each bundle depends on by using the bundle's classloader to resolve imported classes. (Since Package Imports are resolved dynamically this can only be done at runtime in an active OSGi container). The collected info is stored in an instance of my model. I stayed away from using any implementation-specific admin API's, instead I'm just using the bundle's classloader to resolve the packages listed in the &lt;span style="font-family:courier new;"&gt;Import-Package&lt;/span&gt; header to and figure out what bundle the OSGi runtime has linked them to.&lt;br /&gt;Additionally,  &lt;span style="font-family:courier new;"&gt;Require-Bundle&lt;/span&gt; dependencies are stored in the model too.&lt;br /&gt;&lt;br /&gt;Finally, after all the bundles have been processed, I use the built-in EMF &lt;a href="http://www.omg.org/technology/documents/formal/xmi.htm"&gt;XMI&lt;/a&gt; serialization to save model instance to a file (currently always called &lt;span style="font-family:courier new;"&gt;save.bundles&lt;/span&gt; in your temp directory).&lt;br /&gt;&lt;br /&gt;That's the hard bit done. You can see the code here in the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_visualizer/osgi_bundles.introspector.bundle/src/org/coderthoughts/osgi/introspector/bundle/BundleIntrospector.java"&gt;BundleIntrospector&lt;/a&gt; and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_visualizer/osgi_bundles.introspector.bundle/src/org/coderthoughts/osgi/introspector/bundle/BundleIntrospectorTools.java"&gt;BundleIntrospectorTools&lt;/a&gt; classes which are invoked from my simple &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_visualizer/osgi_bundles.introspector.bundle/src/org/coderthoughts/osgi/introspector/bundle/Activator.java"&gt;Activator&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Visualizer&lt;/span&gt;&lt;br /&gt;Believe it or not, the easy bit is in creating the graphical visualizer. GMF does all the hard work for me. I'm by no means a GMF connaisseur, so I used the GMF Cheat Sheet that comes with GMF in Eclipse. I ran all the wizards from the Cheat Sheet, and used all the defaults to create the &lt;span style="font-family:courier new;"&gt;.gmfgraph&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;.gmftool&lt;/span&gt;, &lt;span style="font-family:courier new;"&gt;.gmfmap &lt;/span&gt;and &lt;span style="font-family:courier new;"&gt;.gmfgen &lt;/span&gt;files. For some reason, there was one setting that I had to manually change: the Label Mapping, but the cheat sheet told me what to do.&lt;br /&gt;Finally I hit 'generate diagram code' on the &lt;span style="font-family:courier new;"&gt;.gmfgen&lt;/span&gt; file and I'm done.&lt;br /&gt;&lt;br /&gt;My Eclipse Workspace looks like this.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/SA9HWsUpIfI/AAAAAAAAAIc/4Lji6D56_cM/s1600-h/all_generated.JPG"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/SA9HWsUpIfI/AAAAAAAAAIc/4Lji6D56_cM/s400/all_generated.JPG" alt="" id="BLOGGER_PHOTO_ID_5192447350561907186" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Running it&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Running the introspector with Equinox&lt;br /&gt;&lt;/span&gt;This is very easy from within Eclipse. To try this out, create a new OSGi Framework launch configuration with the introspector bundle and, to give it something to look at, I also include the org.mortbay.jetty bundle and it's dependencies:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/SA9HWsUpIgI/AAAAAAAAAIk/byvK9XyuzfE/s1600-h/equinox_launch.JPG"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/SA9HWsUpIgI/AAAAAAAAAIk/byvK9XyuzfE/s400/equinox_launch.JPG" alt="" id="BLOGGER_PHOTO_ID_5192447350561907202" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Make sure that the introspector bundle is not automatically started. When I launch Equinox I have the following bundles loaded:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;pre&gt;&lt;br /&gt;osgi&gt; ss&lt;br /&gt;Framework is launched.&lt;br /&gt;&lt;br /&gt;id&amp;nbsp;&amp;nbsp;&amp;nbsp;State&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Bundle&lt;br /&gt;0&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ACTIVE&amp;nbsp;&amp;nbsp;&amp;nbsp;org.eclipse.osgi_3.3.1.R33x_v20070828&lt;br /&gt;1&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ACTIVE&amp;nbsp;&amp;nbsp;&amp;nbsp;org.mortbay.jetty_5.1.11.v200706111724&lt;br /&gt;2&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ACTIVE&amp;nbsp;&amp;nbsp;&amp;nbsp;org.apache.commons.logging_1.0.4.v200706111724&lt;br /&gt;3&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ACTIVE&amp;nbsp;&amp;nbsp;&amp;nbsp;javax.servlet_2.4.0.v200706111738&lt;br /&gt;4&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;RESOLVED&amp;nbsp;osgi_bundles.introspector.bundle_0.0.3.qualifier&lt;br /&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;When I start the introspector bundle it starts analyzing the other bundles:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;pre&gt;&lt;br /&gt;osgi&gt; start 4&lt;br /&gt;Initializing data from bundle: org.eclipse.osgi&lt;br /&gt;Initializing data from bundle: org.mortbay.jetty&lt;br /&gt;Initializing data from bundle: org.apache.commons.logging&lt;br /&gt;...&lt;br /&gt;Saving... Done dependency model saved to: c:\temp\save.bundles&lt;br /&gt;Finished introspecting, stopping bundle.&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Running the Visualizer&lt;/span&gt;&lt;br /&gt;Now that I have a data file, I can run the visualizer. Just right-click on one of your projects in Eclipse and select Run As Eclipse Application.&lt;br /&gt;In the Eclipse Application that gets started, create a new Project and copy the &lt;span style="font-family:courier new;"&gt;save.bundles&lt;/span&gt; file in there.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SA9HW8UpIhI/AAAAAAAAAIs/2cXGuZ8xYiM/s1600-h/vis0.JPG"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SA9HW8UpIhI/AAAAAAAAAIs/2cXGuZ8xYiM/s400/vis0.JPG" alt="" id="BLOGGER_PHOTO_ID_5192447354856874514" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;At this point the save.bundles file is purely an EMF serialization into XMI, but GMF can initialize a picture from it, right-click 'initialize bundles_diagram file', and you'll get one:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/SA9Hy8UpIiI/AAAAAAAAAI0/90Rm-mE0t2g/s1600-h/vis1.JPG"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/SA9Hy8UpIiI/AAAAAAAAAI0/90Rm-mE0t2g/s400/vis1.JPG" alt="" id="BLOGGER_PHOTO_ID_5192447835893211682" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Tadaa, a nice visualization of the bundles and their dependencies. The Jetty bundle uses the commons logging and servlet bundles (the system bundle is ommitted).&lt;br /&gt;Now you can use the GMF features to rearrange your diagram, choose colours, line smoothness etc to make your diagram the way you want it (which is quite useful once you have a lot of bundles). You can even export the picture as a PNG, JPG or SVG file.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Running the introspector in ServiceMix 4 Kernel&lt;/span&gt;&lt;br /&gt;The ServiceMix 4 Kernel is an OSGi based system built using Apache Felix. If you want to run the introspector in the ServiceMix 4 Kernel you can follow these steps:&lt;br /&gt;1. Get hold of a SMX 4 Kernel distribution. I created mine by checking out the code from http://svn.apache.org/repos/asf/servicemix/smx4/kernel/trunk and running &lt;span style="font-family:courier new;"&gt;mvn install&lt;/span&gt; to build it. You will find the distribution in &lt;span style="font-family:courier new;"&gt;assembly/target&lt;/span&gt; subdirectory.&lt;br /&gt;2. Run bin/servicemix&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;pre&gt;&lt;br /&gt; ____                  _          __  __ _&lt;br /&gt;/ ___|  ___ _ ____   _(_) ___ ___|  \/  (_)_  __&lt;br /&gt;\___ \ / _ \ '__\ \ / / |/ __/ _ \ |\/| | \ \/ /&lt;br /&gt; ___) |  __/ |   \ V /| | (_|  __/ |  | | |&gt;  &lt;&lt;br /&gt;|____/ \___|_|    \_/ |_|\___\___|_|  |_|_/_/\_\&lt;br /&gt;&lt;br /&gt; ServiceMix (1.0-m3-SNAPSHOT)&lt;br /&gt;&lt;br /&gt;Type 'help' for more information.&lt;br /&gt;------------------------------------------------&lt;br /&gt;servicemix&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;Move to the osgi shell and install the introspector, I'm doing this straight from the Google Code project downloads section:&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;pre&gt;&lt;br /&gt;servicemix&gt; osgi&lt;br /&gt;servicemix osgi&gt; install http://coderthoughts.googlecode.com/files/osgi_bundles.&lt;br /&gt;introspector.bundle_0.0.3.200804202025.jar&lt;br /&gt;Bundle ID: 28&lt;br /&gt;servicemix osgi&gt;&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;When I start bundle 28 it will do the introspection and save this to the save.bundles file. When loaded in the visualizer, you'll get a picture like the first one in this posting.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Getting your environment set up&lt;/span&gt;&lt;br /&gt;I used Eclipse 3.3.1.1 with the EMF/GMF plugins loaded. See &lt;a href="http://coderthoughts.blogspot.com/2007/10/how-to-get-emf-and-gmf-based-uml.html"&gt;this posting&lt;/a&gt; for details on setting it up.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Getting the code&lt;/span&gt;&lt;br /&gt;All the code used here is available under the Apache License from the Google Code project via SVN. The Eclipse Projects are accessible from here: &lt;span style="font-size:70%;" &gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/osgi_visualizer/"&gt;http://coderthoughts.googlecode.com/svn/trunk/osgi_visualizer/&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Getting the distribution&lt;/span&gt;&lt;br /&gt;If you just want to use or play with the visualizer, you can download the introspector bundle and visualizer plugins from the the download area: &lt;a href="http://code.google.com/p/coderthoughts/downloads/list"&gt;http://code.google.com/p/coderthoughts/downloads/list&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Improvements are welcomed!&lt;/span&gt;&lt;br /&gt;The project is by no means finished. I'm still in the process of learning more about GMF and any suggestions or improvements are appreciated.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-3596246851957264677?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/3596246851957264677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=3596246851957264677' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/3596246851957264677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/3596246851957264677'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2008/04/osgi-bundle-dependency-visualizer-in.html' title='An OSGi Bundle dependency visualizer in GMF'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JTmY6gtOOjs/SA9OZ8UpIjI/AAAAAAAAAI8/qZHyky6P9wM/s72-c/smx_kernel2.JPG' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-8803408796186276383</id><published>2008-03-31T11:54:00.000-07:00</published><updated>2008-11-13T12:53:48.494-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi'/><category scheme='http://www.blogger.com/atom/ns#' term='Equinox'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring-DM'/><category scheme='http://www.blogger.com/atom/ns#' term='Jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='JAX-RS'/><category scheme='http://www.blogger.com/atom/ns#' term='JSR311'/><title type='text'>Using Jersey (JSR311) inside OSGi with Spring-DM</title><content type='html'>&lt;span style="font-weight: bold; font-size: 130%"&gt;Or: using REST &amp;amp; OSGi to create a number guessing game&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Jersey is an implementation of JSR 311 also know as JAX-RS. It makes writing a REST service quite simple with Java Annotations. So I wanted to use Jersey inside my OSGi bundle with the OSGi HTTP Service. However, as I found out, Jersey is not written to run in an OSGi container...&lt;br /&gt;&lt;br /&gt;Fortunately, the problematic code in Jersey is pluggable. In this posting I'm writing a little Jersey REST Application that runs in my OSGi container. I'm using &lt;a href="http://static.springframework.org/osgi"&gt;Spring-DM&lt;/a&gt; (aka Spring-OSGi) to do the OSGi Service dependency injection which keeps my code really lean &amp;amp; clean (Spring Clean ;) .&lt;br /&gt;&lt;br /&gt;To illustrate I wrote a little number guessing game. The very first program I ever wrote was a game like this. This was on the &lt;a href="http://en.wikipedia.org/wiki/Commodore_pet"&gt;Commodore Pet&lt;/a&gt; computer and I was about 10 years old. So beware...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Development Environment Setup&lt;br /&gt;&lt;/span&gt;The Jersey version I got is 0.7-ea, which I downloaded from the &lt;a href="https://jersey.dev.java.net/servlets/ProjectDocumentList"&gt;Jersey download site&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I'm using the Equinox OSGi runtime in Eclipse 3.3.2 (get it &lt;a href="http://www.eclipse.org/downloads/"&gt;here&lt;/a&gt;) with Spring-DM added in.&lt;br /&gt;&lt;br /&gt;I downloaded Spring-DM 1.0.2 from &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=73357&amp;amp;package_id=227224"&gt;here&lt;/a&gt; and copied all the jars from the dist and the lib directory of the download in my eclipse/plugins directory. The jars in the Spring-DM lib directory have been OSGi-ified by the Spring guys so we can nicely drop them in as OSGi bundles.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Bundle Dependencies&lt;/span&gt;&lt;br /&gt;The general dependency picture is like the image below. I'm writing a Jersey app in a bundle that depends on a library bundle which contains the Jersey jars. This way multiple bundles can use the Jersey functionality to implement REST services while sharing the same Jersey Library bundle.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/R-01rNRgirI/AAAAAAAAAH0/VsM_S9zKJiQ/s1600-h/schema.png"&gt;&lt;img style="cursor:pointer; cursor:hand; width: 50%; height: 50%;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/R-01rNRgirI/AAAAAAAAAH0/VsM_S9zKJiQ/s400/schema.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5182857762587052722" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Jersey Library Bundle&lt;/span&gt;&lt;br /&gt;This is a simple wrapper around the Jersey libraries. At the moment I'm only depending on jersey.jar, jsr311-api.jar and asm-3.1.jar. So I'm simply creating an OSGi Bundle Project in Eclipse (Plug-in Project - see &lt;a href="http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications_30.html"&gt;this posting&lt;/a&gt; for some details on how to do this), add these three jars from the Jersey install to the root of the bundle and then put them on the Bundle-Classpath. Finally simply export all the classes defined by these bundles. I guess a subset of the classes would do too, anyone fancy figuring out what the minimum Export-Package would be?&lt;br /&gt;&lt;br /&gt;My &lt;span style="font-family:courier new"&gt;MANIFEST.MF&lt;/span&gt; looks like this:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;&lt;br /&gt;Manifest-Version: 1.0&lt;br /&gt;Bundle-ManifestVersion: 2&lt;br /&gt;Bundle-Name: JerseyLibBundle&lt;br /&gt;Bundle-SymbolicName: JerseyLibBundle&lt;br /&gt;Bundle-Version: 1.0.0&lt;br /&gt;Bundle-ClassPath: asm-3.1.jar,&lt;br /&gt;&amp;nbsp;jersey.jar,&lt;br /&gt;&amp;nbsp;jsr311-api.jar,&lt;br /&gt;&amp;nbsp;.&lt;br /&gt;Import-Package: javax.servlet,&lt;br /&gt;&amp;nbsp;javax.servlet.http&lt;br /&gt;Export-Package: ...,&lt;br /&gt;&amp;nbsp;... export all the packages in these jars ...&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That's the Jersey Library Bundle done. My Eclipse screen looks like this (click on it to see all the details):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R-0TttRgimI/AAAAAAAAAHM/4jQYdiseBiM/s1600-h/JerseyLibBundle.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R-0TttRgimI/AAAAAAAAAHM/4jQYdiseBiM/s400/JerseyLibBundle.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5182820422141381218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The REST Bundle&lt;/span&gt;&lt;br /&gt;This is the interesting bit. Here's where we'll have our REST Guessing Game implemented.&lt;br /&gt;&lt;br /&gt;The REST application will be available on:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;http://localhost/fun/numbers&lt;/span&gt;&lt;br /&gt;Lists all the numbers available in XML format.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;" &gt;&amp;nbsp;&amp;nbsp;http://localhost/fun/numbers/{number}&lt;/span&gt;&lt;br /&gt;Returns information about the requested number in text format. The information says what number it is (the same one you asked for) and whether or not you guessed the right number.&lt;br /&gt;&lt;br /&gt;I have a &lt;span style="font-family:courier new;" &gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/jersey_osgi/JerseyOsgiSpringDM/src/jersey_osgi/rest/Number.java"&gt;jersey_osgi.rest.Number&lt;/a&gt;&lt;/span&gt; class:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;package jersey_osgi.rest;&lt;br /&gt;&lt;br /&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.List;&lt;br /&gt;import java.util.Random;&lt;br /&gt;&lt;br /&gt;public class Number {&lt;br /&gt;&amp;nbsp;&amp;nbsp;enum Location {LESS, FOUND, MORE};&lt;br /&gt;&amp;nbsp;&amp;nbsp;private static final int MAX_NUM = 10;&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;static List&amp;lt;Number&amp;gt; NUMBERS;&lt;br /&gt;&amp;nbsp;&amp;nbsp;static {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Initialize with numbers 1 to 10, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// and pick a random on to guess.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NUMBERS = new ArrayList&amp;lt;Number&amp;gt;();&lt;br /&gt;        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Random random =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Random(System.currentTimeMillis());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int theOne = random.nextInt(MAX_NUM) + 1;&lt;br /&gt;        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i=1; i &amp;lt;= MAX_NUM; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Location location;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (i == theOne) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;location = Location.FOUND;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else if (i &amp;lt; theOne) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;location = Location.LESS;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;location = Location.MORE;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;NUMBERS.add(new Number(i, location));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;private final int number;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private final Location location;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public Number(int num, Location l) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;number = num;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;location = l;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public int  getNumber() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return number;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}    &lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;@Override&lt;br /&gt;&amp;nbsp;&amp;nbsp;public String toString() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StringBuilder sb = new StringBuilder();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("I'm number ");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append(number);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append(". ");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;switch (location) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case FOUND:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("You guessed right!");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case LESS:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("You need to guess higher.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;case MORE:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("You need to guess lower.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return sb.toString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Next thing we need is a JSR311 compliant class that binds our Number class to a REST API:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;package jersey_osgi.rest;&lt;br /&gt;&lt;br /&gt;import javax.ws.rs.GET;&lt;br /&gt;import javax.ws.rs.Path;&lt;br /&gt;import javax.ws.rs.PathParam;&lt;br /&gt;import javax.ws.rs.ProduceMime;&lt;br /&gt;&lt;br /&gt;@Path("/numbers")&lt;br /&gt;public class NumberResource {&lt;br /&gt;&amp;nbsp;&amp;nbsp;@GET&lt;br /&gt;&amp;nbsp;&amp;nbsp;@ProduceMime("text/xml")&lt;br /&gt;&amp;nbsp;&amp;nbsp;public String listNumbers() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;StringBuilder sb = new StringBuilder();&lt;br /&gt;        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("&amp;lt;!-- Guess the right number! The following numbers are available. --&amp;gt;");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("\n");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("&amp;lt;numbers&amp;gt;");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("\n");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (Number number : Number.NUMBERS) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("&amp;lt;number&amp;gt;");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append(number.getNumber());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("&amp;lt;/number&amp;gt;");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("\n");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sb.append("&amp;lt;/numbers&amp;gt;");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return sb.toString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;@GET&lt;br /&gt;&amp;nbsp;&amp;nbsp;@Path("/{id}")&lt;br /&gt;&amp;nbsp;&amp;nbsp;@ProduceMime("text/plain")&lt;br /&gt;&amp;nbsp;&amp;nbsp;public String getNumber(@PathParam("id") String id) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int i = Integer.parseInt(id);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Number n = Number.NUMBERS.get(i-1);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return n.toString();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} catch (Throwable th) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return "Sorry that number is out of range";&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;That's our REST implementation done. Only thing left to do is use the OSGi HTTPService to launch the Jersey Servlet and tell it what our REST Resource are, right? You wish! You would think that the following piece does the trick:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"&gt;HttpService httpService = ...; // The OSGi HTTP Service&lt;br /&gt;Hashtable&lt;String, String&gt; initParams = new Hashtable&lt;String, String&gt;();&lt;br /&gt;initParams.put(&lt;br /&gt;&amp;nbsp;&amp;nbsp;// The following are standard Jersey properties&lt;br /&gt;&amp;nbsp;&amp;nbsp;"com.sun.ws.rest.config.property.resourceConfigClass",&lt;br /&gt;&amp;nbsp;&amp;nbsp;"com.sun.ws.rest.api.core.PackagesResourceConfig");&lt;br /&gt;initParams.put(&lt;br /&gt;&amp;nbsp;&amp;nbsp;"com.sun.ws.rest.config.property.packages",&lt;br /&gt;&amp;nbsp;&amp;nbsp;"jersey_osgi.rest");&lt;br /&gt;        &lt;br /&gt;Servlet jerseyServlet =&lt;br /&gt;&amp;nbsp;&amp;nbsp;new com.sun.ws.rest.spi.container.servlet.ServletContainer();                &lt;br /&gt;httpService.registerServlet("/fun", jerseyServlet, initParams, null);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;But hey! I'm getting an ugly exception:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;com.sun.ws.rest.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is because the &lt;span style="font-family:courier new;" &gt;com.sun.ws.rest.api.core.PackagesResourceConfig&lt;/span&gt; class contains OSGi unfriendly classloader code (it tries to treat classes as resources, which at least does'nt work on Equinox). Luckily this class is plugged into Jersey, so I can write an OSGi-friendly version. &lt;span style="font-family:courier new;"&gt;PackagesResourceConfig&lt;/span&gt; searches the specified packages for any classes with the JSR311 annotations. &lt;br /&gt;My alternative implementation is called &lt;span style="font-family:courier new;" &gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/jersey_osgi/JerseyOsgiSpringDM/src/jersey_osgi/common/OSGiResourceConfig.java"&gt;jersey_osgi.common.OSGiResourceConfig&lt;/a&gt;&lt;/span&gt;. The source is slightly too big for the posting, so just click on the link if you want to see it.&lt;br /&gt;&lt;br /&gt;For my OSGi version I have to actually specify the classes that are annotated in a property called &lt;span style="font-family:courier new;" &gt;jersey_osgi.classnames&lt;/span&gt;. So I change to properties like this:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"&gt;HttpService httpService = ...; // The OSGi HTTP Service&lt;br /&gt;Hashtable&lt;String, String&gt; initParams = new Hashtable&lt;String, String&gt;();&lt;br /&gt;initParams.put(&lt;br /&gt;&amp;nbsp;&amp;nbsp;"com.sun.ws.rest.config.property.resourceConfigClass",&lt;br /&gt;&amp;nbsp;&amp;nbsp;"jersey_osgi.common.OSGiResourceConfig");&lt;br /&gt;initParams.put(&lt;br /&gt;&amp;nbsp;&amp;nbsp;"jersey_osgi.classnames",&lt;br /&gt;&amp;nbsp;&amp;nbsp;"jersey_osgi.rest.NumberResource");&lt;br /&gt;        &lt;br /&gt;Servlet jerseyServlet =&lt;br /&gt;&amp;nbsp;&amp;nbsp;new com.sun.ws.rest.spi.container.servlet.ServletContainer();                &lt;br /&gt;httpService.registerServlet("/fun", jerseyServlet, initParams, null);&lt;/span&gt;&lt;br /&gt;And run it. Now I'm getting:&lt;br /&gt;&lt;span style="font-family:courier new;" &gt;com.sun.ws.rest.api.container.ContainerException: No WebApplication provider is present&lt;/span&gt; aaaargh! After some debugging I found out that Jersey heavily depends on the &lt;span style="font-family:courier new;" &gt;META-INF/services&lt;/span&gt; SPI architecture which is quite non-OSGi friendly. There are only two possible solutions to this that I can think of:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;&lt;span style="font-family:courier new;" &gt;Export-Package META-INF.services&lt;/span&gt; in the Jersey Library Bundle. This works but is kinda nasty because you can only do it once in a single OSGi runtime. If you have multiple bundles exporting the same package, the OSGi runtime will only link you to only one of them for this package. If multiple bundles export &lt;span style="font-family:courier new;" &gt;META-INF.services&lt;/span&gt; you will get unexpected behaviour.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Copy the &lt;span style="font-family:courier new;" &gt;META-INF/services&lt;/span&gt; directory from the jersey.jar into our own bundle. Other projects with OSGi bundles seem to be taking this approach too.&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;So I'm taking approach 2 as well and copy all the &lt;span style="font-family:courier new;" &gt;META-INF/services&lt;/span&gt; files of jersey.jar into my bundle. And now it works! I can nicely build my Jersey apps as OSGi bundles that all depend on shared Jersey code.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Where's the Spring stuff?&lt;/span&gt;&lt;br /&gt;Spring-DM makes it really easy for me to interact with the OSGi HTTP Service as it simply injects it in my JerseyServletFactory class which then registers the Jersey Servlet. To trigger this, I put a &lt;span style="font-family:courier new;" &gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/jersey_osgi/JerseyOsgiSpringDM/META-INF/spring/jersey_osgi_spring.xml"&gt;jersey_osgi_spring.xml&lt;/a&gt;&lt;/span&gt; file in the META-INF/spring directory of my bundle:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans"&lt;br /&gt;&amp;nbsp;&amp;nbsp;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;&amp;nbsp;&amp;nbsp;xmlns:osgi="http://www.springframework.org/schema/osgi"&lt;br /&gt;&amp;nbsp;&amp;nbsp;xsi:schemaLocation="..."&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;osgi:reference id="httpServiceRef"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;interface="org.osgi.service.http.HttpService"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;bean name="jerseyServletFactory"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;class="jersey_osgi.common.JerseyServletFactory" &amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="jerseyClassNames"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;value="jersey_osgi.rest.NumberResource"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="rootContext" value="/fun"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;property name="httpService" ref="httpServiceRef"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/bean&amp;gt;&lt;br /&gt;&amp;lt;/beans&lt;/span&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;" &gt;JerseyServletFactory&lt;/span&gt; registers the Jersey Servlet with the OSGi HTTP Service. For this it needs that HTTP Service in the first place. Those who had the pleasure of using the pure OSGi API's to get hold of an OSGi Service know that this is actually slightly tricky. So OSGi Declarative Services come to the rescue (which I used in the earlier article about writing a &lt;a href="http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications.html"&gt;Card Game with OSGi Services&lt;/a&gt;). Spring-DM provides an alternative to OSGi-DS and brings with it all of the goodies that Spring has to offer. With the above Spring file, my &lt;span style="font-family:courier new;" &gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/jersey_osgi/JerseyOsgiSpringDM/src/jersey_osgi/common/JerseyServletFactory.java"&gt;JerseyServletFactory&lt;/a&gt;&lt;/span&gt; is nicely reusable (the Root Context and Jersey resource classes are also externalized into the Spring Configuration file) and no bigger than this:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"&gt;package jersey_osgi.common;&lt;br /&gt;&lt;br /&gt;import java.util.Hashtable;&lt;br /&gt;import javax.servlet.Servlet;&lt;br /&gt;import org.osgi.service.http.HttpService;&lt;br /&gt;import com.sun.ws.rest.spi.container.servlet.ServletContainer;&lt;br /&gt;&lt;br /&gt;public class JerseyServletFactory {&lt;br /&gt;&amp;nbsp;&amp;nbsp;private String classNames;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private String rootContext;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;// Spring uses the setters to inject our stuff&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void setJerseyClassNames(String names) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;classNames = names;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;public void setRootContext(String ctx) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rootContext = ctx;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;    &lt;br /&gt;&amp;nbsp;&amp;nbsp;public void setHttpService(HttpService httpService) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;Hashtable&amp;lt;String, String&amp;gt; initParams = &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Hashtable&amp;lt;String, String&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;initParams.put(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"com.sun.ws.rest.config.property.resourceConfigClass",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"jersey_osgi.common.OSGiResourceConfig");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;initParams.put(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"jersey_osgi.classnames",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;classNames);&lt;br /&gt;        &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Servlet jerseyServlet =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new ServletContainer();                &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;httpService.registerServlet(rootContext,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;jerseyServlet, initParams, null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Running it all inside Eclipse&lt;/span&gt;&lt;br /&gt;Running Spring-DM inside Equinox as shipped with Eclipse requires careful definition of the runtime environment. You set this up by going to &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Run | Open Run Dialog ...&lt;/span&gt;&lt;/span&gt; in Eclipse where you create a new OSGi Framework run configuration. Deselect the Target Platform tickbox and only select the following bundles:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"&gt;JerseyLibBundle_1.0.0&lt;br /&gt;JerseyOsgiSpringDM_1.0.0&lt;br /&gt;javax.servlet_2.4.0.v200706111738&lt;br /&gt;org.apache.commons.logging_1.0.4.v200706111724&lt;br /&gt;org.eclipse.core.jobs_3.3.1.R33x_v20070709&lt;br /&gt;org.eclipse.core.runtime.compatibility.registry_3.2.100.v20070316&lt;br /&gt;org.eclipse.equinox.common_3.3.0.v20070426&lt;br /&gt;org.eclipse.equinox.http.jetty_1.0.1.R33x_v20070816&lt;br /&gt;org.eclipse.equinox.http.registry_1.0.1.R33x_v20071231&lt;br /&gt;org.eclipse.equinox.http.servlet_1.0.1.R33x_v20070816&lt;br /&gt;org.eclipse.equinox.registry_3.3.1.R33x_v20070802&lt;br /&gt;org.eclipse.osgi.services_3.1.200.v20070605&lt;br /&gt;org.eclipse.osgi_3.3.2.R33x_v20080105&lt;br /&gt;org.mortbay.jetty_5.1.11.v200706111724&lt;br /&gt;org.springframework.bundle.osgi.core_1.0.2&lt;br /&gt;org.springframework.bundle.osgi.extender_1.0.2&lt;br /&gt;org.springframework.bundle.osgi.io_1.0.2&lt;br /&gt;org.springframework.bundle.spring.aop_2.5.1&lt;br /&gt;org.springframework.bundle.spring.beans_2.5.1&lt;br /&gt;org.springframework.bundle.spring.context_2.5.1&lt;br /&gt;org.springframework.bundle.spring.core_2.5.1&lt;br /&gt;org.springframework.osgi.aopalliance.osgi_1.0.0.SNAPSHOT&lt;/span&gt;&lt;br /&gt;Like this (I edited the unselected bundles out of the screenshot):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R-0yutRginI/AAAAAAAAAHU/WUs52L1SEm0/s1600-h/run_configuration.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R-0yutRginI/AAAAAAAAAHU/WUs52L1SEm0/s400/run_configuration.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5182854524181711474" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I found that if I don't do it exactly like this that I'm getting problems with the correct logger being instantiated, but this configuration works. Just hit 'Run' and you're off. You can now access the REST application from &lt;a href="http://localhost/fun/numbers"&gt;http://localhost/fun/numbers&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/R-0y6dRgioI/AAAAAAAAAHc/PyUqhqPMiy4/s1600-h/Numbers.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/R-0y6dRgioI/AAAAAAAAAHc/PyUqhqPMiy4/s400/Numbers.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5182854726045174402" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I can guess a number: &lt;a href="http://localhost/fun/numbers/5"&gt;http://localhost/fun/numbers/5&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/R-0y_9RgipI/AAAAAAAAAHk/VT7QbJYzDRI/s1600-h/Number5.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/R-0y_9RgipI/AAAAAAAAAHk/VT7QbJYzDRI/s400/Number5.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5182854820534454930" /&gt;&lt;/a&gt;&lt;br /&gt;... and try again: &lt;a href="http://localhost/fun/numbers/3"&gt;http://localhost/fun/numbers/3&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/R-0zLdRgiqI/AAAAAAAAAHs/jtRiDzC4nkc/s1600-h/Number3.JPG"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/R-0zLdRgiqI/AAAAAAAAAHs/jtRiDzC4nkc/s400/Number3.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5182855018102950562" /&gt;&lt;/a&gt;&lt;br /&gt;Yay!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Get the code&lt;/span&gt;&lt;br /&gt;You can get all the code (Apache License) as usual from the google code site. It's at available as 2 Eclipse projects at &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/jersey_osgi/"&gt;http://coderthoughts.googlecode.com/svn/trunk/jersey_osgi/&lt;/a&gt;. The easiest way to get them into your Eclipse installation is by using an SVN client inside Eclipse and check them out as projects. This is described in more detail in &lt;a href="http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications.html"&gt;this posting&lt;/a&gt; (just search for the 'subclipse' in there).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-8803408796186276383?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/8803408796186276383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=8803408796186276383' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/8803408796186276383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/8803408796186276383'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2008/03/using-jersey-jsr311-inside-osgi-with.html' title='Using Jersey (JSR311) inside OSGi with Spring-DM'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JTmY6gtOOjs/R-01rNRgirI/AAAAAAAAAH0/VsM_S9zKJiQ/s72-c/schema.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-7693338024506628943</id><published>2007-12-03T11:00:00.000-08:00</published><updated>2008-11-13T12:53:48.870-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi Java cardgame Eclipse Equinox'/><title type='text'>OSGi Services for Dynamic Applications (IV)</title><content type='html'>&lt;span style="font-weight: bold;font-size:130%;" &gt;Or: use OSGi to write a dueling card game - part 4&lt;/span&gt;&lt;br /&gt;In the &lt;a href="http://coderthoughts.blogspot.com/2007/12/osgi-services-for-dynamic-applications.html"&gt;previous postings&lt;/a&gt; I created the basics of a dueling card game using OSGi services. In this last posting of this series I'm going to add some special spell cards. Finally I'll use the capabilities of OSGi to dynamically add a bundle with new cards to the ongoing games without having to interrupt them.&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Adding Spell Cards&lt;br /&gt;&lt;/span&gt;Spells make the game really interesting as they can influence the game beyond the standard rules. They can do things like change the power of a monster, inflict damage on a player or another card or generally change the game state. Sometimes spells can behave like monsters or monsters can have additional spell-like behaviour too. My &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/"&gt;SpellDeck&lt;/a&gt; bundle adds a number of them, each of which has special abilities and a special implementation.&lt;br /&gt;&lt;br /&gt;In my Activator, I'm registering my spell cards (5 of each):&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;public class Activator implements BundleActivator {&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void start(BundleContext ctx) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new ThunderBoltCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new ReflectCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new SkipBattlePhaseCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new DoubleAttackCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new MedicinCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new ExplosionCard()); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new LightningBoltCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new DoubleStrengthCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new HalfStrengthCard());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;   &lt;br /&gt;&amp;nbsp;&amp;nbsp;private void registerCard(BundleContext ctx, Card c) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ctx.registerService(Card.class.getName(), c,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Hashtable());&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Let's zoom in on a few of them: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/SpellDeck/src/spelldeck/ThunderBoltCard.java"&gt;ThunderBoltCard&lt;/a&gt;, &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/SpellDeck/src/spelldeck/SkipBattlePhaseCard.java"&gt;SkipBattlePhaseCard&lt;/a&gt;, &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/SpellDeck/src/spelldeck/ReflectCard.java"&gt;ReflectCard&lt;/a&gt; and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/SpellDeck/src/spelldeck/DoubleStrengthCard.java"&gt;DoubleStrengthCard&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;ThunderBoltCard&lt;/span&gt;&lt;br /&gt;This spell card can be played during a player's Standby or Recover phase and deals 20 damage directly to the opponent. This functionality is implemented in the &lt;span style="font-family:courier new;" &gt;execute()&lt;/span&gt; of this Command Card.&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;public class ThunderBoltCard extends AbstractCommandCard implements Card, Command {&lt;br /&gt;&amp;nbsp;&amp;nbsp;public ThunderBoltCard() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;super(CardType.SPELL, "Thunderbolt",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Thunderbolt deals 20 damage to your opponent.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public boolean execute(Game game) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Player p = game.toGrave(this); // discard this card  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Player q = game.getOtherPlayer();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q.setScore(q.getScore() - 20);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printBoth(game, p + " plays Thunderbolt, " + &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q + " loses 20 life");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;SkipBattlePhaseCard&lt;/span&gt;&lt;br /&gt;This card can be played by a player who is being attacked during battle. It will end the current Battle phase aborting any pending attacks and move to the Recover phase. Here's the code:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;public class SkipBattlePhaseCard extends AbstractCommandCard implements Card, Command {&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected SkipBattlePhaseCard() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;super(CardType.SPELL, "Battle Skipper", &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"With battle skipper, your opponent must drop " +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"the current attack and skip the battle phase.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public boolean execute(Game game) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Player p = game.getOwner(this);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (game.getPlayer() == p) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;p.getUI().print("Can only play this card during " +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"your opponent's battle phase when attacked.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return false;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;} else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;game.setAttacking(new ArrayList&amp;lt;Fighter&amp;gt;());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;game.setPhase(Phase.RECOVER);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;game.toGrave(this);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;printBoth(game, p + " plays Battle Skipper, " +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;which causes the current battle phase to end.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;span style="font-weight: bold;"&gt;ReflectCard&lt;/span&gt;&lt;br /&gt;This spell can also be played by a player who is being attacked during battle. It will reflect the same amount of defense back to the attacker as was the original attack. Typically this will destroy the attacking monster.&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;public class ReflectCard extends AbstractCard implements Card, Fighter {&lt;br /&gt;&amp;nbsp;&amp;nbsp;private int defense;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public ReflectCard() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;super(CardType.SPELL, "Reflect",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Reflects an attack back to the attacker as " +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"defense, will normally destroy the attacker.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void damage(Game game, int amount) {}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public int getAttack() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public int getDefense() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return defense;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public boolean responding(Game game) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;defense = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// calculate the defense&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (Fighter f : game.getAttacking()) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;defense += game.getAttack(f);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;DoubleStrengthCard&lt;/span&gt;&lt;br /&gt;This is an attached card, which means that it is attached to another Fighter card on the field. It doubles the attack of the fighter card. The attachment behaviour of this card is implemented in the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/AbstractAttachedCard.java"&gt;AbstractAttachedCard&lt;/a&gt; base class. This card implements the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Modifier.java"&gt;Modifier&lt;/a&gt; interface, which is taken into account when a monster's strength is calculated.&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;public class DoubleStrengthCard extends AbstractAttachedCard implements Card, Modifier {&lt;br /&gt;&amp;nbsp;&amp;nbsp;protected DoubleStrengthCard() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// This card can be attached to a Monster card on&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// the table of type Fighter&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;super("Double Strength",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Area.TABLE, Fighter.class, CardType.MONSTER, &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Target creature gets double the attack.");&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public int addToAttack(Game game, Fighter fighter) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (fighter instanceof Card) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Player owner = game.getOwner((Card) fighter);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (owner != null &amp;&amp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;owner.getTable().contains(fighter)) {&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;  &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return fighter.getAttack();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public int addToDefense(Game game, Fighter fighter) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}   &lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Get the code of the other spells by checking out the SpellDeck Bundle from the usual location: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/"&gt;http://coderthoughts.googlecode.com/svn/trunk/duel/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Adding More Monsters while playing&lt;br /&gt;&lt;/span&gt;Last but not least, I'm going to add a whole bunch of extra monsters to my game, and I'm going to do this &lt;span style="font-style: italic;"&gt;while&lt;/span&gt; a game is being played. In a development scenario this is not really normal, but it will be needed once our application is successful and in production. Let's say our application is used by many users and we don't want to disturb our users by the mere action of adding a few new cards to the game. We just want those cards to appear in any ongoing games and in any new games from now on. This is where OSGi will &lt;span style="font-style: italic;"&gt;really&lt;/span&gt; help us. It allows us to &lt;span style="font-weight: bold;"&gt;add&lt;/span&gt; and &lt;span style="font-weight: bold;"&gt;remove&lt;/span&gt; bundles without the need to restart the container. If those new bundles contain new Card service my game controller will be notified of them straight away. Similarly if a bundle that contains cards is being removed, we will be told about it too. If our application (bundles) interact with other bundles via OSGi Services, we will be able to patch our entire application without taking it down. To show this functionality in action I'm going to add the MoreMonsters bundle to the OSGi runtime after the game has started, but first of all I need to slightly modify the &lt;span style="font-family:courier new;"&gt;addCard()&lt;/span&gt; method in my &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/Duel/src/game/Controller.java"&gt;Controller&lt;/a&gt; class to shuffle newly arrived cards into the decks of any ongoing games.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;public void addCard(Card card) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;synchronized (cards) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cards.add(card);&lt;br /&gt;&lt;br/&gt;     &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// add the new card to any game deck in &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// progress in a random place&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (Game g : games) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Shuffling new card into existing game " + card);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;List&amp;lt;card&amp;gt; deck = g.getDeck();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;deck.add(rand.nextInt(deck.size()), card);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To get started, first check out the MoreMonsters bundle from the usual SVN location: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel"&gt;http://coderthoughts.googlecode.com/svn/trunk/duel&lt;/a&gt;. This bundle only contains an &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/MoreMonsters/src/moremonsters/Activator.java"&gt;Activator &lt;/a&gt;in which additional monsters (instances of the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/MonsterDeck/src/monsterdeck/MonsterCard.java"&gt;MonsterCard&lt;/a&gt;) are created and registered as Card services (courtesy to my 9 and 11 year old kids, who created these monsters).&lt;br /&gt;&lt;br /&gt;I want the MoreMonsters bundle to be added &lt;span style="font-style:italic;"&gt;while&lt;/span&gt; the game is in progress, so I need to make sure that it's not automatically started with the other bundles. For this, the launch confiration that I'm using in Eclipse needs to be tweaked, I'm setting the 'Auto-Start' of MoreMonsters to &lt;span style="font-family:courier new;"&gt;false&lt;/span&gt;:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0rqr1yJa2I/AAAAAAAAAF0/ArjWkb6ilJY/s1600-h/dyna_run.GIF"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0rqr1yJa2I/AAAAAAAAAF0/ArjWkb6ilJY/s400/dyna_run.GIF" alt="" id="BLOGGER_PHOTO_ID_5137176363862158178" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;You may also need to select the 'Clear the configuration area before launching' option in the 'Settings' tab of the launch configuration, because Equinox remembers your first settings and sometimes doesn't pick up changes like this without clearning the configuration area.&lt;br /&gt;&lt;br /&gt;Hit 'Run' and the game will start as usual, with the original three monsters and our spell cards.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/R0rv0lyJa3I/AAAAAAAAAF8/GKXtbZvLN1Q/s1600-h/dyna_run2.GIF"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/R0rv0lyJa3I/AAAAAAAAAF8/GKXtbZvLN1Q/s400/dyna_run2.GIF" alt="" id="BLOGGER_PHOTO_ID_5137182011744152434" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;If you go into the Equinox console and type 'ss', you'll see that the MoreMonsters bundle is there, but it's just not started:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;&lt;br /&gt;osgi&amp;gt; &lt;span style="color: rgb(0, 153, 0); font-weight: bold;"&gt;ss&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Framework is launched.&lt;br /&gt;&lt;br /&gt;id State       Bundle&lt;br /&gt;0 ACTIVE      org.eclipse.osgi_3.3.1.R33x_v20070828&lt;br /&gt;1 ACTIVE      org.eclipse.osgi.services_3.1.200.v20070605&lt;br /&gt;3 RESOLVED    MoreMonsters_1.0.0&lt;br /&gt;4 ACTIVE      DuelInterfaces_1.0.0&lt;br /&gt;7 ACTIVE      javax.servlet_2.4.0.v200706111738&lt;br /&gt;8 ACTIVE      org.eclipse.equinox.ds_1.0.0.v20070226&lt;br /&gt;9 ACTIVE      SpellDeck_1.0.0&lt;br /&gt;10 ACTIVE      Duel_1.0.0&lt;br /&gt;11 ACTIVE      SimpleUI_1.0.0&lt;br /&gt;12 ACTIVE      MonsterDeck_1.0.0&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;So let's start the bundle!&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;&lt;br /&gt;osgi&amp;gt; &lt;span style="color: rgb(0, 153, 0); font-weight: bold;"&gt;start 3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And we'll see our new cards arriving:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;" &gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;Shuffling new card into existing game Thundergirl&lt;br /&gt;&amp;nbsp;&amp;nbsp;Shuffling new card into existing game DB Machine&lt;br /&gt;&amp;nbsp;&amp;nbsp;Shuffling new card into existing game Devil Spider&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;...and so on...&amp;gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Soon you'll see your new cards arriving in your current game:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0rwa1yJa5I/AAAAAAAAAGM/b1-Mg4swggA/s1600-h/dyna_run3.GIF"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0rwa1yJa5I/AAAAAAAAAGM/b1-Mg4swggA/s400/dyna_run3.GIF" alt="" id="BLOGGER_PHOTO_ID_5137182668874148754" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In this case the MoreMonsters Bundles was automatically installed by Eclipse for me, it was just not started. A more realistic real-world scenario would be where the OSGi container is running outside of Eclipse. In that case I would first have to install the bundle (Equinox has the &lt;span style="font-family:courier new;"&gt;install&lt;/span&gt; command for that) before I can actually start it.&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Dynamically removing services&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;The same mechanism can be used to dynamically remove services from the system. I'll leave the implementation of that to the reader, but I hope you can see how powerful this mechanism can be. If you need to patch the an OSGi Service-based system, just install the patch bundle first, which will registered the patched services, then remove the old bundle, the old services will be unregistered which can make clients to switch to the patched services. Together with OSGi's strong versioning support, this is a great feature for mission critical systems that need to be up and running 24/7 (like game servers ;).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Equinox Specifics&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;In this posting I did use some Equinox-specific commands to control the OSGi container, other OSGi containers have other commands for achieving the same. All of the code in these postings should be 100% portable across the various OSGi containers.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Download the code&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;All the code for all these postings is available under the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/LICENSE-2.0.txt"&gt;Apache License&lt;/a&gt; in the google code &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/"&gt;Subversion Repository&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-7693338024506628943?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/7693338024506628943/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=7693338024506628943' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/7693338024506628943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/7693338024506628943'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/12/osgi-services-for-dynamic-applications_03.html' title='OSGi Services for Dynamic Applications (IV)'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JTmY6gtOOjs/R0rqr1yJa2I/AAAAAAAAAF0/ArjWkb6ilJY/s72-c/dyna_run.GIF' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-5715642142496408136</id><published>2007-12-02T11:00:00.000-08:00</published><updated>2008-11-13T12:53:49.942-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi Java cardgame Eclipse Equinox'/><title type='text'>OSGi Services for Dynamic Applications (III)</title><content type='html'>&lt;span style="font-weight: bold;font-size:130%;" &gt;Or: use OSGi to write a dueling card game, part 3&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Adding Monster cards to the game and a simple UI&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications_30.html"&gt;previous posting&lt;/a&gt; I've created the hardest bit of the game. The actual game controller. In this posting I'm going to create a small bundle that contains a few Monster cards and a simple UI bundle so that we can actually start playing.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Creating the Monster Deck bundle&lt;/span&gt;&lt;br /&gt;Every card in my Dueling game is an OSGi Service.&lt;br /&gt;In the Duel Bundle I used OSGi Declarative Services to activate the bundle and consume the services, and you can use DS also to register services, but in this case I'm going to use the plain OSGi API's for this. Registering a service is so simple in OSGi that in this case I'm just going to go API.&lt;br /&gt;&lt;br /&gt;My monster cards instances are realized by a class called &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/MonsterDeck/src/monsterdeck/MonsterCard.java"&gt;MonsterCard&lt;/a&gt;, which implements the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Card.java"&gt;Card&lt;/a&gt;, &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Fighter.java"&gt;Fighter&lt;/a&gt; and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Command.java"&gt;Command&lt;/a&gt; interfaces. It's nice and simple, also because it extends &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/AbstractCommandCard.java"&gt;AbstractCommandCard&lt;/a&gt;, which means that &lt;span style="font-family: courier new"/&gt;Command.execute()&lt;/span&gt; for the Standby and Recover phase are implemented as placing the card on the table. Other basic Card requirements such as accessors to the name, ID and description and proper implementations of &lt;span style="font-family: courier new"/&gt;equals()&lt;/span&gt;, &lt;span style="font-family: courier new"/&gt;hashcode()&lt;/span&gt; and &lt;span style="font-family: courier new"/&gt;toString()&lt;/span&gt; are handled by &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/AbstractCard.java"&gt;AbstractCard&lt;/a&gt;, the superclass of AbstractCommandCard. My MonsterCard class basically adds the implementation of the Fighter interface into the mix. It also specifies when the card is enabled in &lt;span style="font-family: courier new"/&gt;conditions()&lt;/span&gt; - in the Standby phase and the Recover phase, and also in the Battle phase, but only when it's on the table.&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;public class MonsterCard extends AbstractCommandCard implements Fighter, Card, Command {&lt;br /&gt;&amp;nbsp;&amp;nbsp;private int attack;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private int defense;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public MonsterCard(String name, String desc,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;int att, int def) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;super(CardType.MONSTER, name, desc);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;attack = att;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;defense = def;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void damage(Game game, int amount) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// nothing to do on damage&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// the battle code will discard when hit&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public int getAttack() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return attack;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public int getDefense() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return defense;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public Condition [] conditions() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return new Condition [] {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Condition(Phase.STANDBY),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Condition(Area.TABLE, Phase.BATTLE),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Condition(Phase.RECOVER)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;};&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public boolean responding(Game game) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true; // can respond to any attacker&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;To create any straight monster without any special abilities, I can simply use this class. I'm going to create 3 different monster cards, Super Hero, Floppie and Diamond Buster:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;&amp;nbsp;&amp;nbsp;new MonsterCard("Super Hero",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"This is the invincible super hero.", 60, 35));&lt;br /&gt;&amp;nbsp;&amp;nbsp;new MonsterCard("Floppie", &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Floppie joppie.", 15, 25));&lt;br /&gt;&amp;nbsp;&amp;nbsp;new MonsterCard("Diamond Buster", &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Diamond buster is as strong as a rock.", 40, 55));&lt;br /&gt;&lt;/span&gt;To turn these POJO's into OSGi services, I need to register them as services with the &lt;a href="http://www2.osgi.org/javadoc/r4/org/osgi/framework/BundleContext.html"&gt;BundleContext.registerService()&lt;/a&gt;, defined as:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;&amp;nbsp;&amp;nbsp;registerService(java.lang.String clazz,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;java.lang.Object service,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;java.util.Dictionary properties)&lt;/span&gt;&lt;br /&gt;Simply pass in the name of the class as the first argument, the actual object and optionally a map of properties. The properties can be used when selecting services as a service consumer, but I'm not using them here.&lt;br /&gt;&lt;br /&gt;So to get 5 of each card registered when the MonsterDeck bundle is started, a small OSGi &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/MonsterDeck/src/monsterdeck/Activator.java"&gt;Activator&lt;/a&gt; class does it all:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;package monsterdeck;&lt;br/&gt;&lt;br /&gt;import game.model.Card;&lt;br /&gt;import java.util.Hashtable;&lt;br /&gt;import org.osgi.framework.BundleActivator;&lt;br /&gt;import org.osgi.framework.BundleContext;&lt;br/&gt;&lt;br /&gt;public class Activator implements BundleActivator {&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void start(BundleContext ctx) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (int i = 0; i &amp;lt; 5; i++) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new MonsterCard("Super Hero",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"This is the invincible super hero.", 60, 35));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new MonsterCard("Floppie",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"Floppie joppie.", 15, 25));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;registerCard(ctx, new MonsterCard("Diamond Buster",&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;"As strong as a rock.", 40, 55));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;private void registerCard(BundleContext ctx, Card c) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ctx.registerService(Card.class.getName(), c,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Hashtable());&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public void stop(BundleContext context)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;throws Exception { }&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;The only thing left to do it create the bundle-manifest in the &lt;span style="font-family: courier new" &gt;META-INF/MANIFEST.MF&lt;/span&gt; file:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;&amp;nbsp;&amp;nbsp;Bundle-Name: MonsterDeck Bundle&lt;br /&gt;&amp;nbsp;&amp;nbsp;Bundle-SymbolicName: MonsterDeck&lt;br /&gt;&amp;nbsp;&amp;nbsp;Bundle-Version: 1.0.0&lt;br /&gt;&amp;nbsp;&amp;nbsp;Bundle-Activator: monsterdeck.Activator&lt;br /&gt;&amp;nbsp;&amp;nbsp;Import-Package: game.model,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;org.osgi.framework&lt;br /&gt;&amp;nbsp;&amp;nbsp;Export-Package: monsterdeck&lt;/span&gt;&lt;br /&gt;I have to import the &lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;org.osgi.framework&lt;/span&gt; package since in my Activator I'm using the &lt;a href="http://www2.osgi.org/javadoc/r4/org/osgi/framework/BundleActivator.html"&gt;BundleActivator&lt;/a&gt; and &lt;a href="http://www2.osgi.org/javadoc/r4/org/osgi/framework/BundleContext.html"&gt;BundleContext&lt;/a&gt; classes from this package. I'm exporting the &lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;monsterdeck&lt;/span&gt; package from this bundle so that I can use the MonsterCard class later in another bundle.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Adding a simple UI&lt;/span&gt;&lt;br /&gt;I've tried to create the simplest UI possible. Its written using &lt;a href="http://www.eclipse.org/swt/"&gt;SWT&lt;/a&gt; and simply implements UserInterface. I just wanted to keep this part as simple as possible, but (as always) GUI classes tend to become relatively big. My GUI looks like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R0cEl1yJasI/AAAAAAAAAEk/b3DhZfnXQlU/s1600-h/shot.GIF"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R0cEl1yJasI/AAAAAAAAAEk/b3DhZfnXQlU/s400/shot.GIF" alt="" id="BLOGGER_PHOTO_ID_5136078948178422466" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;At the top you see the cards your opponent has on the table.&lt;br /&gt;Below that is a status area where messages are displayed.&lt;br /&gt;Then the cards you have on the table are shown and at the bottom are the cards that you have in your hand.&lt;br /&gt;On the right hand side there is a status panel where each player's life points are shown, the current game phase is displayed and buttons are present to draw a card, end the current phase and take the damage of a fight.&lt;br /&gt;Monster cards are shown red and spell cards are green. To select a card, you simply click on it. Generally only cards and actions that make sense in the current context are enabled. Attacking monsters have their names shown in &lt;span style="font-weight: bold; font-style: italic;"&gt;italics&lt;/span&gt; and card descriptions can be read by hovering with the mouse over a card.&lt;br /&gt;&lt;br /&gt;The UI bundles comes with a little factory that is launched as soon as the bundles is started. Type your name in the UI Factory and it will create a User Interface for you which is then registered as a UserInterface Service with OSGi.&lt;img src="file:///c:/temp/moz-screenshot-9.jpg" alt="" /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0cFcFyJauI/AAAAAAAAAE0/0j2fIkL9LDU/s1600-h/uifactory.GIF"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0cFcFyJauI/AAAAAAAAAE0/0j2fIkL9LDU/s400/uifactory.GIF" alt="" id="BLOGGER_PHOTO_ID_5136079880186325730" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;You can get this UI implementation by checking it out the SimpleUI bundle from the usual SVN location: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/"&gt;http://coderthoughts.googlecode.com/svn/trunk/duel/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Running the game&lt;/span&gt;&lt;br /&gt;This is really easy if you're using Eclipse.  What you should have is 4 OSGi Bundle Projects:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/R03uG1yJa9I/AAAAAAAAAGs/OVvr9A1c7cg/s1600-h/run1.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/R03uG1yJa9I/AAAAAAAAAGs/OVvr9A1c7cg/s400/run1.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5138024551183641554" /&gt;&lt;/a&gt;&lt;br /&gt;Select the Run -&gt; Open Run Dialog... menu and double-click on the OSGi Framework item in the tree. This will create a new OSGi launch configuration for you. By default it will have all the OSGi bundles in the system selected, which is waaaay too much, so I normally deselect the root 'Target Platform' node and then click 'Add Required Bundles':&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R03uSVyJa-I/AAAAAAAAAG0/y_powV_xiuk/s1600-h/run2.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R03uSVyJa-I/AAAAAAAAAG0/y_powV_xiuk/s400/run2.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5138024748752137186" /&gt;&lt;/a&gt;&lt;br /&gt;Hit 'Run' and you're off. The UI Controller will show up and after creating two UI's the game will start. &lt;br /&gt;The game controller prints the names of the users as soon as they register their UI services and when two users are in, it will shuffle the cards and start the game. My console shows:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;&amp;nbsp;&amp;nbsp;Adding user interface for: Pinky&lt;br /&gt;&amp;nbsp;&amp;nbsp;Adding user interface for: Brain&lt;br /&gt;&amp;nbsp;&amp;nbsp;Shuffling deck...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I can Draw a card:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R0mXzVyJazI/AAAAAAAAAFc/j2p7dXQJ08I/s1600-h/play1.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R0mXzVyJazI/AAAAAAAAAFc/j2p7dXQJ08I/s400/play1.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5136803758269360946" /&gt;&lt;/a&gt;&lt;br /&gt;Play a monster (e.g. Super Hero) on the table:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0mXzlyJa0I/AAAAAAAAAFk/huc_gwJrNpE/s1600-h/play2.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0mXzlyJa0I/AAAAAAAAAFk/huc_gwJrNpE/s400/play2.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5136803762564328258" /&gt;&lt;/a&gt;&lt;br /&gt;and engage in battle: my opponent attacks me with Diamond Buster, I can choose to defend with Super Hero, but he will die, alternatively I can take the damage myself and lose life points.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/R0mXz1yJa1I/AAAAAAAAAFs/qS4Qz-2_cLY/s1600-h/play3.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/R0mXz1yJa1I/AAAAAAAAAFs/qS4Qz-2_cLY/s400/play3.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5136803766859295570" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Before I forget: closing all the windows will not actually exit the system. The OSGi container will keep running. The command to stop the OSGi container is implementation-specific, if you're using Equinox just type in &lt;span style="font-family: courier new" &gt;exit&lt;/span&gt; in the console. &lt;br /&gt;&lt;br /&gt;With only 3 different cards in the game it is a little bit boring to say the least. In the &lt;a href="http://coderthoughts.blogspot.com/2007/12/osgi-services-for-dynamic-applications_03.html"&gt;next posting&lt;/a&gt; I'll add some cool spell cards and a bunch more monsters too!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-5715642142496408136?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/5715642142496408136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=5715642142496408136' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5715642142496408136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/5715642142496408136'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/12/osgi-services-for-dynamic-applications.html' title='OSGi Services for Dynamic Applications (III)'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JTmY6gtOOjs/R0cEl1yJasI/AAAAAAAAAEk/b3DhZfnXQlU/s72-c/shot.GIF' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-4333512594402774398</id><published>2007-11-30T11:50:00.000-08:00</published><updated>2008-11-13T12:53:50.727-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi Java cardgame Eclipse Equinox'/><title type='text'>OSGi Services for Dynamic Applications (II)</title><content type='html'>&lt;span style="font-weight: bold;font-size:130%;" &gt;Or: use OSGi to write a dueling card game, part 2: the Game Controller OSGi Services client&lt;/span&gt;&lt;br /&gt;In this posting I'm going to write the Game Controller which is a client to the User Interface &lt;a href="http://www.osgi.org/"&gt;OSGi&lt;/a&gt; Services and also a client to the Card OSGi services.&lt;br /&gt;&lt;br /&gt;The following diagram from the &lt;a href="http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications.html"&gt;first posting&lt;/a&gt; outlines the game architecture:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0YF0VyJapI/AAAAAAAAAEM/1atxcUEGHAA/s1600-h/osgi_schematic2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0YF0VyJapI/AAAAAAAAAEM/1atxcUEGHAA/s400/osgi_schematic2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5135798821821442706" /&gt;&lt;/a&gt;&lt;br /&gt;The yellow triangles represent the OSGi services, the red rectangles are bundles that provide service implementations. The Game Controller is a client to the services and lives in an OSGi bundle too.&lt;br /&gt;&lt;br /&gt;I am using OSGi Declarative Services to handle the client side of the OSGi services. I'm doing this because DS is easier than using the OSGi &lt;a href="http://www2.osgi.org/javadoc/r4/org/osgi/util/tracker/ServiceTracker.html"&gt;ServiceTracker&lt;/a&gt; API, and it's part of the OSGi spec.&lt;br /&gt;&lt;br /&gt;I always find the easiest way to develop an OSGi bundle by using the Eclipse tooling that you get with the Eclipse Plugin Development Environment (&lt;a href="http://www.eclipse.org/downloads/"&gt;download from here&lt;/a&gt;). With that you simply go: File -&gt; New Project... -&gt; Plug-in project. In the following window you simply select 'OSGi Platform' as the target platform. If you specify 'standard' it means that you won't be using any Equinox specific bits.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0_uuFyJbAI/AAAAAAAAAHE/mzEH-4PQ8nE/s1600-R/pluginwizard.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0_uuFyJbAI/AAAAAAAAAHE/vi4XJERy8jE/s400/pluginwizard.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5138588175446928386" /&gt;&lt;/a&gt;&lt;br /&gt;On the next page you can specify some details around the plugin ID, name and version. The defaults are generally fine but for this posting you won't need an activator as Declarative Services will take care of the stuff that is typically done in an activator for us, so you can deselect that option. Finally, you don't need to use any of the templates that come with Eclipse for this posting.&lt;br /&gt;Hitting 'Finish' will get you an Eclipse project that is marked as a PDE project (which is an OSGi project) with a &lt;span style="font-family:courier new;"&gt;META-INF/MANIFEST.MF&lt;/span&gt; file which defines the attributes of the OSGi bundle.&lt;br /&gt;&lt;br /&gt;My game bundle has two dependencies:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It depends on the DuelInterfaces bundle from the previous posting. All of its classes are in the &lt;span style="font-family: courier new;" &gt;game.model&lt;/span&gt; package, so I will be importing that package, which gives me the freedom to rename or reversion the DuelInterfaces bundle if needed.&lt;/li&gt;&lt;li&gt;Since I'm using Declarative Services it depends on a DS implementation. I'm using the one from Equinox / Eclipse, which doesn't come with Eclipse by default, you have to manually download and install it. To get it, go to the &lt;a href="http://download.eclipse.org/eclipse/equinox/"&gt;Equinox download page&lt;/a&gt;, select your Eclipse version (I took 3.3.1.1) and then look for the org.eclipse.equinox.ds_xxx.jar download. I downloaded &lt;a href="http://www.eclipse.org/downloads/download.php?file=/eclipse/equinox/drops/R-3.3.1.1-200710231652/org.eclipse.equinox.ds_1.0.0.v20070226.jar"&gt;this&lt;/a&gt; one. Then simply drop the downloaded jar file in the plugins directory of your Eclipse installation.&lt;br /&gt;Because I'm not using any specific classes from the DS implementation, I cannot use the 'Import Package' mechanism, so I've specified this bundle as a direct dependency in 'Required Plugins'.&lt;/li&gt;&lt;/ul&gt;I've entered the dependencies in my manifest, using the Eclipse manifest editor, which now looks like this.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/R03qQ1yJa8I/AAAAAAAAAGk/TQMzELPCZLI/s1600-h/dueldeps.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/R03qQ1yJa8I/AAAAAAAAAGk/TQMzELPCZLI/s400/dueldeps.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5138020324935822274" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;What I want DS to do for me is tell the Game Controller when someone has registered any new Card Services in the game or when a new User Interface is registered. When a UI is added, the controller will look for another UI that is not playing a game, if there is one it will hook them up and start the game for both players. If there is no free other UI, the newly added UI will be held until another UI presents itself. This is implemented in the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/Duel/src/game/Controller.java"&gt;Controller&lt;/a&gt; class which contains an addUI() method like this:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"  &gt;&lt;br /&gt;public synchronized void addUI(UserInterface ui) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;System.out.println("Adding UI for: " +&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ui.getPlayerName());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (freeUIs.size() &gt; 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;UserInterface otherUI = freeUIs.remove(0);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Player p1 = new Player(otherUI, 250);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Player p2 = new Player(ui, 250);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Init both UI's with players and static commands&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ui.initializeGame(p2, p1, new DrawCommand(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new NextPhaseCommand(), new SelfFighter(p2));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;otherUI.initializeGame(p1, p2, new DrawCommand(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new NextPhaseCommand(), new SelfFighter(p1));&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// Create a new game with all the cards available&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;final Game g =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new GameImpl(new ArrayList&lt;Card&gt;(cards), p1, p2);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new Thread(new Runnable() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;public void run() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;g.play();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}    &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}).start(); // play, don't block the OSGi thread&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;games.add(g);&lt;br /&gt;&amp;nbsp;&amp;nbsp;} else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;freeUIs.add(ui);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A similar, but even simpler method is called when new cards are registered, my addCard() looks like this:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"  &gt;public void addCard(Card card) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;synchronized (cards) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cards.add(card);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;But what do I do to get my methods called when these services get registered. This is where DS comes in. I have added a DS configuration file that instantiates my Controller component and gets DS to call me back when this happens. This pattern is called &lt;span style="font-style: italic;"&gt;Inversion of Control&lt;/span&gt; (IOC). I have added a DS configuration file in my Duel bundle project in &lt;span style="font-family:courier new;"&gt;OSGI-INF/controlcomponent.xml&lt;/span&gt;. I could really place this file anywhere in the bundle, but putting it in OSGI-INF seems to be the convention. It contains the following XML:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"  &gt;&amp;lt;component name="controller"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;implementation class="game.Controller"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;reference name="UI"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;interface="game.model.UserInterface"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bind="addUI"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unbind="removeUI"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cardinality="0..n"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;policy="dynamic"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;reference name="CARD"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;interface="game.model.Card"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;bind="addCard"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;unbind="removeCard"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cardinality="1..n"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;policy="dynamic"/&amp;gt;&lt;br /&gt;&amp;lt;/component&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is what tells DS to call the Controller class when a &lt;span style="font-family: courier new;"&gt;game.model.UserInterface&lt;/span&gt; and a &lt;span style="font-family: courier new;"&gt;game.model.Card&lt;/span&gt; service is registered. It defines a component called 'controller', specifies its class and service dependencies. I've set the policy to 'dynamic' so I get notified every time one of these services gets bound or unbound. Matter of fact, this component configuration file actually causes my &lt;span style="font-family: courier new;"&gt;game.Controller&lt;/span&gt; class to be instantiated in the first place so that's why I don't need an OSGi Activator here. DS does it for me.&lt;br /&gt;There's one last handy little nugget in here. You can see that the cardinality of the service dependencies is specified. For user interfaces it's 0 or more, but for cards it's 1 or more. The latter is handy, because it makes sure that my game Controller doesn't get created before there are a few cards registered. There's no point offering a card game to play without any cards!&lt;br /&gt;So as soon as there's a few cards, my &lt;span style="font-family: courier new;"&gt;game.Controller&lt;/span&gt; class gets created and players can make themselves known by registering a UI.&lt;br /&gt;&lt;br /&gt;We will need to tell DS about our &lt;span style="font-family: courier new;"&gt;OSGI-INF/controlcomponent.xml&lt;/span&gt; file. For this I have to reference it in the &lt;span style="font-family: courier new;"&gt;META-INF/MANIFEST.MF&lt;/span&gt; file. Mine looks like this:&lt;br /&gt;&lt;span style="font-weight: bold;font-family:courier new;font-size:85%;"  &gt;&amp;nbsp;&amp;nbsp;Bundle-Name: Duel Bundle&lt;br /&gt;&amp;nbsp;&amp;nbsp;Bundle-SymbolicName: Duel&lt;br /&gt;&amp;nbsp;&amp;nbsp;Bundle-Version: 1.0.0&lt;br /&gt;&amp;nbsp;&amp;nbsp;Import-Package: game.model&lt;br /&gt;&amp;nbsp;&amp;nbsp;Require-Bundle: org.eclipse.equinox.ds&lt;br /&gt;&amp;nbsp;&amp;nbsp;Service-Component: OSGI-INF/controlcomponent.xml&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I won't go into too much detail on the &lt;span style="font-family: courier new;"&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/Duel/src/game/GameImpl.java"&gt;game.GameImpl&lt;/a&gt;&lt;/span&gt; class. This is an implementation of the &lt;span style="font-family: courier new;"&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Game.java"&gt;game.model.Game&lt;/a&gt;&lt;/span&gt; interface. It's not very complicated but it's just a bit too long for a blog post; so just look at the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/Duel/src/game/GameImpl.java"&gt;code&lt;/a&gt; :)&lt;br /&gt;You can get the Game Implementation class with the Duel bundle from Subversion here: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/"&gt;http://coderthoughts.googlecode.com/svn/trunk/duel/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We can't start the game just yet, we first need to add a Bundle with a few cards. That's what the &lt;a href="http://coderthoughts.blogspot.com/2007/12/osgi-services-for-dynamic-applications.html"&gt;next posting&lt;/a&gt; is about.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-4333512594402774398?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/4333512594402774398/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=4333512594402774398' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/4333512594402774398'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/4333512594402774398'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications_30.html' title='OSGi Services for Dynamic Applications (II)'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JTmY6gtOOjs/R0YF0VyJapI/AAAAAAAAAEM/1atxcUEGHAA/s72-c/osgi_schematic2.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-9135347872925357020</id><published>2007-11-29T23:00:00.000-08:00</published><updated>2008-11-13T12:53:52.070-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='OSGi Java cardgame Eclipse Equinox'/><title type='text'>OSGi Services for Dynamic Applications (I)</title><content type='html'>&lt;span style="font-weight: bold;font-size:130%;" &gt;Or: use OSGi to write a dueling card game&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Surprise is one of the interesting things about &lt;span style="font-weight: bold;"&gt;dueling&lt;/span&gt; card games. You might know &lt;a href="http://www.wizards.com/magic/"&gt;Magic the Gathering&lt;/a&gt; or &lt;a href="http://www.yugioh-card.com/"&gt;Yi-Gi-Oh&lt;/a&gt;, which is quite popular with kids at the moment, many others exist too. They are interesting because there is a sheer endless number of cards so you never know what card your opponent will play. On the other hand, I never really liked the fact that whoever spends the most money on them always wins. I'd rather prefer a variant with a shared deck so that player's chances are even.&lt;br /&gt;&lt;br /&gt;I've wanted to write a little framework for a game like this for a while now and &lt;a href="http://www.osgi.org"&gt;OSGi&lt;/a&gt; is a great technology for doing this. Its bundle architecture brings some nice modularization and especially the OSGi Services Framework allows me to plug my services in and out at runtime without having to restart other bundles that make use of my services.&lt;br /&gt;In my case, I will use this to add a new sets of cards to my game without interrupting my ongoing games, the new cards will even appear straight away in any ongoing games. I will do this in one of the next postings. &lt;br /&gt;&lt;br /&gt;So the basics of my dueling game are similar to the games above and go like this. Each player starts with 250 points, whoever hits 0 first loses. Each player plays in turn:&lt;ol&gt;&lt;li&gt;Draw phase - generally just draw a card&lt;br /&gt;&lt;li&gt;Standby phase - here you can cast spells and a monster. Some spells are cast on your own area or attached to one of your monsters, other spells can affect the opponent.&lt;br /&gt;&lt;li&gt;Battle phase - now you can attack with your monsters that are on the table. The opponent can defend with any monsters that are on her side of the table. If the defence of the defender is higher than the attack of the attacker, the defender wins, and vice versa. If the defender does not have any monsters on the table, she has to take the damage, which will cause the attack amount to be deducted from her life points.&lt;br /&gt;&lt;li&gt;Recover phase - play spells and possibly a monster if you haven't already in this turn.&lt;/ol&gt;The very first battle phase is skipped (as it would be unfair to attack an opponent who didn't get to play any cards yet).&lt;br /&gt;&lt;br /&gt;Monsters and spells often have special abilities that could be tied to a particular phase. They could change the behaviour of other cards or generally disturb the order of the game. &lt;br /&gt;Another feature cards in these games generally have, is some stunning artwork which, unfortunately, I won't be able to provide.&lt;br /&gt;&lt;br /&gt;I have come up with a little architecture that looks like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0YFN1yJaoI/AAAAAAAAAEE/bb0L8fT4J_M/s1600-h/osgi_schematic2.png"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0YFN1yJaoI/AAAAAAAAAEE/bb0L8fT4J_M/s400/osgi_schematic2.png" alt="" id="BLOGGER_PHOTO_ID_5135798160396479106" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I'm using OSGi-style diagramming here. The yellow triangles are services, the red boxes are bundles, which provide some implementations of the services, the blue box is a component which itself lives inside a bundle.&lt;br /&gt;&lt;br /&gt;The Game Controller is a consumer of OSGi services. It consumes any number of Card services, it also consumes 2 UI services, one UI per player.&lt;br /&gt;So every card is a service of its own and every user interface is a separate service instance. As long as we adhere to the Card interface we can add any cards we can think of. Also using the service approach for the UI allows us to develop the UI separately from the game and gives us the possibility to implement any type of GUI, a simple one, a fancy one, an AI player (without a screen) or maybe even a remote UI without changing the actual game.&lt;br /&gt;&lt;br /&gt;OSGi doesn't use the static wiring for services that you can see in some other Service Oriented Architectures, in OSGi a service consumer specifies the Java interface that it is interested in, the OSGi runtime will then dynamically bind this to an available service that implements this interface. This is completely dynamic, and services could even come and go during the life time of the system. BTW I'm not going to go too much into the OSGi basics. Neil Bartlett's blog has some excellent &lt;a href="http://neilbartlett.name/blog/osgi-articles/"&gt;OSGi Basics tutorials&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Let's start by diving into the the core model of the game. (BTW I would really have loved to use EMF here again, but I wanted to keep this posting focused so I'm just using the EMF Class diagram editor for the picture, and am in this case handcoding the classes) - click on the image for a clearer picture:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0FyZlyJajI/AAAAAAAAADc/N8i8l7A_Xdg/s1600-h/Card.png"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/R0FyZlyJajI/AAAAAAAAADc/N8i8l7A_Xdg/s400/Card.png" alt="" id="BLOGGER_PHOTO_ID_5134510834143816242" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Entities in italics are interfaces, others are real classes. Let's look at the interfaces that define the various types of cards first. I put a few example cards in the middle. The top interface is called &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Action.java"&gt;Action&lt;/a&gt;, everything you can do and every card is an action. Cards generally implement both a subclass of Action as well as the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Card.java"&gt;Card&lt;/a&gt; interface. However there are certain actions that are not a card, such as the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/Duel/src/game/DrawCommand.java"&gt;DrawCommand&lt;/a&gt; and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/Duel/src/game/NextPhaseCommand.java"&gt;NextPhaseCommand&lt;/a&gt; actions.&lt;br /&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Command.java"&gt;Command&lt;/a&gt;s are things that can executed, usually in a particular &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Phase.java"&gt;Phase&lt;/a&gt; of the game. Example command cards are the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/SpellDeck/src/spelldeck/ThunderBoltCard.java"&gt;ThunderBoltCard&lt;/a&gt; spell which directly damages the opponent, or the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/SpellDeck/src/spelldeck/SkipBattlePhaseCard.java"&gt;SkipBattlePhaseCard &lt;/a&gt;which ends the current Battle phase.&lt;br /&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Fighter.java"&gt;Fighter&lt;/a&gt;s are things that can take part in a battle, generally monsters, but also the player himself. &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Modifier.java"&gt;Modifier&lt;/a&gt;s can change the attack and/or defense of a Fighter.&lt;br /&gt;&lt;br /&gt;Then there are the following classes and interfaces:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R084H1yJa_I/AAAAAAAAAG8/AFzD0B56HUA/s1600-h/Classes.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R084H1yJa_I/AAAAAAAAAG8/AFzD0B56HUA/s400/Classes.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5138387407200676850" /&gt;&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Game.java"&gt;Game&lt;/a&gt;: controls the game and provides information to the various entities about the game state. Provides accessors to change the game state too.&lt;/li&gt;&lt;li&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/Player.java"&gt;Player&lt;/a&gt;: data class that holds player information, such as the cards she holds in her hand, table and graveyard. Also holds a reference to the UI service that this player uses and the name of the player.&lt;/li&gt;&lt;li&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/src/game/model/UserInterface.java"&gt;UserInterface&lt;/a&gt;: this API defines the interaction with the UI. The purpose of the UI is simply to display the game state and get any input decisions (actions) from the player. The UI can also display a line of text to the user.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Since my dueling game revolves around these classes, I have put them in a separate OSGi bundle. Other bundles can participate in the game by importing these, without having to depend on any implementation bundles. This OSGi bundle is available in Subversion &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/"&gt;here&lt;/a&gt;. If you're using Eclipse, the easiest way to get it into your Eclipse IDE is with a Subversion client. I always use &lt;a href="http://subclipse.tigris.org/"&gt;subclipse&lt;/a&gt;, which is extremely easy to install; &lt;a href="http://subclipse.tigris.org/install.html"&gt;instructions here&lt;/a&gt;.&lt;br /&gt;Once you have subclipse, open the &lt;span style="font-weight: bold;"&gt;SVN Repository Exploring&lt;/span&gt; perspective and add the following SVN repository URL: &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/"&gt;http://coderthoughts.googlecode.com/svn/trunk/duel/&lt;/a&gt;. There's no need to log in, if you expand the root node, you will get the following repositories:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R03hMVyJa6I/AAAAAAAAAGU/FQRpdT-uAEc/s1600-h/svn_co.GIF"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R03hMVyJa6I/AAAAAAAAAGU/FQRpdT-uAEc/s400/svn_co.GIF" border="0" alt=""id="BLOGGER_PHOTO_ID_5138010352021760930" /&gt;&lt;/a&gt;&lt;br /&gt;Just check out the DuelInterfaces project and you're done. You will then have the OSGi bundle in your local Eclipse workspace, it will be the basis for the other pieces of the game that I'll be writing next.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0hOCVyJawI/AAAAAAAAAFE/7A5Gi-n2W_4/s1600-h/svn_co2.GIF"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/R0hOCVyJawI/AAAAAAAAAFE/7A5Gi-n2W_4/s400/svn_co2.GIF" alt="" id="BLOGGER_PHOTO_ID_5136441177130232578" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;For more details on the DuelInterfaces classes, read their &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/duel/DuelInterfaces/doc/index.html"&gt;Javadoc here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications_30.html"&gt;following few postings&lt;/a&gt; I will be implementing my dueling game using more OSGi bundles and services. I'll be using a mixture of OSGi Declarative Services (DS) and direct OSGi API calls for this. Besides DS and the plain API approach (which are both part of the OSGi spec) there exist a number of other technologies to work with OSGi services: &lt;a href="http://www.springframework.org/osgi"&gt;Spring-OSGi&lt;/a&gt;, &lt;a href="http://felix.apache.org/site/ipojo.html"&gt;iPojo&lt;/a&gt; and &lt;a href="http://wiki.ops4j.org/confluence/display/ops4j/Guice-OSGi"&gt;Guice-OSGi&lt;/a&gt; are some of them. The nice thing is that it doesn't matter what toolkit you use as the actual service registered with OSGi conforms to the OSGi Service Spec, so you can mix-n-match all these technologies if you like.&lt;br /&gt;&lt;br /&gt;Besides the game logic bundle, I will be writing a bundle with monster cards and another with spell cards and I'll also provide a basic UI implementation written using &lt;a href="http://www.eclipse.org/swt/"&gt;SWT&lt;/a&gt;, which looks like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/R0cE21yJatI/AAAAAAAAAEs/O86oDid8C3g/s1600-h/shot.GIF"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/R0cE21yJatI/AAAAAAAAAEs/O86oDid8C3g/s400/shot.GIF" alt="" id="BLOGGER_PHOTO_ID_5136079240236198610" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The good thing is, it allows me to play the game and I can write a nicer GUI later if I want, without changing the rest of the code. I might at some point even want to do a GUI that runs remotely, which allows me to use my OSGi-based application as a game server that can be used by people playing the game in various locations from a browser or something like that.&lt;br /&gt;&lt;br /&gt;As usual I'll be using the Eclipse IDE and Equinox OSGi implementation, but nothing in the code is tied to these so please use your IDE and OSGi implementation of choice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-9135347872925357020?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/9135347872925357020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=9135347872925357020' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/9135347872925357020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/9135347872925357020'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/11/osgi-services-for-dynamic-applications.html' title='OSGi Services for Dynamic Applications (I)'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_JTmY6gtOOjs/R0YFN1yJaoI/AAAAAAAAAEE/bb0L8fT4J_M/s72-c/osgi_schematic2.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-1314871023976856414</id><published>2007-10-20T15:53:00.000-07:00</published><updated>2008-11-13T12:53:52.235-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EMF Java text adventure'/><title type='text'>Superfast Model Driven Development with EMF (III)</title><content type='html'>&lt;font size="4"&gt;&lt;font style="font-weight: bold;"&gt;Load and Save&lt;/font&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;In the previous postings (&lt;a href="http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with.html"&gt;part I&lt;/a&gt; and &lt;a href="http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with_20.html"&gt;part II&lt;/a&gt;) I've created an EMF model to hold the definition of an adventure game. I've used the EMF-generated editors to create an adventure and wrote a little bit of code to run the game.&lt;br /&gt;&lt;br /&gt;In this posting I'm going to look at how we can use the EMF built-in serialization as a very easy way to load and save the game state. At the end I will also show how to run the program from the command line outside of Eclipse.&lt;br /&gt;&lt;br /&gt;To implement Load and Save, I'm adding two new actions to every location in the game (a player can load and save in every game location): &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/SaveAction.java"&gt;SaveAction &lt;/a&gt;and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/LoadAction.java"&gt;LoadAction&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The SaveAction creates a new SaveGame instance and stores the current location and the current inventory in it.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/RxqL_Cqdf1I/AAAAAAAAACo/U6HN4fPwuNo/s1600-h/model.JPG"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/RxqL_Cqdf1I/AAAAAAAAACo/U6HN4fPwuNo/s400/model.JPG" alt="" id="BLOGGER_PHOTO_ID_5123561441250344786" border="0"&gt;&lt;/a&gt;&lt;br /&gt;Looking at the model again, you can see that the SaveGame contains an Inventory object but only has a relation to the Location. This has an impact wrt to the content of the file that we're saving to. The contained items are actually inlined in the file where the referenced items are stored as hrefs.&lt;br /&gt;&lt;br /&gt;A sample &lt;font face="courier new"&gt;adventure.save &lt;/font&gt;file would look like this. The format of this XML file is called &lt;a href="http://www.omg.org/technology/documents/formal/xmi.htm"&gt;XMI&lt;/a&gt;, but as you can see, it reads as very typical XML. You can see that the inventory is actually contained in the file but the location is pointed to. When we save the file, it is in the same Resource Set as the &lt;font face="courier new"&gt;My.adventure &lt;/font&gt;resource, so that's how they know about each other. The &lt;font face="courier new"&gt;href &lt;/font&gt;URL also explains why we need an ID attribute. It is used in there. The ID attribute value cannot have a space in it so that's why it's generally handy to use a separate attribute for it.&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;"&gt;&amp;lt;adv:savegame version="2.0" xmi="http://www.omg.org/XMI" adv="http://adventure"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;inventory&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;items name="Spade" id="spade"/ &amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;/inventory&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;lt;location&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;href="file:/C:/EMF_Adventure/bin/My.adventure#desert"/&amp;gt;&lt;br /&gt;&amp;lt;/adv:savegame&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;The &lt;font face="courier new"&gt;adventure.save&lt;/font&gt; file is created using the following code:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;SaveGame sg = &lt;br /&gt;&amp;nbsp;&amp;nbsp;AdventureFactory.eINSTANCE.createSaveGame();&lt;br /&gt;sg.setLocation(Status.location);&lt;br /&gt;sg.setInventory(Status.inventory);&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;String fileName = System.getProperty("java.io.tmpdir") + &lt;br /&gt;&amp;nbsp;&amp;nbsp;"adventure.save";&lt;br /&gt;Resource resource = Status.resourceSet.createResource(&lt;br /&gt;&amp;nbsp;&amp;nbsp;URI.createFileURI(fileName));&lt;br /&gt;resource.getContents().add(sg);&lt;br /&gt;try {&lt;br /&gt;&amp;nbsp;&amp;nbsp;resource.save(Collections.emptyMap());      &lt;br /&gt;&amp;nbsp;&amp;nbsp;System.out.println("Game saved to " + fileName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;return true;&lt;br /&gt;} catch (IOException e) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;e.printStackTrace();&lt;br /&gt;} finally {&lt;br /&gt;&amp;nbsp;&amp;nbsp;Status.resourceSet.getResources().remove(resource);&lt;br /&gt;}&lt;/span&gt;&lt;ol&gt;&lt;li&gt;First we create and fill the SaveGame object.&lt;/li&gt;&lt;li&gt;Then we add a new Resource, stored in a file called adventure.save in the temp dir, to the ResourceSet used by the game.&lt;/li&gt;&lt;li&gt;Add the SaveGame object to the new Resource.&lt;/li&gt;&lt;li&gt;call resource.save()&lt;/li&gt;&lt;li&gt;Finally remove the new resource from the game's ResourceSet again as we don't want it to change after this.&lt;/li&gt;&lt;/ol&gt;And we're done.&lt;br /&gt;&lt;br /&gt;Loading data from an XMI file is very similar. The LoadAction does the following:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;" &gt;String fileName = System.getProperty("java.io.tmpdir") + &lt;br /&gt;&amp;nbsp;&amp;nbsp;"adventure.save";&lt;br /&gt;Resource resource = Status.resourceSet.getResource(&lt;br /&gt;&amp;nbsp;&amp;nbsp;URI.createFileURI(fileName), true);&lt;br /&gt;try {&lt;br /&gt;&amp;nbsp;&amp;nbsp;SaveGame sg = (SaveGame) EcoreUtil.getObjectByType(&lt;br /&gt;&amp;nbsp;&amp;nbsp;resource.getContents(), &lt;br /&gt;&amp;nbsp;&amp;nbsp;AdventurePackage.eINSTANCE.getSaveGame());&lt;br /&gt;&amp;nbsp;&amp;nbsp;if (sg != null) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status.location = sg.getLocation();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status.inventory = sg.getInventory();&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.out.println("Game loaded from " + fileName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return true;&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;} finally {&lt;br /&gt;&amp;nbsp;&amp;nbsp;Status.resourceSet.getResources().remove(resource);&lt;br /&gt;}&lt;/span&gt;&lt;ol&gt;&lt;li&gt;Create and add a resource that points to the adventure.save file. In this case we use ResourceSet.getResource() instead of ResourceSet.createResource().&lt;/li&gt;&lt;li&gt;Then we look up all of the contents of the file and find the SaveGame object in it. Since the adventure.save file really should only contain a single SaveGame object, resource.getContents().iterator().next() would also do the job.&lt;/li&gt;&lt;li&gt;Once we have our SaveGame object, simply change the current location of the game to be the one loaded and change the inventory to be the one from the savegame.&lt;/li&gt;&lt;li&gt;Finally, remove the adventure.save resource from the resourcset and were done.&lt;/li&gt;&lt;/ol&gt;Well, not quite, there is actually a little problem with the above code in that we should actually revisit all the locations of the adventure game and reset the items that belong there, taking into account the items that we've just loaded, but I'll leave that for another day :)&lt;br /&gt;&lt;br /&gt;Oh, and I'm adding the following 2 lines to &lt;font face="courier new"&gt;Status.initializeActions()&lt;/font&gt; so that my load and save actions are available from every location in the game:&lt;br /&gt;&lt;pre&gt;actions.add(new SaveAction());&lt;br /&gt;actions.add(new LoadAction());&lt;/pre&gt;&lt;br /&gt;&lt;font size="4"&gt;&lt;font style="font-weight: bold;"&gt;Running the program from the command line&lt;/font&gt;&lt;/font&gt;&lt;br /&gt;&lt;br /&gt;We're not writing an Eclipse application here, we're only using Eclipse and EMF as a tool to build our app. So we just want to run everything from the commandline. To do this, add to the following jars to your &lt;font face="courier new"&gt;CLASSPATH &lt;/font&gt;or copy them into your project. For EMF that comes with Eclipse 3.3.1 the needed libraries are the following, but the actual number could be slightly different if you are using a different version of Eclipse (see the &lt;a href="http://wiki.eclipse.org/index.php/EMF-FAQ"&gt;EMF FAQ&lt;/a&gt; for more info):&lt;br /&gt;&lt;ul style="font-family: courier new;font-size:80%;"&gt;&lt;li&gt;org.eclipse.emf.common_2.3.0.v200709252135.jar&lt;/li&gt;&lt;li&gt;org.eclipse.emf.ecore_2.3.1.v200709252135.jar&lt;/li&gt;&lt;li&gt;org.eclipse.emf.ecore.change_2.3.0.v200709252135.jar&lt;/li&gt;&lt;li&gt;org.eclipse.emf.ecore.xmi_2.3.1.v200709252135.jar&lt;/li&gt;&lt;/ul&gt;Now I can run my adventure game from the command line with:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;pre&gt;&lt;br /&gt;set CLASSPATH=.;&lt;br /&gt;&amp;nbsp;&amp;nbsp;org.eclipse.emf.common_2.3.0.v200709252135.jar;&lt;br /&gt;&amp;nbsp;&amp;nbsp;org.eclipse.emf.ecore_2.3.1.v200709252135.jar;&lt;br /&gt;&amp;nbsp;&amp;nbsp;org.eclipse.emf.ecore.change_2.3.0.v200709252135.jar;&lt;br /&gt;&amp;nbsp;&amp;nbsp;org.eclipse.emf.ecore.xmi_2.3.1.v200709252135.jar&lt;br /&gt;java game.Main&lt;/pre&gt;&lt;/span&gt;&lt;br /&gt;&lt;font size="2"&gt;&lt;font style="color: rgb(153, 51, 0); font-weight: bold;" face="courier new"&gt;Welcome to Adventure Island. This is the start of the adventure game. You are at&lt;/font&gt; &lt;font style="color: rgb(153, 51, 0); font-weight: bold;" face="courier new"&gt;the house of a Viking.&lt;/font&gt;&lt;br/&gt;&lt;font style="color: rgb(153, 51, 0); font-weight: bold;" face="courier new"&gt;Hints: (q)uit, (i)nventory, (w)est, (e)ast, save, load, you see a Spade&lt;/font&gt;&lt;br/&gt;&lt;font style="color: rgb(153, 51, 0); font-weight: bold;" face="courier new"&gt;Please enter command:&lt;/font&gt; &lt;/font&gt;&lt;br /&gt;All the code above is available in the Subversions repository at &lt;span style="font-size:70%;"&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure"&gt;http://coderthoughts.googlecode.com/svn/trunk/emf_adventure&lt;/a&gt;&lt;/span&gt; for more info on how to check it out &lt;a href="http://code.google.com/p/coderthoughts/source"&gt;look here&lt;/a&gt;. Want all the projects including the generated code, get it here: &lt;span style="font-size:70%;"&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure_full/"&gt;http://coderthoughts.googlecode.com/svn/trunk/emf_adventure_full&lt;/a&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-1314871023976856414?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/1314871023976856414/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=1314871023976856414' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1314871023976856414'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1314871023976856414'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with_2828.html' title='Superfast Model Driven Development with EMF (III)'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_JTmY6gtOOjs/RxqL_Cqdf1I/AAAAAAAAACo/U6HN4fPwuNo/s72-c/model.JPG' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-1724904393004498118</id><published>2007-10-20T13:36:00.000-07:00</published><updated>2008-11-13T12:53:52.758-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EMF Java text adventure'/><title type='text'>Superfast Model Driven Development with EMF (II)</title><content type='html'>&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Creating the actual adventure game&lt;/span&gt; &lt;/span&gt;&lt;br /&gt;In the &lt;a href="http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with.html"&gt;previous posting&lt;/a&gt; I used EMF to create a model and the generated editor to create a file (&lt;span style="font-family:courier new;"&gt;My.adventure&lt;/span&gt;) containing the definition of my text adventure game. In this posting I'm going to create the actual game.&lt;br /&gt;&lt;br /&gt;Copy and paste the &lt;span style="font-family:courier new;"&gt;My.adventure&lt;/span&gt; file created in the previous posting into the src directory of the EMF_Adventure project. Also create a &lt;span style="font-family:courier new;"&gt;game&lt;/span&gt; package where we'll do our development. One of the reasons to do the actual development in a different place or different package than the code generated by EMF is that this way, if you make a change to your EMF code you can simply delete all the generated code and regenerate it, without having to watch out for your own files. So the EMF-generated code is in the &lt;span style="font-family:courier new;"&gt;adventure&lt;/span&gt; package and our code is in the &lt;span style="font-family:courier new;"&gt;game&lt;/span&gt; package.&lt;br /&gt;&lt;br /&gt;The little text adventure that we'll be writing allows you to move from one location to another. It allows you to pick up items you may find or perform actions in order to find hidden items. Certain items can only be obtained once you have other items first. For example, in the sample adventure game definition (which is based on a game I wrote with my kids, get it &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/My.adventure"&gt;here&lt;/a&gt;) you need a spade in order to dig up the treasure.&lt;br /&gt;&lt;br /&gt;To give you an idea, here's the output from a sample running of it:&lt;br /&gt;&lt;span style="color: rgb(153, 51, 0); font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;Welcome to Adventure Island. This is the start of the adventure game. You are at the house of a Viking.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Hints: (q)uit, (i)nventory, (w)est, (e)ast, save, load, you see a Spade&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Please enter command:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&gt; &lt;span style="color: rgb(51, 204, 0);"&gt;pick up spade&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;You pick up Spade...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Viking's house.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Hints: (q)uit, (i)nventory, (w)est, (e)ast, save, load&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Please enter command:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&gt; &lt;span style="color: rgb(51, 204, 0);"&gt;w&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;You're going west&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;You are at the Viking's boat. Will you help the Viking with his fishing?&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Hints: (q)uit, (i)nventory, (e)ast, save, load&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Please enter command:&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;etc...&lt;br /&gt;&lt;br /&gt;I know, it's text only, which is slightly boring, but the point of this posting is about EMF. We've added images to the locations and that's actually quite easy to do, but I'll leave it up to yourselves.&lt;br /&gt;&lt;br /&gt;The first class we'll make is the &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/Main.java"&gt;Main&lt;/a&gt; class which kicks off the game and holds the game loop:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;"&gt;package game;&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;import java.io.BufferedReader;&lt;br /&gt;import java.io.InputStreamReader;&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;public class Main {&lt;br /&gt;&amp;nbsp;&amp;nbsp;public static void main(String[] args) throws Exception {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status.intializeGame();&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for (;;) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status.describeLocation();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;String command = new BufferedReader(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new InputStreamReader(System.in)).readLine();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Status.executeCommand(command);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;The &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/Status.java"&gt;Status &lt;/a&gt;class holds the game state and controls the state transitions. The first thing it does it read the EMF model instance we've created that defines the game:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;"&gt;public class Status {&lt;br /&gt;&amp;nbsp;&amp;nbsp;static Adventure adventure;&lt;br /&gt;&amp;nbsp;&amp;nbsp;static Location location;&lt;br /&gt;&amp;nbsp;&amp;nbsp;static Inventory inventory =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AdventureFactory.eINSTANCE.createInventory();&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;static Map&amp;lt;Location, List&amp;lt;Action&amp;gt;&amp;gt; actionMap = &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new HashMap&amp;lt;Location, List&amp;lt;Action&amp;gt;&amp;gt;();&lt;br /&gt;&amp;nbsp;&amp;nbsp;static ResourceSet resourceSet = new ResourceSetImpl();&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;public static void intializeGame() throws IOException {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resourceSet.getResourceFactoryRegistry().&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getExtensionToFactoryMap().&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;put(Resource.Factory.Registry.DEFAULT_EXTENSION,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new XMIResourceFactoryImpl());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resourceSet.getPackageRegistry().&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;put(AdventurePackage.eNS_URI,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AdventurePackage.eINSTANCE);&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;URI gameDefURI = URI.createURI(Status.class.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;getResource("/My.adventure").toString());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Resource resource = resourceSet.getResource(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gameDefURI, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;adventure = (Adventure) EcoreUtil.getObjectByType(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resource.getContents(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AdventurePackage.eINSTANCE.getAdventure());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;location = adventure.getStartLocation();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;initializeActions();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In EMF objects are persisted in a resource, which could take many forms. I am using the XMIResource which basically is an XML file in the &lt;a href="http://www.omg.org/technology/documents/formal/xmi.htm"&gt;XMI&lt;/a&gt; format. Not all the objects in an EMF model have to be in the same file, objects could be spread over multiple files. To associate these files with each other EMF puts them in a ResourceSet.&lt;br /&gt;&lt;br /&gt;The first two lines of &lt;span style="font-family:courier new;"&gt;initializeGame() &lt;/span&gt;set up the ResourceSet to use XMI resources and also informs the EMF package registry about our Adventure model.&lt;br /&gt;Another thing you might notice in the class is the creation of the inventory object. Instead of going &lt;span style="font-family:courier new;"&gt;inventory = new InventoryImpl()&lt;/span&gt; (which would actually work) EMF prefers you to use the Factory pattern. So we create our inventory object by calling &lt;span style="font-family:courier new;"&gt;createInventory() &lt;/span&gt;on the factory instead.&lt;br /&gt;Next, I'm loading the game definition from the &lt;span style="font-family:courier new;"&gt;My.adventure &lt;/span&gt;file. EMF wants to have a URI to the resource (rather than a simple file path).&lt;br /&gt;After this I use the &lt;span style="font-family:courier new;"&gt;EcoreUtil.getObjectByType()&lt;/span&gt; to find my single Adventure object in the Resource that has loaded the file. There are other ways to get to the objects in a resource, the simplest way is just calling &lt;span style="font-family:courier new;"&gt;resource.getContents() &lt;/span&gt;which returns a list of the objects in the resource. The &lt;span style="font-family:courier new;"&gt;AdventurePackage.eINSTANCE.getAdventure()&lt;/span&gt; returns an &lt;span style="font-family:courier new;"&gt;EClass &lt;/span&gt;object that represents the Adventure EMF entity. EMF has its own metamodel which is richer than the Java reflection metamodel. It basically allows you to find out all the things that you have specified in your model, over and above what can be reflected in the Java signatures. But in a way &lt;span style="font-family:courier new;"&gt;AdventurePackage.eINSTANCE.getAdventure()&lt;/span&gt; in EMF-speak is similar to what would be &lt;span style="font-family:courier new;"&gt;Adventure.class&lt;/span&gt; in Java-speak.&lt;br /&gt;&lt;br /&gt;We now finally have our adventure object and life becomes much easier. At this point can pretty much use the EMF model instance by utilizing Pojo bean-conventions. So we set our initial location to be the location that was marked as the start location in our Adventure definition file.&lt;br /&gt;Last but not least we'll initialize the &lt;span style="font-family:courier new;"&gt;actionMap&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;"&gt;actionMap &lt;/span&gt;is a map from a location to a list of Actions that are available on that location. These actions can be added statically (e.g. the 'quit' action is available everywhere) or dynamically, such as actions that relate to routes you can take from a particular place or actions associated with items. All actions in the game implement the &lt;span style="font-family:courier new;"&gt;game.Action&lt;/span&gt; interface:&lt;br /&gt;&lt;span style="font-family: courier new;font-weight: bold;font-size:85%;"&gt;public interface Action {&lt;br /&gt;&amp;nbsp;&amp;nbsp;/** A hint to the user how to execute this action, or &lt;br /&gt;&amp;nbsp;&amp;nbsp;* null if there is no direct hint.&lt;br /&gt;&amp;nbsp;&amp;nbsp;* @return A hint such as "(q)uit" or "You see a Spade"&lt;br /&gt;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;String hint();&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;/** Called on the action to see does it want to execute &lt;br /&gt;&amp;nbsp;&amp;nbsp;* the command. The command is passed in as an array of &lt;br /&gt;&amp;nbsp;&amp;nbsp;* strings. If the action does not want to execute the&lt;br /&gt;&amp;nbsp;&amp;nbsp;* command it can just return false.&lt;br /&gt;&amp;nbsp;&amp;nbsp;* @param commands The commands in an Array e.g. &lt;br /&gt;&amp;nbsp;&amp;nbsp;*        ["pick", "up", "spade"]&lt;br /&gt;&amp;nbsp;&amp;nbsp;* @return true if the action was performed, &lt;br /&gt;&amp;nbsp;&amp;nbsp;*        false otherwise.&lt;br /&gt;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;boolean execute(String[] commands);&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;/** The minimum number of words needed for this action &lt;br /&gt;&amp;nbsp;&amp;nbsp;* to qualify. So if your action is triggered by "pick &lt;br /&gt;&amp;nbsp;&amp;nbsp;* up spade" it would return 3. The action will not be &lt;br /&gt;&amp;nbsp;&amp;nbsp;* visited if the number of commands is less.&lt;br /&gt;&amp;nbsp;&amp;nbsp;* @return The minimum number for words required to &lt;br /&gt;&amp;nbsp;&amp;nbsp;*         visit this action&lt;br /&gt;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;int minLength();&lt;br /&gt;&amp;nbsp;&lt;br/&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;/** Whether or not to remove the action after &lt;br /&gt;&amp;nbsp;&amp;nbsp;* performing it. An item action would typically be &lt;br /&gt;&amp;nbsp;&amp;nbsp;* removed after you've picked it up (you can't pick &lt;br /&gt;&amp;nbsp;&amp;nbsp;* it up twice) but other actions normally remain.&lt;br /&gt;&amp;nbsp;&amp;nbsp;* @return Whether or not to remove the action after &lt;br /&gt;&amp;nbsp;&amp;nbsp;*         successful execution.&lt;br /&gt;&amp;nbsp;&amp;nbsp;*/&lt;br /&gt;&amp;nbsp;&amp;nbsp;boolean removeAfter();&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Status.initializeActions() &lt;/span&gt;initializes the map of all the locations in the game with the actions that are available there.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Status.describeLocation() &lt;/span&gt;simply prints out the description of the current location and also prints out the hints for all the commands available here. &lt;span style="font-family:courier new;"&gt;Status.executeCommand()&lt;/span&gt; parses the string typed in by the user and then visits the actions registered with the current location to see whether any one action wants to execute the command. See &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/Status.java"&gt;here&lt;/a&gt; for the rest of the Status class, apart from the &lt;span style="font-family:courier new;"&gt;initializeGame() &lt;/span&gt;it's fairly simple Java. There are also a number of actions provided, such as &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/InventoryAction.java"&gt;InventoryAction&lt;/a&gt;, &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/ItemAction.java"&gt;ItemAction&lt;/a&gt;, &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/QuitAction.java"&gt;QuitAction &lt;/a&gt;and &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/game/RouteAction.java"&gt;RouteAction &lt;/a&gt;which all take care of their particular things.&lt;br /&gt;&lt;br /&gt;Finally, because we're using the XMI functionality to load the model, we need to make sure to specify this dependency in the bundle that holds our code. EMF generates code as OSGi bundles/eclipse plugins which use the &lt;span style="font-family:courier new;"&gt;META-INF/MANIFEST.MF&lt;/span&gt; file to describe their dependencies, for those who want to run this as a simple Java app we'll see later in this posting how to get these jar files on the classpath. For now just open the &lt;span style="font-family:courier new;"&gt;MANIFEST.MF&lt;/span&gt; file and in the Dependencies tab add 'org.eclipse.emf.ecore.xmi' to the required plugins.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/Rxpo9Sqdf0I/AAAAAAAAACg/I70x5vOEioY/s1600-h/plugin.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/Rxpo9Sqdf0I/AAAAAAAAACg/I70x5vOEioY/s400/plugin.JPG" alt="" id="BLOGGER_PHOTO_ID_5123522928278601538" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Now we can actually run our game. To do this open the &lt;span style="font-family:courier new;"&gt;Main&lt;/span&gt; class, right-click in the editor and select 'Run As -&gt; Java Application'. The game will start:&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(153, 51, 0); font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family:courier new;"&gt;Welcome to Adventure Island. This is the start of the adventure game. You are at the house of a Viking.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Hints: (q)uit, (i)nventory, (w)est, (e)ast, you see a Spade&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;Please enter command:&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;All the code above is available in the Subversions repository at &lt;span style="font-size:80%;"&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure"&gt;http://coderthoughts.googlecode.com/svn/trunk/emf_adventure&lt;/a&gt;&lt;/span&gt; for more info on how to check it out &lt;a href="http://code.google.com/p/coderthoughts/source"&gt;look here&lt;/a&gt;. Want all the projects including the generated code, get it here: &lt;span style="font-size:80%;"&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure_full/"&gt;http://coderthoughts.googlecode.com/svn/trunk/emf_adventure_full&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with_2828.html"&gt;next posting&lt;/a&gt; I'll use EMF to provide savegame/loadgame functionality and run the game as a plain Java App that uses EMF outside of Eclipse.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-1724904393004498118?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/1724904393004498118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=1724904393004498118' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1724904393004498118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1724904393004498118'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with_20.html' title='Superfast Model Driven Development with EMF (II)'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_JTmY6gtOOjs/Rxpo9Sqdf0I/AAAAAAAAACg/I70x5vOEioY/s72-c/plugin.JPG' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-7520012447887836002</id><published>2007-10-18T23:59:00.000-07:00</published><updated>2008-11-13T12:53:54.176-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='EMF Java text adventure'/><title type='text'>Superfast Model Driven Development with EMF (I)</title><content type='html'>&lt;span style="font-weight: bold;font-size:130%;" &gt;Or: write a text adventure game in no time&lt;/span&gt;&lt;span style="font-size:180%;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;One of the technologies that I use quite a lot is EMF. It has helped me increase my development speed enormously, and I use it pretty much every time I need a little data model.&lt;br /&gt;&lt;br /&gt;What is lesser known is that you don't actually need to be writing Eclipse plugins to take advantage of EMF. You can use EMF in any old Java program. Another thing Eclipse gives you is tooling around EMF and tooling for your own model, which makes it super handy.&lt;br /&gt;&lt;br /&gt;So with the Eclipse tooling you get a nice UML editor on your EMF class model. Eclipse generates all the EMF runtime code for you, and it also generates a model instance editor, so basically you get a data entry tool for your model as well, which can be quite useful.&lt;br /&gt;In addition to the modeling stuff, the EMF runtime also gives you a loading and saving of your model (to an XML file format called &lt;a href="http://www.omg.org/technology/documents/formal/xmi.htm"&gt;XMI&lt;/a&gt;). All of this stuff is available for free as open source.&lt;br /&gt;&lt;br /&gt;So I've used it to write a little text adventure game framework (add your own graphics later ;) You can define the actual text adventure in the EMF model. EMF is also used to provide the load and save game functionality.&lt;br /&gt;&lt;br /&gt;So I started off with &lt;a href="http://www.eclipse.org/downloads/"&gt;Eclipse 3.3.1&lt;/a&gt; with EMF and GMF installed (see here how that's done). We need GMF to do the graphical UML editing. I've posted a little &lt;a href="http://coderthoughts.blogspot.com/2007/10/how-to-get-emf-and-gmf-based-uml.html"&gt;how-to&lt;/a&gt; on this, in case you need help.&lt;br /&gt;&lt;br /&gt;In this posting I'm creating the model and will generate a data entry editor for my model. The next posting will focus on the actual game and how you run it as a simple Java app.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;1. Create the model&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Create a project called &lt;span style="font-family:courier new;"&gt;EMF_Adventure.model&lt;/span&gt; and add an Ecore Model file to it (in Eclipse go to File | New | Ecore Model) and call it &lt;span style="font-family:courier new;"&gt;Adventure.ecore&lt;/span&gt;.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxJtOiqdfjI/AAAAAAAAAAU/cn980tN-Lr8/s1600-h/filenew.JPG"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxJtOiqdfjI/AAAAAAAAAAU/cn980tN-Lr8/s400/filenew.JPG" alt="" id="BLOGGER_PHOTO_ID_5121275822864170546" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Then right-click on the &lt;span style="font-family:courier new;"&gt;Adventure.ecore &lt;/span&gt;file in the tree, and select 'Initialize ecore_diagram diagram file' from the popup menu. Double-click the&lt;span style="font-family:courier new;"&gt; Adventure.ecore_diagram &lt;/span&gt;that has just been generated and you're in the UML editor (click on the image for a clearer picture):&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/RxJuYCqdflI/AAAAAAAAAAk/gXUZIuBs6xE/s1600-h/emptyumleditor.JPG"&gt;&lt;img style="cursor: pointer; width: 416px; height: 322px;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/RxJuYCqdflI/AAAAAAAAAAk/gXUZIuBs6xE/s400/emptyumleditor.JPG" alt="" id="BLOGGER_PHOTO_ID_5121277085584555602" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;So now we actually create our model. My little model for the adventure game looks like this:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfNgSqdfqI/AAAAAAAAABQ/ryRTow0s3eQ/s1600-h/model.JPG"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfNgSqdfqI/AAAAAAAAABQ/ryRTow0s3eQ/s400/model.JPG" alt="" id="BLOGGER_PHOTO_ID_5122789055806668450" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;My model contains the definition of the structure of an Adventure.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The &lt;span style="font-weight: bold;"&gt;Adventure&lt;/span&gt; entity contains a number of Locations (containment is visualized using the diamond UML notation). One of these locations is to be the start location of the game.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Locations&lt;/span&gt; in turn have routes to other locations, they also possibly contain items. Every location has a name and a description and an ID. &lt;/li&gt;&lt;li&gt;A &lt;span style="font-weight: bold;"&gt;Route &lt;/span&gt;object describes how to get from one location to another.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Items &lt;/span&gt;are things you can find on a location. Certain items are visible, you just see them and can pick them up. Other items are a hidden, for instance, if you want to find the treasure you will need to dig, otherwise you don't see it. In this case there might be a precondition on another item that you need to have in your inventory to perform this action. In order to dig you may need a spade.&lt;/li&gt;&lt;/ul&gt;Besides the adventure definition, the model also contains a representation of the &lt;span style="font-weight: bold;"&gt;Inventory&lt;/span&gt; which contains all the items you have picked up during the game. EMF is quite nice in that it make sure the model is consistent. This means that contained objects can only have one container. So by adding an Item to your Inventory EMF will automatically remove it from the Location.&lt;br /&gt;&lt;br /&gt;Finally the definition contains an entity used for saving and loading games. The &lt;span style="font-weight: bold;"&gt;SaveGame&lt;/span&gt; entity has a reference to the location where you saved the game and also holds the inventory that you had when you saved.&lt;br /&gt;&lt;br /&gt;Another way to look at the same model is by opening the &lt;span style="font-family:courier new;"&gt;Adventure.ecore&lt;/span&gt; file, which gives you a tree-type view on the model.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_JTmY6gtOOjs/RxplDCqdfyI/AAAAAAAAACQ/FtEZGCCBJsY/s1600-h/model_ecore-2.JPG"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_JTmY6gtOOjs/RxplDCqdfyI/AAAAAAAAACQ/FtEZGCCBJsY/s400/model_ecore-2.JPG" alt="" id="BLOGGER_PHOTO_ID_5123518629016338210" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;You can either edit the model using the diagram editor or using the ecore editor. They generally keep each other in sync.&lt;br /&gt;&lt;br /&gt;Find the &lt;span style="font-family:courier new;"&gt;Adventure.ecore&lt;/span&gt; file that I created &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure.model/Adventure.ecore"&gt;here&lt;/a&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure.model/model/Adventure.ecore"&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;2. Generate a data entry tool for your model&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;The first thing you will need to do in order to use your EMF model is to generate code from it.&lt;br /&gt;&lt;br /&gt;To generate your code you need to create a &lt;span style="font-family:courier new;"&gt;.genmodel&lt;/span&gt; file. Do this by going File -&gt; New -&gt; Other and then select 'Eclipse Modeling Framework | EMF Model'.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/RxfQeCqdfsI/AAAAAAAAABg/_k094iCrHWc/s1600-h/new_genmodel.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/RxfQeCqdfsI/AAAAAAAAABg/_k094iCrHWc/s400/new_genmodel.JPG" alt="" id="BLOGGER_PHOTO_ID_5122792315686846146" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Just call it &lt;span style="font-family:courier new;"&gt;Adventure.genmodel&lt;/span&gt;, select 'Ecore model' as the Model Importer on the next page and then pick your &lt;span style="font-family:courier new;"&gt;Adventure.ecore&lt;/span&gt; file from your workspace. Then finish the wizard.&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-family:courier new;"&gt;Adventure.genmodel&lt;/span&gt; file contains a whole lot of settings that influence how the EMF code generation is done. I normally change at least one setting. By default EMF generates code in the project that holds the .genmodel file. I always prefer to have the generated code in another project, so I typically change the following property:&lt;br /&gt;Model Directory: change from &lt;span style="font-family:courier new;"&gt;/EMF_Adventure.model/src&lt;/span&gt; to &lt;span style="font-family:courier new;"&gt;/EMF_Adventure/src&lt;/span&gt;&lt;br /&gt;Then right-click on the top-level Adventure node in the &lt;span style="font-family:courier new;"&gt;Aventure.genmodel&lt;/span&gt; file and select 'Generate All':&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfQeSqdftI/AAAAAAAAABo/PwtkS0AHKSI/s1600-h/genmodel_things.JPG"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfQeSqdftI/AAAAAAAAABo/PwtkS0AHKSI/s400/genmodel_things.JPG" alt="" id="BLOGGER_PHOTO_ID_5122792319981813458" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The model generation gives us a couple of options:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Generate Model Code - this is the code that we will be using at runtime. Its an implementation of the model that we defined.&lt;/li&gt;&lt;li&gt;Generate Editor Code - this generates an Eclipse editor to fill your model with data. It can be really handy to populate your model. The Edit Code that is also generated is used internally by the editor.&lt;/li&gt;&lt;li&gt;Generate Test Code - this will create a plugin that contains test templates and some sample code that utilizes your model.&lt;/li&gt;&lt;/ul&gt;After running the generation you will get 4 new projects in your workspace that all compile without errors:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_JTmY6gtOOjs/RxfQeiqdfuI/AAAAAAAAABw/K58RFLlTbKc/s1600-h/generated_model.JPG"&gt;&lt;img style="cursor: pointer;" src="http://1.bp.blogspot.com/_JTmY6gtOOjs/RxfQeiqdfuI/AAAAAAAAABw/K58RFLlTbKc/s400/generated_model.JPG" alt="" id="BLOGGER_PHOTO_ID_5122792324276780770" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;In this screenshot (again click on it for a high-res version) you can see some of the generated code. Currently open is the &lt;span style="font-weight: bold;"&gt;Item &lt;/span&gt;class which contains getters and setters for all the properties we defined in the model. Both interface and implementation are generated for the model, our implementation is generated in the &lt;span style="font-family:courier new;"&gt;adventure.impl &lt;/span&gt;package.&lt;br /&gt;&lt;br /&gt;Now we can run it to create our adventure content! Just right-click any of the newly generated projects and select 'Run As -&gt; Eclipse Application'.&lt;br /&gt;This starts up a separate Eclipse Runtime that contains your newly generated editor. Create a new project in it and add a new adventure model file by going 'File -&gt; New -&gt; Example EMF Creation Wizards -&gt; Adventure Model':&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_JTmY6gtOOjs/RxplDSqdfzI/AAAAAAAAACY/zQ0nUP5lNBs/s1600-h/new_adventure_model-2.JPG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_JTmY6gtOOjs/RxplDSqdfzI/AAAAAAAAACY/zQ0nUP5lNBs/s400/new_adventure_model-2.JPG" alt="" id="BLOGGER_PHOTO_ID_5123518633311305522" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Call it &lt;span style="font-family:courier new;"&gt;My.adventure &lt;/span&gt;and select 'Adventure' as the Model Object in the last wizard page.&lt;br /&gt;&lt;br /&gt;Once you have your file, you can double-click it and voilà there is your editor where you can define your adventure. It will allow you to create all your locations, items and routes. It also has a properties view where various properties are edited. And this all without writing a single line of code!&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxplCiqdfxI/AAAAAAAAACI/1yGQmpIfPqA/s1600-h/editor-2.JPG"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxplCiqdfxI/AAAAAAAAACI/1yGQmpIfPqA/s400/editor-2.JPG" alt="" id="BLOGGER_PHOTO_ID_5123518620426403602" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;The actual file edited is an XML file. I created one based on a little adventure I made with my kids a little while ago, you can find it &lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/My.adventure"&gt;here&lt;/a&gt;&lt;a href="http://coderthoughts.googlecode.com/svn/trunk/emf_adventure/EMF_Adventure/src/Adventure.adventure_model"&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with_20.html"&gt;next post&lt;/a&gt;, we'll create the adventure runtime.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-7520012447887836002?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/7520012447887836002/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=7520012447887836002' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/7520012447887836002'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/7520012447887836002'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/10/superfast-model-driven-development-with.html' title='Superfast Model Driven Development with EMF (I)'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JTmY6gtOOjs/RxJtOiqdfjI/AAAAAAAAAAU/cn980tN-Lr8/s72-c/filenew.JPG' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-1201819112154873641</id><published>2007-10-18T13:28:00.000-07:00</published><updated>2008-11-13T12:53:54.580-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eclipse EMF'/><title type='text'>How to get the EMF UML editor bits into Eclipse</title><content type='html'>This is not really a blog entry per se, its more a little how-to on getting your tools set up for the next blog entry.&lt;br /&gt;&lt;br /&gt;All of the tools required here are open source.&lt;br /&gt;&lt;br /&gt;Well, first you have to get Eclipse: I've downloaded the 'classic' distribution of Eclipse 3.3.1 from &lt;a href="http://www.eclipse.org/"&gt;www.eclipse.org&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Then use the Eclipse update mechanism to get the EMF bits.&lt;br /&gt;&lt;br /&gt;Start Eclipse and select the menu 'Help -&gt; Software Updates -&gt; Find and Install' (click on the image for a better picture)&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfDJSqdfnI/AAAAAAAAAA4/S6LqIoAMuEY/s1600-h/fandi.JPG"&gt;&lt;img style="cursor: pointer; width: 478px; height: 358px;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfDJSqdfnI/AAAAAAAAAA4/S6LqIoAMuEY/s400/fandi.JPG" alt="" id="BLOGGER_PHOTO_ID_5122777665553399410" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Select 'Search for new features to install', then select the 'Europa Discovery Site':&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfDJSqdfpI/AAAAAAAAABI/rSSIS8rD_dI/s1600-h/updatesites.JPG"&gt;&lt;img style="cursor: pointer; width: 373px; height: 415px;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfDJSqdfpI/AAAAAAAAABI/rSSIS8rD_dI/s400/updatesites.JPG" alt="" id="BLOGGER_PHOTO_ID_5122777665553399442" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;In the following wizard page (called 'Search Results') select 'Graphical Modeling Framework' in the 'Models and Model Development' section. Then hit the 'Select Required' button.&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfDJSqdfoI/AAAAAAAAABA/3hY3veB0QDk/s1600-h/selectGMF.JPG"&gt;&lt;img style="cursor: pointer; width: 407px; height: 413px;" src="http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfDJSqdfoI/AAAAAAAAABA/3hY3veB0QDk/s400/selectGMF.JPG" alt="" id="BLOGGER_PHOTO_ID_5122777665553399426" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I'm selecting GMF because that thechnology provides the UML-style class diagram  editor for EMF. Since GMF depends on EMF, you will get all the needed EMF bits too by hitting the 'Select Required' button.&lt;br /&gt;&lt;br /&gt;Hit next 'Next' a couple of times and then hit Finish.&lt;br /&gt;&lt;br /&gt;Eclipse will now download and install EMF.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-1201819112154873641?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/1201819112154873641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=1201819112154873641' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1201819112154873641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/1201819112154873641'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/10/how-to-get-emf-and-gmf-based-uml.html' title='How to get the EMF UML editor bits into Eclipse'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_JTmY6gtOOjs/RxfDJSqdfnI/AAAAAAAAAA4/S6LqIoAMuEY/s72-c/fandi.JPG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9210566578097047576.post-8727272717170796821</id><published>2007-10-04T14:15:00.000-07:00</published><updated>2007-10-17T00:34:46.002-07:00</updated><title type='text'>First posting</title><content type='html'>Le Code pour le Code - or: Just for the fun of it :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9210566578097047576-8727272717170796821?l=coderthoughts.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://coderthoughts.blogspot.com/feeds/8727272717170796821/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9210566578097047576&amp;postID=8727272717170796821' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/8727272717170796821'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9210566578097047576/posts/default/8727272717170796821'/><link rel='alternate' type='text/html' href='http://coderthoughts.blogspot.com/2007/10/first-posting.html' title='First posting'/><author><name>davidb</name><uri>http://www.blogger.com/profile/13786738766478890804</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='30' height='32' src='http://4.bp.blogspot.com/_JTmY6gtOOjs/SnlGCzltCrI/AAAAAAAAAPk/H7gKC1mzfFY/S220/David2_SML.JPG'/></author><thr:total>0</thr:total></entry></feed>
