Two technologies that have been on my radar for a while are JavaFX, Sun’s entry in the RIA race, and OSGi, the Dynamic Module System for Java. In this tutorial, I will combine the two by running a JavaFX script from an OSGi bundle. You will need Eclipse and Java 6. [That’s right: a JavaFX tutorial that doesn’t want you to use NetBeans!]
Although JavaFX is still in the beta stage (despite being announced at JavaOne last year), and is very much in flux, it makes for an interesting UI technology. I wouldn’t want to write production ready applications in it yet, but maybe that will change in the not-too-distant future.
OSGi, on the other hand, is very much a mature technology. It is widely used, for instance for the plug-ins that make up Eclipse. In fact, Eclipse Equinox is now the reference implementation for OSGi. BTW, the OSGi term for plug-in is bundle, and I will use the two interchangeably.
Wrapping the JavaFX libraries in an OSGI bundle
Everything in OSGi happens inside a bundle, so the first step to running a JavaFX script from an OSGi bundle, is to create a bundle that holds the JavaFX jars. You will need to download the latest JavaFX distribution and unzip it somewhere.
Then, in Eclipse, select File|New|Project...
, so that the New Project
wizard appears. In the first step, select from the Plug-in Development
category the Plug-in from existing JAR archives
, and click Next
. In the second step, click the Add External
button and add all the jars from the JavaFX distribution’s lib
directory. In the third step, enter JavaFX
for the Project name
, and click an OSGi framework
under Target platform
. Select standard
for the OSGi framework, and click Finish
. After a while, the plug-in project appears.

Next, open META-INF/MANIFEST.MF
, select the MANIFEST.MF
tab, and paste the following line into it:
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Now right-click on JRE System Library
in the Package Explorer, and select Properties
. For System library
, select Execution environment
, and from the combo box next to it, select JavaSE-1.6
. Click OK
.

Creating an OSGi bundle that will run a JavaFX script
You will now use the bundle you just created by a new bundle, the one running the JavaFX script. Again, select File|New|Project...
, but this time, select Plug-in Project
from the Plug-in Development
category. In the next step, enter Run JavaFX
for the Project Name
, select the standard OSGi framework as the Target platform
(same as above), and click Next
. In the next step, just click Finish
to create the project.

In the Manifest Editor, select the Dependencies
tab. Click the Add
button under Required Plug-ins
, select JavaFX (1.0.0)
from the list, and click the Save
button in the toolbar.

Creating the JavaFX script
Create a new directory named script
, and create a new file named HelloWorld.fx
in it. Paste the following code into it:
import javafx.application.Frame;
import javafx.application.Stage;
import javafx.scene.paint.Color;
import javafx.scene.text.*;
Frame {
title: "Hello World!"
width: 550
height: 200
visible: true
stage: Stage {
content: Text {
font: Font {
name: "Sans Serif"
style: FontStyle.BOLD
size: 24
}
x: 20
y: 40
stroke: Color.BLUE
fill: Color.BLUE
content: "Hello from JavaFX Compiled Script"
}
}
}

Next, right-click the script
directory, and select Build path|Use as Source Folder
.
Running the script
To actually run the JavaFX script, we will use JSR-223: Scripting for the Java Platform. [That’s why I made you select JavaSE-1.6
as the bundle execution environment.]
In the Run JavaFX
project, open the src
directory, the run_javafx
package, and then the Activator
class. [A bundle’s activator allows you to do stuff when your bundle starts or stops.] In the start()
method, add the following code:
final ScriptEngine engine = new ScriptEngineManager()
.getEngineByExtension("fx");
final String scriptName = "HelloWorld.fx";
final InputStream stream = Thread.currentThread()
.getContextClassLoader()
.getResourceAsStream(scriptName);
engine.eval(new InputStreamReader(stream));
Use Eclipse’s Quick Fix to add the required import statements for ScriptEngine
, ScriptEngineManager
, InputStream
, and InputStreamReader
.

To run the bundle from Eclipse, select the MANIFEST.MF
file, select the Overview
tab, and click the Run
button at the top.

In the Console, you will see the osgi>
prompt, followed by a lot of error output. If you scroll through the output, you will see
org.osgi.framework.BundleException: Exception in
run_javafx.Activator.start() of bundle Run_JavaFX.
[...]
Caused by: java.lang.NullPointerException
at run_javafx.Activator.start(Activator.java:24)
[...]

Type exit
+ Enter in the Console to shut down the plug-in.
Finding the script engine
The NullPointerException
comes from the ScriptEngine
for JavaFX that could not be found. We can fix this as follows: create a new folder named services
under META-INF
, and copy the javax.script.ScriptEngineFactory
file from the JavaFX
project to it. See the Service Provider section in the Jar File Specification for an explanation.

Now when you run the plug-in, you will get
org.osgi.framework.BundleException: Exception in
run_javafx.Activator.start() of bundle Run_JavaFX.
[...]
Caused by: javax.script.ScriptException: compilation failed
at com.sun.tools.javafx.script.JavaFXScriptEngineImpl.parse(JavaFXScriptEngineImpl.java:254)
at com.sun.tools.javafx.script.JavaFXScriptEngineImpl.eval(JavaFXScriptEngineImpl.java:144)
at com.sun.tools.javafx.script.JavaFXScriptEngineImpl.eval(JavaFXScriptEngineImpl.java:135)
at com.sun.tools.javafx.script.JavaFXScriptEngineImpl.eval(JavaFXScriptEngineImpl.java:140)
at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:232)
at run_javafx.Activator.start(Activator.java:24)
[...]

There is nothing wrong with the JavaFX script, though. You can test this yourself by running the command line tools (javafxc
and javafx
) against it.
Fixing the class path
The problem is in the JavaFXScriptEngineImpl
class. It passes the class path to the JavaFXScriptCompiler
. But this class path is not the actual class path that is used by the class loader, it is taken from the java.class.path
system property. You can override this by either setting the com.sun.tools.javafx.script.classpath
system property, or by setting the classpath
attribute on the ScriptContext
.
I will take that last approach. Just before the engine.eval(...)
line, add the following:
engine.getContext().setAttribute("classpath",
getJavafxClassPath(), ScriptContext.ENGINE_SCOPE);
Implementing the getJavafxClassPath()
method is a bit tricky:
private String getJavafxClassPath() {
String result = FX.class.getProtectionDomain()
.getCodeSource().getLocation().toExternalForm();
final int index = result.indexOf(":/");
if (index >= 0) {
result = result.substring(index + 1);
}
return result;
}

Now finally, when you run the plug-in, it will show the Hello world
window:

You must be logged in to post a comment.