-
Notifications
You must be signed in to change notification settings - Fork 272
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. All the resources below are OSGi enabled, including the TornadoFX
jar.
Click here to download a distribution with all the required bundles already installed into Apache Felix 5.4.0.
Artifact | Version | Binary |
---|---|---|
JavaFX 8 OSGi Support | 8.0 | jar |
TornadoFX | 1.5.5 | 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. Some containers, like Apache Karaf, already has the Config Admin bundle loaded, so you won't need it there.
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) {
}
}
If you prefer OSGi declarative services instead, this will have the same effect provided that you have the OSGi DS bundle loaded:
@Component
class AppRegistration : ApplicationProvider {
override val application = MyApp::class
}
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) {
}
}
Using OSGi Declarative Services the registration looks like this:
@Component
class StyleRegistration : StylesheetProvider {
override val stylesheet = Styles::class
}
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) {
}
}
Again, the OSGi Declarative Services way of exporting the View would look like this:
@Component
class MusicPlayerRegistration : ViewProvider {
override val discriminator = "dashboard"
override fun getView() = find(MusicPlayer::class)
}
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
}
Manual handling of dynamic Views
A good starting point is the tornadofx-maven-osgi-project
template in the TornadoFX IntelliJ IDEA plugin. This contains everything you need to automatically build OSGi bundles from your sources. The OSGI IDEA plugin makes it very easy to setup and run an OSGi container directly from the IDE. There is a screencast at
https://www.youtube.com/watch?v=liOFCH5MMKk that shows these concepts in action.
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.
TornadoFX has a built in OSGi console from which you can inspect bundles, change their state and even install new bundles with drag and drop. You can bring up the console with Alt-Meta-O
or configure another shortcut by setting FX.osgiConsoleShortcut
or programmatically opening the OSGIConsole
View.
Next: i18n