Friday, February 24, 2012

Building project Penrose (or OpenJDK 8)

As OpenJDK Project Penrose was approved last week, I started looking at getting set up to actually be able to contribute to OpenJDK and Penrose.
As I had never built OpenJDK before I started looking for the build page. While OpenJDK build page says that there is no OpenJDK 8 build readme yet, the link behind the 'coming soon' text actually contains some useful information but it relates to old versions of Linux and I want to get this working on Fedora 16 :)

Currently the Penrose codebase is still the same as the Jigsaw code base, so the steps below also apply to building Jigsaw, and they also work for plain OpenJDK 8.
At some point there will be changes in the Penrose codebase, but I don't expect that they will change the build process much, if at all.

While Julien Ponge nicely describes how to get this working on Ubuntu, the steps are a bit different on Fedora 16, so I'm documenting here how to get started from a fresh new Fedora 16 machine.

Get a few dependencies first:
# set up gcc and the static version of libstdc++
sudo yum install gcc gcc-c++ libstdc++-static

# install build deps on alsa, freetype and cups
sudo yum install alsa-lib-devel freetype-devel cups-devel

# install some X11 headers
sudo yum libXt-devel libXext-devel libXrender-devel libXtst-devel

# get mercurial
sudo yum install mercurial

# install Java 7 and ant
sudo yum install java-1.7.0-openjdk-devel ant

Now clone the whole forest of Mercurial trees (you can also use fclone with the Forest extension instead, which will save you from doing calling the bash script manually):
$ hg clone http://hg.openjdk.java.net/penrose/jigsaw pj
$ cd pj
$ bash get_source.sh

Set the following environment variables:
export LANG=C
export ALT_BOOTDIR=/usr/lib/jvm/java-1.7.0-openjdk
export ALLOW_DOWNLOADS=true

At this point 'make sanity', which does a rough check on the setup, should succeed:
$ make sanity
...
Sanity check passed.

Ok, we're ready to build:
$ make
... go for a coffee, walk the dog, book a holiday
... after quite a while you'll see that it's finished
-- Build times ----------
Target all_product_build
Start 2012-02-24 08:15:23
End   2012-02-24 08:52:47
00:00:58 corba
00:17:31 hotspot
00:00:19 jaxp
00:00:23 jaxws
00:17:44 jdk
00:00:29 langtools
00:37:24 TOTAL
-------------------------

There you go, you built Penrose and the result is a JDK 8 installation based on the Penrose codebase:
$ cd build/linux-i586/jdk-module-image/bin
$ ./java -version
openjdk version "1.8.0-internal"
OpenJDK Runtime Environment (build 1.8.0-internal-david_2012_02_24_08_15-b00)
OpenJDK Client VM (build 23.0-b11, mixed mode)

Looking forward to start hacking around in the code :)

Thanks to Andrew Haley and Alan Bateman for helping me with some issues I came across along the way.

Tuesday, January 31, 2012

Backing up through FTP on a Mac

It's been a little over half a year now that I made the transition to mac. I was more or less forced into it by Dell because they didn't make the 1920x1200 high-res laptops any more that I like working with.
But I have to say, the Apple hardware is second to none - I haven't experienced any mysterious crashes since.

