JavaFX and OSGi, part 1

Integrating new technologies is something that I like. The combination of JavaFX RIA technology and the OSGi platform was on my wish-list for some time. The title of this entry contains "part 1" since I assume this is an ongoing effort. When combining JavaFX and OSGi, a number of possibilities exist:

  • Install a bundle containing JavaFX code in an OSGi framework
  • Run an OSGi framework in a JavaFX environment
The first option is described in this blogentry.
The second option is very relevant to me, since I hope that there will be a huge number of devices that come pre-installed with JavaFX. If the OSGi lifecycle management platform runs on these devices, lots of nice stuff can be executed on the devices.

The latest OSGi specification contains a framework launch API. This allows the framework to be instantiated by third party code. This is cool, since it allows me to start an OSGi framework without any code change to the framework implementation code.

For my proof of concept, I use the KnopflerFish framework from Makewave. This is an Open Source implementation of the OSGi specification, and it is very readable.
Running the Knopflerfish framework in JavaFX is not too difficult. You have to be careful with the threading system of JavaFX though ("don't block the AWT thread", remember?). I did it this way:

  • The Main.fx class launches the Framework and obtains a reference to the System Bundle, hence to a BundleContext Object
  • The BundleContext is used for installing new Bundles and for registering a ServiceListener and a BundleListener. Installing Bundles is done asynchronously, not in the UI thread.
  • A JavaFX class implements BundleListener and ServiceListener is notified on Bundle and Service Events, and some UI component the Main.fx class reflects the changes.

This approach leads to 4 classes:

  • Main.fx
  • BundleInstaller.fx contains the FX code for installing bundles
  • NativeInstaller.java contains the Java code for installing bundles
  • FxServiceListener.fx implements both ServiceListener and BundleListener

Here is the code:

package com.lodgon.kfix;

import org.osgi.framework.launch.Framework;
import org.osgi.framework.launch.FrameworkFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Bundle;
import java.util.HashMap;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.TextBox;
import javafx.scene.control.Button;
import javafx.scene.Group;
import javafx.scene.layout.VBox;
import javafx.scene.control.Label;
import javafx.async.Task;

/**
 * @author johan
 */

var urlText: TextBox = TextBox {
    text: "http://xavier.lodgon.com/osgi/log_all-2.0.0.jar"
    width: 282
    selectOnFocus: true
  }

  var installButton: Button = Button {
    translateX: 300;
      text: "install"
      action: function() {
        var installer: BundleInstaller = BundleInstaller {
          bc:bc;
          location:urlText.text;
        }
        installer.start();
      }
    }

  var installGroup: Group = Group {
    translateY:10;
    content: [urlText, installButton];
  }


  public var eventListBox: VBox = VBox {
    translateY:50;
  };



var stage: Stage = Stage {
    title: "Application title"
    width: 450
    height: 180
    scene: Scene {
        content: bind [
            installGroup,
            eventListBox
        ]
    }
  }

var bc: BundleContext;
var listener: FxServiceListener ;

function run () {
  stage;
  var knopflerfish: org.knopflerfish.framework.Main =
  new org.knopflerfish.framework.Main();
  var factory:FrameworkFactory = knopflerfish.getFrameworkFactory();
  var map:HashMap = new HashMap();
  var framework: Framework = factory.newFramework(map);
  framework.init();
  listener = FxServiceListener{};
  bc = framework.getBundleContext();
  bc.addServiceListener (listener);
  bc.addBundleListener (listener);
}

BundleInstaller:
package com.lodgon.kfix;

import javafx.async.JavaTaskBase;

import javafx.async.RunnableFuture;
import org.osgi.framework.BundleContext;

/**
 * @author johan
 */

public class BundleInstaller extends JavaTaskBase {

  public-init var bc: BundleContext;
  public-init var location: String;
  var installer: NativeInstaller;

  override protected function create() : RunnableFuture {
    installer = new NativeInstaller(bc, location);
    return installer;
 }

}
NativeInstaller:

package com.lodgon.kfix;

import javafx.async.RunnableFuture;
import org.osgi.framework.BundleContext;

/**
 *
 * @author johan
 */
public class NativeInstaller implements RunnableFuture {
  private final String location;
  private final BundleContext bc;

  public NativeInstaller (BundleContext bc, String loc) {
    this.bc = bc;
    this.location = loc;
  }

  @Override
  public void run() throws Exception {
    System.out.println ("[JVDBG] I have to install bundle at "+location);
    bc.installBundle(location);
    System.out.println ("[JVDBG] I installed "+location);
  }

}

FxServiceListener:
package com.lodgon.kfix;

import org.osgi.framework.*;
import javafx.scene.control.Label;

/**
 * @author johan
 */
public class FxServiceListener extends ServiceListener, BundleListener {

  override function serviceChanged (evt: ServiceEvent) {
    println ("[KFiX] Service changed: {evt}");
    var label: Label = Label {
        text: "service changed: {evt}"
      }
   insert label into Main.eventListBox.content;
  }

  override function bundleChanged (evt: BundleEvent) {
    println ("[KFiX] Bundle changed: {evt}");
    var label: Label = Label {
        text: "bundle changed: {evt}"
      }
    insert label into Main.eventListBox.content;
  }


}
That's it for now, but I have more ideas...

written on 05 Oct 2009 20:21.

no comments

Create comment