Scala and OSGi using NetBeans

For some reason, I can't keep OSGi in my head. Everything I read about it slips away in a few weeks, and I have re-read the guides and tutorials.

Here's a memo of setting up OSGi bundle written in Scala using NetBeans, following Neil Barlett's OSGi in Practice, except the book uses Eclipse.

Installing Scala on Mac

Skip this section if you use non-Mac. Install MacPorts. Run the following from Terminal:

sudo port install scala-devel

This could take minutes as it downloads the files.

Install RCEnvironment by copying RCEnvironment.prefPane into /Library/PreferencePanes/. When you open System Preferences, Environment Variables now show up. Set JAVA_HOME and SCALA_HOME to where it makes sense. I have them set to /Library/Java/Home/ and /opt/local/share/scala/.

Installing NetBeans

Download NetBeans. You could pick any edition as long as it has Java SE. Follow the instructions in Scala Plugins for NetBeans 6.8 v1.x (RC1) to install Scala plugin.

Installing a Framework

Download Felix Framework binary package, extract it under some location such as ~/Application/. The felix directory will be referred to as FELEX_HOME.

Setting up NetBeans

Open NetBeans. Within NetBeans, open Library Manager (Tools menu → Libraries). Hit New Library... button, and type "ApacheFelix2.1.0" or whatever the version number you are using. Next hit Add JAR/Folder... button and add FELIX_HOME/bin/felix.jar. I've also added some source zip files under Sources section.

Create new Scala project by selecting (File menu → New Project...) and picking Scala Application under Scala folder. Enter the project name OSGiTutorial and accept all the other default values. This project directory will be referred to as PROJECT_HOME.

Under Projects tree view, find Libraries node for the newly created project, right-click and select Add Library.... This should pop up a dialog where you can select ApacheFelix1.8.0 and hit Add Library button.

Copy conf/ and bundle/ from FELIX_HOME to PROJECT_HOME. If you are using Felix 2.0.1 like I am, you have to modify the conf/config.properties file to mimic the older behavior of Felix as follows:

Line 61:

#felix.auto.deploy.action=install,start
felix.auto.deploy.action=

Line 72:

#felix.auto.install.1=
felix.auto.start.1= \
 file:bundle/org.apache.felix.shell-1.4.1.jar \
 file:bundle/org.apache.felix.shell.tui-1.4.1.jar \
 file:bundle/org.apache.felix.bundlerepository-1.4.2.jar

Running Felix

Select (Run menu → Set Project Configuration → Customize...) to open the Project Properties. Enter org.apache.felix.main.Main to be the Main class, and enter
-Dfelix.config.properties=file:conf/config.properties to be VM options. Now, Felix will start by hitting Run button.

Installing bnd

Download Peter Kriens's bnd.jar, and put under some location like ~/Application/bnd. Open Library Manager (Tools menu → Libraries). Hit New Library... button, and type "Bnd." Next hit Add JAR/Folder... button and add BND_HOME/bnd-0.0.xxx.jar.

Under Projects tree view, find Libraries node for the newly created project, right-click and select Add Library.... This should pop up a dialog where you can select Bnd and hit Add Library button.

Installing Scala OSGi bundle

Find out the Scala version that's being used by NetBeans by selecting (Tools menu → Scala Platforms) if you don't have $SCALA_HOME set, or by running
$ scala -version from the Terminal if you have $SCALA_HOME. In my case it was 2.8.0.Beta1-RC7.

Download Heiko Seeberger's Scala OSGi bundles. The snapshots are available, so find OSGi bundle of scala-library for the appropriate version, and place them in some folder such as ~/Applications/scala-osgi. Copy scala-library-2.8.0-20100109.130112-12.jar into $PROJECT_HOME/bundle/ folder as well.

Hello, World!

Under Projects tree view there should be a node for Source Packages and a package called osgitutorial. Right-click and delete Main.scala. Right-click, select (New → Other...) and select Scala Class under Scala node. Enter HelloWorldActivator as the name of the file and hit Finish button.

Here's the code for HelloWorldActivator.scala:

package osgitutorial
 
import org.osgi.framework._
 
