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.

Wednesday, January 20, 2010

Step 1 - Creating your Domain

The most common question I've heard regarding OSGi is how do I carve my application into bundles? There is no clear cut answer, but I typically look at functional subsystems within the enterprise (shipping, fulfillment, billing, etc...) and then at the horizontal slices within those systems (presentation, services and persistence). This will tend to provide a good balance for dealing with change and running efforts in parallel without introducing too much complexity. In order to share business objects across separate bundles within the system, we'll need to carve out a domain bundle that others can use.

I'll use pax-construct in these examples as it provides bundle management on top of maven in a relatively intuitive and easy to use fashion. The steps are relatively short to get up and running with a domain bundle.

Start out by creating the project using pax-create-project

Screen shot 2010-01-20 at 9.32.42 PM

This command will setup a maven project for this OSGi system using an OSGi archetype. From here you may want to update the main pom.xml to generate eclipse project and classpath files, update the compiler version used by Maven, and switch it to use equinox over felix as the OSGi container. The main pom.xml will then resemble this

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.pillartech.raffle</groupId>
  <artifactId>raffle</artifactId>
  <version>1.0-SNAPSHOT</version>
  <name>com.pillartech.raffle (OSGi project)</name>
  <description>Generated using Pax-Construct</description>
  <properties>
    <org.osgi.service.http.port>8080</org.osgi.service.http.port>
<org.osgi.service.http.port.secure>8443</org.osgi.service.http.port.secure>
  </properties>
  <packaging>pom</packaging>
  <modules>
    <module>poms</module>
    <module>provision</module>
    <module>domain</module>
  </modules>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.ops4j</groupId>
        <artifactId>maven-pax-plugin</artifactId>
        <version>1.4</version>
        <configuration>
          <provision>
            <param>--platform=equinox</param>
          </provision>
        </configuration>
        <executions>
          <execution>
            <id>ide-support</id>
            <goals>
              <goal>eclipse</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>


After the main project is created pax-runner will be able to start an osgi container from this folder and automatically deploy any homemade or imported bundles automatically. Before we do that, let’s give it something to deploy. Run pax-create-bundle from the main project directory, keeping the group id the same as the one you used when creating the project.



Screen shot 2010-01-20 at 9.47.36 PM 



The pax-create-bundle command will create a familiar project structure for anyone who has used maven. From here you can explore the sample code, delete it when ready and then create your domain classes. My simple example just has two domain classes an Entry and a Raffle. If you are creating domain objects that are not anemic, you’ll probably have a good chance of needing some third party libraries. In my code I planned on using the commons-lang libraries for their equals and hashcode builders. When presented with the need for an external jar in an OSGi environment, you have to ask yourself if this should be embedded into your own bundle as a dependency or installed as a separate bundle within the container. Both have pros and cons, but the decision boils down to whether you feel this jar will be used by other bundles within the system. The commons-lang jars are only useful right now in our domain bundle so we’ll embed. Just update your pom.xml file with the dependency and maven will take care of bringing the external jar into a repository and ensuring that it’s compiled into your bundle. Now is also a good time to add a test dependency on TestNG or JUnit and write some unit tests around the logic in your domain classes.



One other thing to note before deploying this bundle. Pax Construct will make use of BND to generate the manifest file. BND scans the source files, generates an import list, and provides sensible defaults for the manifest so that you don’t have to roll it by hand each time. Since we are just using our domain bundle as a standalone bundle to be consumed by others and it doesn’t have a service or activator, you can simply nuke the generated line in the osgi.bnd file. If all has gone according to plan so far, the last thing is to fire it up. From the main project directory you can run mvn install to compile the domain and then run pax-provision to start up the container and deploy your bundle. Hopefully you get a slew of nifty ascii art indicating that pax-runner has successfully started up and you are greeted with an OSGi prompt. Issuing a short status command if you are using equinox should result in 2 happily active bundles:



Screen shot 2010-01-20 at 10.26.15 PM



So far we’ve created a project, learned how to use pax-construct, embedded a third party jar, and created a bundle that is relatively useless by itself. In the next post, we’ll see how to make use of that bundle from another.



Code samples are available on github.

Monday, January 11, 2010

Step 0 - Simple OSGi

OSGi is a longstanding framework for building modular applications. I won't go into the details as I'm assuming you are somewhat interested in the framework to have found my blog. Two of the best resources I've found are:

Craig Walls' blog
Kirk Knoerschild's blog

Craig and Kirk are helping many (including myself) to grok the benefits of OSGi and dispel the myths around it's adoption. Specifically, Craig's post refuting the challenges with OSGi covers almost all of the issues usually brought up.

Now that that's settled, let's get a simple example up and running.

I'm going to be using Equinox for my examples. Primarily because it supports all of the features we'll need and also because it's bundled with Eclipse so you can easily find it and get it started.

Download equinox from here. You can just get by with the framework jar. You can start the eclipse container with

java -jar org.eclipse.osgi_3.5.1.R35x_v20090827.jar -console

Issuing a short status command with ss shows



Since we haven't built a bundle yet, the only thing available is the osgi core bundle. A bundle is simply no more than a jar file with a quirky META-INF/MANIFEST.MF file. Building the equivalent of Hello World can be done with this:

    1 package com.pillartech;
2
3 import org.osgi.framework.BundleActivator;
4 import org.osgi.framework.BundleContext;
5
6 public class SuperSimple implements BundleActivator {
7
8 public void start(BundleContext ctx) throws Exception {
9 System.out.println("Starting Up!");
10
}

11
12 public void stop(BundleContext ctx) throws Exception {
13 System.out.println("Shutting Down!");
14
}

15 }


This is just a java file though until we add the magic to a MANIFEST.MF file and jar the thing up. Here is a simple manifest to get started:

    1 Bundle-ManifestVersion: 2
2 Bundle-SymbolicName: com.pillartech.SuperSimple
3 Bundle-Name: SuperSimple
4 Bundle-Version: 1.0.0
5 Bundle-Activator: com.pillartech.SuperSimple
6 Import-Package: org.osgi.framework


As long as you include the symbolic name of the bundle it should work, but the rest of it gives you a feel of the meaning of the manifest. It provides all of the information used by other bundles to consume it, and also a list of the other bundles that it imports.

Compiling this class and jar-ing the class and manifest together allows you to deploy it and start it



From there you can see descriptive information about this bundle and it's manifest with the bundle and headers commands



Stopping the bundle will produce an equally productive message and return it to the INSTALLED state. Finally issuing an exit command will shutdown the container.

So with this example we've done the simplest thing we can to get a bundle installed, started, described, and stopped in equinox. These commands are the core to developing and manipulating bundles in a runtime with OSGi. We'll leverage them further in the subsequent examples to build our solution.

Enterprise OSGi in 10 steps

I'm presenting a Modular Java pre-compiler session at Codemash and in preparing I have learned a good deal about OSGi. My mode of learning was to take small granular steps towards a front to back solution to a typical project problem, but with the benefit of using OSGi. If you want to follow my footsteps, the best thing you can do is come to Codemash and attend the pre-compiler. If you aren't fortunate enough to get there, I'm going to post 10 blog posts to build up your knowledge of OSGi and the accompanying tools. This post will serve as a table of contents.

Step 0 - Simple Bundle
Step 1 - Creating your Domain
Step 2 - Importing from another Bundle
Step 3 - Service Oriented OSGi
Step 4 - Spring DM
Step 4.5 - Spring DM Extender Logging
Step 5 - Persistence
Step 6 - Integration Testing
Step 7 - Web Development
Step 8 - Skinning a UI
Step 9 - Deploy Time Configuration