Sunday, January 24, 2010

Step 2 – Importing Bundles

The previous post in this series covered a lot of ground on setting up a project with pax-construct, creating a domain bundle, and then embedding another jar file as part of that bundle. At the end of the day we had a simple bundle deployed to our OSGi container, but it didn’t have any external dependencies or an activator so we’ve only developed a traditional jar file and deployed it somewhere new. Let’s change that by creating another bundle that relies on the domain.

From the main directory of the project, run pax-create-bundle as we did last time. If you would like to omit the generated code or interfaces, you can pass flags directly to maven. In this example I’ve advised maven to not bother creating a dummy activator class.

Screen shot 2010-01-24 at 9.42.14 PM

I’ve titled this bundle “rigged” as we’re going to rig our raffle to consistently pick one winner… me. The code to rig the raffle is pretty straightforward as it just makes use of the business logic in the domain classes we created last time. Unfortunately, we have no references to those classes yet, so we need to import that bundle. From the rigged sub-directory run the pax-import-bundle command, passing in the group, artifact, and version information used when creating the domain bundle.

Screen shot 2010-01-24 at 9.48.08 PM

Pax will update the poms appropriately so that the dependency points to the domain bundle within the same project. Now we can code up a simple activator that will add some raffle entries on startup and pick a winner on shutdown.

package com.pillartech.raffle.rigged.internal;
import java.util.Set;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.pillartech.raffle.domain.Entry;
import com.pillartech.raffle.domain.Raffle;
public final class SimpleRiggedRaffleActivator implements BundleActivator {
  private Raffle raffle = null;
  public void start(BundleContext bc) throws Exception {
    System.out.println("Adding entrants");
    raffle = new Raffle();
    raffle.setNumberOfWinners(1);
    for (int i = 0; i < 10; i++) {
      Entry e = new Entry();
      e.setName("The Todd(" + i + ")");
      e.setEmail("toddkaufman@gmail.com");
      raffle.addEntry(e);
    }
    System.out.println(raffle.getEntries().size()
        + " entries added to the raffle.");
  }
  public void stop(BundleContext bc) throws Exception {
    System.out.println("And the winner of the raffle is ...");
    Set<Entry> winners = raffle.pickWinners();
    for (Entry winner : winners) {
      System.out.println(winner.getName());
    }
  }
}


The code to implement an activator is not complex. Implementing the BundleActivator interface necessitates a start and stop method, each taking a BundleContext as a parameter. We don’t need to use the BundleContext just yet so we’ll just get by with some simple rigging of the raffle and system.out.println calls. The only last thing to update is the osgi.bnd file so that the manifest generated correctly points to the activator. The bnd file can make use of properties set by maven so it remains relatively simple.



#-----------------------------------------------------------------
# Use this file to add customized Bnd instructions for the bundle
#-----------------------------------------------------------------
Bundle-Activator: ${bundle.namespace}.internal.SimpleRiggedRaffleActivator


Running mvn clean install and pax-provision at the project layer should get you a success message and something that looks like this



Screen shot 2010-01-24 at 10.20.20 PM



You can see that the activator kicked off on the rigged bundle and it added some entries into the raffle. You can look at how the bundles are viewed differently by the container by running bundle and headers commands on the two. Domain will show relatively little, while the rigged bundle will display the import of the domain bundle and the class name specified as the activator. Stopping the raffle will correctly award the raffle prize to me.



So to this point we’ve created a couple of bundles, imported one to the other, and leveraged the activator to execute some logic when the bundle is started or stopped. Importing other bundles is obviously useful, but to really show the power of OSGi, we need to get bundles talking together in a service / consumer fashion. Stay tuned.



Examples as always, available via github.

No comments: