-
Notifications
You must be signed in to change notification settings - Fork 274
OSGi
Wiki ▸ Documentation ▸ OSGi
Creating highly modular and dynamic applications on the JVM is made possible by OSGi - a dynamic module system for Java. Feel free to skip this chapter for now if you're not interested in OSGi technology.
To run TornadoFX in an OSGi container, you need to load the following bundles. Usually this is a matter of dumping these jars into the bundle
directory. Note that any jar that is to be used in an OSGi container needs to be "OSGi enabled". This means adding some OSGi specific entries the META-INF/MANIFEST.MF
file. Alle the resources below are OSGi enabled, including the TornadoFX
jar.
Artifact | Version | Binary |
---|---|---|
JavaFX 8 OSGi Support | 8.0 | jar |
TornadoFX | 1.5.3 | jar |
Kotlin OSGI Bundle* | 1.0.3 | jar |
Configuration Admin** | 1.8.10 | jar |
Commons Logging | 1.2 | jar |
Apache HTTP-Client | 4.5.2 | jar |
Apache HTTP-Core | 4.4.5 | jar |
JSON | 1.0.4 | jar |
*
The Kotlin OSGi bundle contains special versions of kotlin-stdlib
and kotlin-reflect
with the required OSGi manifest information.
**
This links to the Apache Felix implementation of the OSGi Config Admin interface. Feel free to use the implementation from your OSGi container instead.
You should be familiar with the basics of OSGi before you continue. To get a quick overview of OSGi technology you can check out the tutorials on the OSGi Alliance website. The Apache Felix tutorials are also a good reference for basic OSGi patterns.
When the TornadoFX bundle is loaded into an OSGi container, it starts listening for other bundles that provides services to TornadoFX. These services can be any of the following type:
You can tell the TornadoFX OSGi Runtime to automatically start your application when it is loaded. This is done by registering your application in your bundle Activator
:
class Activator : BundleActivator {
override fun start(context: BundleContext) {
context.registerApplication(MyApp::class)
}
override fun stop(context: BundleContext) {
}
}
Provided that the TornadoFX bundle is available in your container, this is enough to start your application automatically. You can also stop and start it, something that is normally not possible in a JavaFX environment. This is possible because TornadoFX creates a ProxyApplication
that forwards requests to the application provided by your bundle. You can even unload one application and load another without stopping the JVM.
You can provide type safe stylesheets to other TornadoFX bundles by registering them in the Activator
:
class Activator : BundleActivator {
override fun start(context: BundleContext) {
context.registerStylesheet(Styles::class)
}
override fun stop(context: BundleContext) {
}
}
Whenever this bundle is loaded, every active View will have this stylesheet applied. When the bundle is unloaded, the stylesheet is automatically removed. If you want to provide multiple stylesheets based on the same style classes, it is a good idea to create one bundle that exports the cssclass
definitions, so that your Views can reference these styles, and the stylesheet bundles can create selectors based on them.
A very cool aspect of OSGi is the ability to have UI elements pop up when they become available. A typical use case could be a "dashboard" application. The base application bundle contains a View that can hold other Views, and tells the TornadoFX OSGi Runtime that it would like to automatically embed Views if they meet certain criteria.
This is a View that contains a VBox. It tells the TornadoFX OSGi Runtime that it would like to have other Views embedded into it if they are tagged with the discriminator dashboard:
class Dashboard : View() {
override val root = VBox()
init {
title = "Dashboard Application"
addViewsWhen { it.discriminator == "dashboard" }
}
}
If the addViewsWhen
function returns true, the View is added to the VBox
. To offer up Views to this Dashboard, another bundle would declare that it wants to export it's View by setting the dashboard
discriminator:
class Activator : BundleActivator {
override fun start(context: BundleContext) {
context.registerView(MusicPlayer::class, "dashboard")
}
override fun stop(context: BundleContext) {
}
}
The addViewsWhen
function is smart enough to inspect the VBox
and find out how to add the child View it was presented. It can also figure out that if you call the function on a TabPane
it would create a new Tab
and set the title to the child View title etc. If you would like to do something custom with the presented Views, you can return false
from the function so that the child View will not be added automatically and then do whatever you want with it. Even though the Tab example is supported out of the box, you could do it explicitly like this:
tabPane.addViewsWhen {
if (it.discriminator == "dashboard") {
val view = it.getView()
tabPane.tab(view.title, view.root)
}
false
}
Manually handling of dynamic Views
Currently this is the extent of the specialised support for OSGi in the TornadoFX framework. As always, TornadoFX doesn't obstruct you from doing everything manually, but provides some convenient and elegant patterns to help you smoothly integrate OSGi into your applications.
Next: i18n