class HelloWorldActivator extends BundleActivator {
  def start(context: BundleContext) {
    println("Hello, World!");
 
    val bundleNames = context.getBundles()
      .map(b => b.getSymbolicName())
      .filter(b => b != context.getBundle());
    println("Installed bundles: " + bundleNames.mkString(", "));
  }
 
  def stop(context: BundleContext) {
    println("Goodbye, World!");
  }
}

Switch to Files tree view next Project tree view on NetBeans. Create helloworld.bnd by selecting (File menu → New File...), and selecting Empty File under Other. Click Next, type helloworld.bnd into the file name, and click Finish. Here's the content for helloworld.bnd:

# helloworld.bnd
 
Private-Package: osgitutorial
Bundle-Activator: osgitutorial.HelloWorldActivator

Under Files tree view, open build.xml. This is an Ant build file. Add two targets within the project tags as follows:

<?xml version="1.0" encoding="UTF-8"?>
<project name="OSGi_Tutorial" default="default" basedir=".">
    <description>Builds, tests, and runs the project OSGi Tutorial.</description>
    <import file="nbproject/build-impl.xml"/>
    <target name="bnd-build">
        <taskdef name="bnd"
          classname="aQute.bnd.ant.BndTask"
          classpath="dist/lib/bnd-0.0.337.jar"/>
 
        <bnd
          classpath="build/classes"
          eclipse="false"
          failok="false"
          exceptions="true"
          files="helloworld.bnd"
          output="bundle"/>
    </target>
 
    <target name="-post-jar" depends="bnd-build">
    </target>
</project>

Now by clicking Build button on NetBeans, OSGi bundle helloworld.jar gets created under bundle/ folder.

Installing the bundles

If you haven't already, hit Run button on NetBeans to start Felix. It should look like the following:

init:
deps-jar:
compile:
run:
 
Welcome to Felix.
=================
 
->

This is Felix shell, which we could use to install bundles. Type ps at the prompt and hit return. This will display all active bundles:

-> ps
START LEVEL 1
   ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   2] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[   3] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
-> 

Now type the following command at Felix shell:
install file:bundle/helloworld.jar
This should return

-> install file:bundle/helloworld.jar
Bundle ID: 4

Next, type following command at Felix shell:
start 4
This should return

-> start 4
org.osgi.framework.BundleException: Unresolved constraint in bundle 4: package; (package=scala.runtime)

So helloworld bundle is loaded into Felix, but it's not able to resolve scala.runtime package, which makes sense since we haven't load it yet.

Now type the following command at Felix shell:
install file:bundle/scala-library-2.8.0-20100109.130112-12.jar
This should return

-> install file:bundle/scala-library-2.8.0-20100109.130112-12.jar
Bundle ID: 5

Next type start 4 again at Felix shell. This should return

-> start 4
Hello, World!
Installed bundles: org.apache.felix.framework, org.apache.felix.shell, org.apache.felix.shell.tui, org.apache.felix.bundlerepository, helloworld, org.scala-lang-osgi.scala-library

Incremental Development

Now that both Build button and Run button are configured, we can try incremental development by updating bundles without shutting down the OSGi container.

Make some changes in HelloWorldActivator.scala. For example, change "Hello, World!" to "Bonjour le Monde!" Then, hit the Build button to build helloworld.jar bundle.

Change the Output tab back to OSGi_Tutorial (run) and type stop 4 at Felix shell. This stops the bundle. Next type update 4 at Felix shell. This updates the bundle. Finally by typing start 4 at the shell, we can start the bundle again:

-> stop 4
Goodbye, World!
-> update 4
-> start 4
Bonjour le Monde!
Installed bundles: org.apache.felix.framework, org.apache.felix.shell, org.apache.felix.shell.tui, org.apache.felix.bundlerepository, helloworld, org.scala-lang-osgi.scala-library

In the end

Type shutdown to shutdown Felix. The installed bundles will stay even after the shutdown. To clear the state, you have to purge PROJECT_HOME/felix-cache/ folder. I merely followed along Neil Barlett's OSGi in Practice, so most of the credits should go to him.