However I had to find Mac alternatives for all the software that I was using, and I managed to find most of what I was looking for quite quickly...
  • For viewing zip archives I currently use Zipeg, it only does extraction but that's good enough more most cases.
  • I really like Breeze for keyboard-based window alignment, similar to what Windows 7 has.
  • And I started using BetterTouchTool to map a keystroke the mousebuttons (saves me from having to press the touchpad all the time, which I don't really like).
  • I'm still undecided about iTunes, I don't really like it (e.g. it's missing a feature that shows what you're playing in the dock or something like that) but it kinda does what it needs to do and there doesn't seem to be anything better on the Mac, strangely enough...
  • Most other applications that I had been using before have Mac versions, except for...
... a backup solution! I was always using Areca in the past which works great and had both Windows and Linux versions available. I guess everybody on Mac simply buys a Time Machine but I wanted to use my existing network storage drive for my backups.

I found iBackup and used it for about half a year. It was seemingly able to do the work over a Samba mount to my network drive, although I did notice that I was getting many failure runs, in fact over half of the runs were failures. It might have been caused by the SMB implementation on my NAS drive, I don't really know but I noticed that it was having issues with relatively large files (over a couple of megs).
I started looking at using FTP to do the backups instead (my NAS drive supports FTP) but iBackup doesn't support that.

After looking around for a while I couldn't find any freely available backup option for Mac that uses FTP. I might have missed one or two, but in the end I started writing a little shell script to do the job. One of the things that I was worried about was the backup speed. My script logs in and out for every directory recursively so I was concerned that it would take much longer to complete than the SMB option. I was very surprised that for my data it took 9 minutes to complete, where the SMB-based option took several hours if it completed at all.

So here it is, a simple little bash script to backup any directory on your mac using FTP, I invoke it like this
example..
ftp-bku.sh ~/bin ~/docs ~/etc ~/utils
Note that I have my FTP credential in my ~/.netrc file so that I don't need to provide them in the script itself...

For those who are interested, you can find the script below (btw one thing that isn't completely reliable yet is the return code of the ftp program. It's 0 in some cases when there actually is a problem. Not sure if this can be fixed, although I could grep the output for error messages...)

FTPURL=ftp://User@192.168.1.24:2121
FTPBASEDIR=MyDir
TIMESTAMP=$(date +"%d%m%y-%H%M%S")
BACKUPDIR=backup_`hostname`_$TIMESTAMP
# Perform the actual FTP backup
function backup {
    PNAME="$1"
    DNAME="$2"
    ftp -i -V $FTPURL<<EOF
    bin
    cd "$FTPBASEDIR"
    mkdir "$BACKUPDIR"
    cd "$BACKUPDIR"
    lcd "$PNAME"
    mkdir "$DNAME"
    cd "$DNAME"
    mput *
    quit
EOF
    if [ "$?" == "0" ]; then
        echo Backup succeeded: "$BACKUPDIR"
    else
        echo Backup failed: "$BACKUPDIR"
    fi
}
# Backup the directory specified as $2 recursively where the base directory is $1
# So to backup /Users/david/etc/tmp as etc/tmp you pass in 
#  /Users/david/etc/tmp etc/tmp
function backup-dir {
    cd "$1"
    if [ -d "$2" ]; then
        BASE="$1"
        DIR="$2"
        BASELEN="${#BASE}"
        SUBDIR="${DIR:BASELEN}"
        TARGET="`basename "$1"`$SUBDIR"
        backup "$2" "$TARGET"
    fi
    cd "$2"
    for dir in *
    do
        if [ -d "$2/$dir" ]; then
            backup-dir "$1" "$2/$dir"
        fi
    done
}
# Staring point, backup all directories specified on the command line
for directory in "$@"
do
    backup-dir "$directory" "$directory"
done

Monday, August 29, 2011

java.util.ServiceLoader in OSGi

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...).

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():
public abstract class MySPI {
    public abstract String doit();
}

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...
You load the SPI implementations by calling ServiceLoader.load(). To obtain the actual implementation objects you iterate over the result of that call:
ServiceLoader ldr = ServiceLoader.load(MySPI.class);
for (MySPI spiObject : ldr) {
  spiObject.doit(); // invoke my SPI object
}
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.
  1. The value of the TCCL is generally not defined in an OSGi context.
  2. 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.
  3. 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.

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).

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.

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...

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:
  SPI-Consumer: *
Which means that this bundle is identified as an SPI consumer with no restrictions. This means that when code such as

  ServiceLoader ldr = ServiceLoader.load(MySPIProvider.class);
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.

In my implementation, which is available in the Apache Aries project, the consumer byte-code is slightly modified so that for the duration of the ServiceLoader.load() call the TCCL is set to the classloader of the providing bundle(s).

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:
  SPI-Provider: *
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 *).

Specifying this header does two things:
  1. It marks the bundle as an SPI provider for use with bundles that are marked as SPI consumer.
  2. It registers the implementations found in META-INF/services in the OSGi Service Registry so that they can be used from there too.
Try it out!
Ok - let's try it out. For that you need:
  • An OSGi Framework - I'm going to use JBoss AS7.
  • An SPI Consumer, Provider and the SPI itself.
  • The Aries SPI-Fly project which implements the functionality associated with the SPI-Consumer and SPI-Provider Manifest headers.
Start the OSGi Framework
I'm taking JBoss AS7 which contains a fully compliant OSGi 4.2 Core Framework. Get version 7.0.1 from here: http://www.jboss.org/jbossas/downloads/
Download and unzip it, then run bin/standalone.sh (or standalone.bat):
...
11:49:45,016 INFO  [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
11:49:45,028 INFO  [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)

BTW the AS7 appserver started up in 1671ms on my plain mac!

Get some example bundles
I'm going to use some of the example bundles from the Aries SPI-Fly project...

You need a SPI Consumer. For instance this one that uses the SPI from the Bundle Activator:
public class Activator implements BundleActivator {
    public void start(BundleContext context) throws Exception {
        ServiceLoader ldr = ServiceLoader.load(SPIProvider.class);
        for (SPIProvider spiObject : ldr) {
            System.out.println(spiObject.doit()); // invoke the SPI object
        }
    }

    public void stop(BundleContext context) throws Exception {}
}

I have a separate bundle that contains the SPI itself:
public abstract class SPIProvider {
    public abstract String doit();
}

I used an abstract class here instead of an interface as that seems to be common practise with SPIs used with ServiceLoader.

And I have an SPI Provider bundle with an implementation:
package org.apache.aries.spifly.mysvc.impl2;
import org.apache.aries.spifly.mysvc.SPIProvider;

public class SPIProviderImpl extends SPIProvider {
    public String doit() {
        return "Doing it too!";
    }
}

The provider bundle has the SPI-Provider: * Manifest header in it and the consumer bundle has the SPI-Consumer: * header. 
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: http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-examples
You can also download them from these locations where I put my built artifacts:
Add OSGi-ServiceLoader support
The Apache Aries SPI Fly component provides two ways to add ServiceLoader support to an OSGi Framework:
  1. 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.
  2. Statically by transforming the bundle Jar file before installing it in the OSGi framework. This mechanism doesn't need the 4.3 Weaving Hooks.
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 Aries SPI-Fly docs for more info.
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: http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-tool or download the copy that I built from here: spifly-static-tool.jar .

  java -jar spifly-static-tool.jar consumer-bundle.jar
  [SPI Fly Static Tool] Processing: consumer-bundle.jar

When it's finished you'll find a new bundle called consumer-bundle_spifly.jar in the same directory. This bundle has the ServiceLoader.load() calls treated so that the TCCL is set appropriately for it to work.

Use it at Runtime
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: http://svn.apache.org/repos/asf/aries/trunk/spi-fly/spi-fly-static-bundle and I have created a pre-built version as well: spifly-static-bundle.jar .

Deploy them into your OSGi Framework. AS7 supports a variety of deployment mechanisms, via maven, AS7 Web Console or Command Line, Felix OSGi Console or JMX. For the sake of simplicity I'm using deployment by copying to the standalone/deployments 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.

So I'm deploying:
On the console we'll see various log messages appear, including these from our SPI Consumer:

12:44:27,480 INFO  [stdout] (MSC service thread 1-3) *** Result from invoking the SPI directly:
12:44:27,483 INFO  [stdout] (MSC service thread 1-3) Doing it too!

Which shows that our ServiceLoader Consumer bundle is now working property inside OSGi.

Conclusion
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.

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.

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: http://www.osgi.org/download/osgi-early-draft-2011-05.pdf

Thursday, November 11, 2010

Using OSGi in JBoss AS7

Recently the first milestone of JBoss Application Server 7 has been released which now comes with OSGi support built-in!

OSGi Support in JBoss AS7 what does that mean?
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...

Let's give it a go!
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: http://www.jboss.org/jbossas/downloads. Unzip it somewhere on disk and start it up by running:
  bin/standalone.sh

Develop your OSGi bundle
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 'classic' distribution. For more serious projects I normally use maven with the Felix Bundle Plugin.

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:


Select the 'Hello OSGi Bundle' template:


And after hitting 'Finish' you've got a simple OSGi bundle, which has an Activator provided by the template:


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:
This puts the OSGi bundle in the directory that you specified in the wizard. In my case the bundle is called TestBundle_1.0.0.jar.

Now deploy in JBoss AS7!
I started AS7 somewhere using the bin/standalone.sh script and it has started up:
...
13:47:22,633 INFO  [org.jboss.as.deployment] (pool-1-thread-2) Started FileSystemDeploymentService for directory /home/davidb/temp/as7/jboss-7.0.0.Alpha1/standalone/deployments
13:47:22,636 INFO  [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]

Ok, so it has started up. A little bit higher in the log you'll also see:
13:47:21,007 INFO  [osgi] Activating OSGi Subsystem

Good - OSGi is there.
Now I copy my TestBundle_1.0.0.jar into the .../jboss-7.0.0.Alpha1/standalone/deployments folder and a moment later I can see the following message in the log:
13:52:24,116 INFO  [stdout] (pool-1-thread-2) Hello World!!
This is the message printed by our OSGi Bundle Activator running in AS7!


Control the framework through JMX
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 service:jmx:rmi:///jndi/rmi://127.0.0.1:1090/jmxrmi to connect to the Remote Process:
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:
Yes, our TestBundle is there, and it has bundle ID 6.

Let's now uninstall it using the framework MBean:
Hit the uninstallBundle JMX API with the bundle ID (6) and you'll see that the bundle gets stopped and then uninstalled.
14:14:00,041 INFO  [stdout] (RMI TCP Connection(13)-127.0.0.1) Goodbye World!!
14:14:00,057 INFO  [org.jboss.osgi.framework.bundle.AbstractBundle] (RMI TCP Connection(13)-127.0.0.1) Bundle uninstalled: TestBundle:1.0.0

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.

Conclusion
OSGi support is now available from the very first milestone of JBoss AS7. Give it a try!
If you have any questions, reach us on the forums: http://community.jboss.org/en/jbossosgi or via JIRA: https://jira.jboss.org/browse/JBOSGI

More in-depth details can be found in Thomas Diesler's blog post: http://jbossosgi.blogspot.com/2010/11/jboss-as7-osgi-integration.html

Thursday, June 17, 2010

Eek! Java reflection can cause NoClassDefFoundError after 15 calls

Now this came as a bit of a surprise to me. Reading this thread on the Felix mailing list...

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.
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.
Another problem with importing sun.reflect is that it's implementation specific. Do other implementations have a similar issue?

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 x method calls.

There is a bug for this at sun: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6265952
Accepted in 2005 but still open...

Wednesday, November 18, 2009

Altering OSGi Service Lookups with Service Registry Hooks

OSGi Services are great. 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. 
 
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.

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 org.acme.Printer interface. Each printer will have additional properties registered that help with the selection:



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:
 
(&(objectClass=org.acme.Printer)(paper-size=A3))

or if you want a printer in a location that starts with 'b' you do this:
 
(&(objectClass=org.acme.Printer)(location=b*))

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.
Service Registry Hooks provide the building blocks that make this possible. I wrote a little bundle called ServiceJockey 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.

Service Registry Hooks: what are they?
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 transparent on demand discovery of remote services. This is what the ListenerHook and FindHook 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.

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 (service.imported) but you clearly don't want to break up all your existing service consumers to add this extra property to their lookup filters.
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 not 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.
Since there isn't really a one-size fits-all here the Service Registry Hooks make it possible to provide a policy for this problem in a separate bundle. You can create EventHooks and FindHooks 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.

Finally, EventHooks and FindHooks, together with the a ServiceListener, allow you to proxy Service Registrations, where you provide a second registration of the same object with modified properties, hiding the original.
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.



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.

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.
The Service Registry Hooks are available in Felix 1.8.0 and Equinox 3.5 and newer versions of these.

Altering Service Registrations

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:
BundleContext context = ... // from Activator.start()
Filter filter = context.createFilter(
  "(&(objectClass=org.acme.Printer)(paper-size=A4))");
st = new ServiceTracker(context, filter, null) {
  public Object addingService(ServiceReference ref) {
    // print out some information on the printer
    // or use the printer
    return super.addingService(ref);
  }
};
st.open();

When I run this it's reporting both printers that I have in my system:
Printer:
objectClass: [org.acme.Printer]
service.id: 27
name: p1
location: b283
capabilities: [Double-sided]
paper-size: [A3, A4]


Printer:
objectClass: [org.acme.Printer]
service.id: 28
name: p7
location: a12
capabilities: [Colour, Staple]
paper-size: [A4, Letter]


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 < 50.
But hold on, we don't even know about energy ratings yet! We need to add this information to the Printer registration without changing the bundle(s) that register the Printers. Let's user Service Jockey to do this.

Service Jockey uses the OSGi Extender Model. This means that it is driven from a data file in a bundle.
So I've got a bundle (called PrinterJockey) that contains META-INF/sj.xml:
<service-jockey xmlns="http://www.coderthoughts.org/schemas/sj/v1.0.0">
 <proxy-registration>
  <rule>
   <service-filter>(&(objectClass=org.acme.Printer)(name=p1))</service-filter>
   <add-property key="energy-rating">75</add-property>
  </rule>
  <rule>
   <service-filter>(&(objectClass=org.acme.Printer)(name=p7))</service-filter>
   <add-property key="energy-rating">50</add-property>
  </rule>
 </proxy-registration>
</service-jockey>


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:
Service-Jockey: META-INF/sj.xml


Lets run the Printer Consumer bundle together with Service Jockey and my 'Printer Jockey' configuration bundle that contains the sj.xml file:
id State Level Bundle
0 ACTIVE  0 org.eclipse.osgi_3.5.1.R35x_v20090827
1 ACTIVE  1 org.coderthoughts.servicejockey_0.0.1
2 ACTIVE  1 PrinterJockey_1.0.0
3 ACTIVE 10 Printers_1.0.0


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.


When I run my system again I can see that it's doing some work. Now my client reports these services:
Printer:
objectClass: [org.acme.Printer]
service.id: 30
name: p1
location: b283
capabilities: [Double-sided]
paper-size: [A3, A4]
energy-rating: 75
.ServiceJockey: Proxied


Printer:
objectClass: [org.acme.Printer]
service.id: 32
name: p7
location: a12
capabilities: [Colour, Staple]
paper-size: [A4, Letter]
energy-rating: 50
.ServiceJockey: Proxied


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.

Altering Client Side Lookups

Now I want my client to only use the one that is rated <= 50.
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.
<service-jockey xmlns="http://www.coderthoughts.org/schemas/sj/v1.0.0">
 <restrict-visibility>
  <rule>
   <bsn>.*</bsn>
   <service-filter>(objectClass=org.acme.Printer)</service-filter>
   <add-filter>(energy-rating<=50)</add-filter>
  </rule>
 </restrict-visibility>
 <proxy-registration>
  ...
 </proxy-registration>
</service-jockey>


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 tag.


You specify to what services the additional filter applies with the tag and you specify the additional filter in . In my case I'm adding the (energy-rating<=50) condition to any Printer Service returned to a consumer (the condition looks a bit dodgy because of the XML escaping of the '<' sign - you could use a CDATA section instead...).
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.


Let's run the client again:
Printer:
objectClass: [org.acme.Printer]
service.id: 32
name: p7
capabilities: [Colour, Staple]
location: a12
paper-size: [A4, Letter]
energy-rating: 50
.ServiceJockey: Proxied


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...

Is it overkill?

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...

Service Jockey - ride your service interactions

I didn't show any of the code to actually use the EventHook and the FindHook. Have a look at the HidingEventHook and HidingFindHook source code for that.

You can check out the source (Apache License) as an Eclipse Project from SVN here. If you fancy modifying the code, I would recommend you also get the tests project and add new ones. The test bundle depend on the Mockito bundle for its mock objects...

I'm sure the Service Jockey isn't perfect, because I only wrote it during an airplane flight so feel free to send patches :)

Also in the source tree, Eclipse projects for the Printers and PrinterJockey example bundles.

Tuesday, October 6, 2009

A single Calendar from Desktop to Phone

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 ;).
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.

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 + Lightning), 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 Google Calendar Provider for Lightning and quite liked it. If only there was something like that for the phone...

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.


1. Go to the Organiser and select Synchronization.



2. Create a new Account.


3. Select Exchange ActiveSync and give it a name, I called mine 'Google'.


4. In the General 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.


5. On the applications tab I only selected Calendar, although the Contancts and Email integration probably also work.


6. Now it asks me to delete all my contacts and 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...



Ok - I'm all set up!
Let's create a calendar event on the phone:


Synchronize with Google...

I think you can even do this automatically, but I haven't enabled that because I don't want my phone bill to explode :)

Let's have a look at Google mail:

Yep, it's there!
Now let's see in Thunderbird Lightning:



And in all three modes I have read/write access. Aaaaah, nice!