Running a JavaFX application on a Virgo OSGi server

This article is to explain how to create and run a Java FX application on a eclipse Virgo application server as an OSGi bundle.

We will start where we left of in the previous article, about how to create an OSGi service and client. We will turn the client into a JavaFX application and let it run on the server. 

Enable JavaFX in Virgo

First we need to make sure we can run javafx on the Virgo server. The java fx library is default not available on the classpath of the OSGi bundles in Virgo. To make them available to all bundles we have to add the java fx libraries and all the packages the libraries should export on the system classpath. 
To enable this we need to do two things:

Copy the JavaFX jar 

First we have to copy the jfxrt.jar to the lib directory in the Virgo root directory. The jar can be found in your JDK directory.

The location of jfxrt.jar in Oracle Java 7 is:

<JRE-Directory>/lib/jfxrt.jar

And in Oracle Java 8 it is:

<JRE-Directory>/lib/ext/jfxrt.jar


Add the packages to the system packages 

Second we need to specify the packages of the jfxrt.jar as system packages in the  Java server 6 profile configuration file. 

We will add all the packages which are made available in the jfxrt.jar to the file <VIRGO-ROOT>/configuration/java6-server.profile In the section System packages (after the line: "org.osgi.framework.system.packages = \" )

These are the lines we need to add:


javafx.application, \
com.sun.browser.plugin, \
com.sun.deploy.uitoolkit.impl.fx, \
com.sun.deploy.uitoolkit.impl.fx.ui, \
com.sun.deploy.uitoolkit.impl.fx.ui.resources, \
com.sun.deploy.uitoolkit.impl.fx.ui.resources.image, \
com.sun.glass.events, \
com.sun.glass.ui, \
com.sun.glass.ui.delegate, \
com.sun.glass.ui.gtk, \
com.sun.glass.ui.mac, \
com.sun.glass.ui.win, \
com.sun.glass.ui.x11, \
com.sun.glass.utils, \
com.sun.javafx, \
com.sun.javafx.animation, \
com.sun.javafx.animation.transition, \
com.sun.javafx.applet, \
com.sun.javafx.application, \
com.sun.javafx.beans, \
com.sun.javafx.beans.annotations, \
com.sun.javafx.beans.event, \
com.sun.javafx.binding, \
com.sun.javafx.charts, \
com.sun.javafx.collections, \
com.sun.javafx.collections.annotations, \
com.sun.javafx.collections.transformation, \
com.sun.javafx.css, \
com.sun.javafx.css.converters, \
com.sun.javafx.css.parser, \
com.sun.javafx.cursor, \
com.sun.javafx.effect, \
com.sun.javafx.embed, \
com.sun.javafx.event, \
com.sun.javafx.font, \
com.sun.javafx.fxml, \
com.sun.javafx.fxml.builder, \
com.sun.javafx.fxml.expression, \
com.sun.javafx.geom, \
com.sun.javafx.geom.transform, \
com.sun.javafx.iio, \
com.sun.javafx.iio.bmp, \
com.sun.javafx.iio.common, \
com.sun.javafx.iio.gif, \
com.sun.javafx.iio.jpeg, \
com.sun.javafx.iio.png, \
com.sun.javafx.image, \
com.sun.javafx.image.impl, \
com.sun.javafx.jmx, \
com.sun.javafx.logging, \
com.sun.javafx.menu, \
com.sun.javafx.perf, \
com.sun.javafx.property, \
com.sun.javafx.property.adapter, \
com.sun.javafx.robot, \
com.sun.javafx.robot.impl, \
com.sun.javafx.runtime, \
com.sun.javafx.runtime.async, \
com.sun.javafx.runtime.eula, \
com.sun.javafx.scene, \
com.sun.javafx.scene.control, \
com.sun.javafx.scene.control.behavior, \
com.sun.javafx.scene.control.skin, \
com.sun.javafx.scene.control.skin.caspian, \
com.sun.javafx.scene.control.skin.resources, \
com.sun.javafx.scene.input, \
com.sun.javafx.scene.layout.region, \
com.sun.javafx.scene.paint, \
com.sun.javafx.scene.shape, \
com.sun.javafx.scene.text, \
com.sun.javafx.scene.transform, \
com.sun.javafx.scene.traversal, \
com.sun.javafx.scene.web, \
com.sun.javafx.scene.web.behavior, \
com.sun.javafx.scene.web.skin, \
com.sun.javafx.sg, \
com.sun.javafx.sg.prism, \
com.sun.javafx.stage, \
com.sun.javafx.tk, \
com.sun.javafx.tk.desktop, \
com.sun.javafx.tk.quantum, \
com.sun.javafx.util, \
com.sun.media.jfxmedia, \
com.sun.media.jfxmedia.control, \
com.sun.media.jfxmedia.effects, \
com.sun.media.jfxmedia.events, \
com.sun.media.jfxmedia.locator, \
com.sun.media.jfxmedia.logging, \
com.sun.media.jfxmedia.track, \
com.sun.media.jfxmediaimpl, \
com.sun.media.jfxmediaimpl.platform, \
com.sun.media.jfxmediaimpl.platform.gstreamer, \
com.sun.media.jfxmediaimpl.platform.java, \
com.sun.media.jfxmediaimpl.platform.osx, \...

Application activation

Starting a JavaFX application in an OSGi server is a little different the usual. 
The application will not be started via the usual "start(Stage stage)" method. If you try this you would get a message that the JavaFX toolkit is not enabled. 
To be able to start the JavaFX application we can use Swing. We create a frame in Swing and then insert the JavaFX scene into it via a FXPanel. Swing will enable the JavaFX toolkit and run the JavaFX part of the application.

To do this we can change the init method in the App class in the OSGi client bundle:

nl.javacodingspot.client.App:

    /**
     * Initialize the application
     */
    final public void init() {

        // Create a Swing JFrame
        JFrame frame = new JFrame("Java coding spot - OSGi client");
        frame.setMinimumSize(new Dimension(500, 500));
        frame.setMaximumSize(new Dimension(500, 500));
        frame.setPreferredSize(new Dimension(500, 500));
        frame.setVisible(true);

        // Create je Swing JFXPanel where the Java FX components can be loaded
        final JFXPanel fxPanel = new JFXPanel();
        frame.add(fxPanel);

        // Initialize the JavaFX components on a JavaFX thread
        Platform.runLater(new Runnable() {
            @Override
            public void run() {
                initFX(fxPanel);
            }
        });
    }


Next we can create the scene and some small components to let us interact with the OSGi server bundle we created in previous article.

    /**
     * Initialize the Java FX components
     *
     * @param fxPanel
     */
    private void initFX(final JFXPanel fxPanel) {
        final Scene scene = createScene();
        fxPanel.setScene(scene);
    }

    /**
     * Create specific UI components.
     *
     * @return the Scene including the components
     */
    private Scene createScene() {

        // Create the Table columns
        final TableColumn firstNameCol = new TableColumn("First Name");
        firstNameCol.setMinWidth(100);
        firstNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("firstname"));

        final TableColumn lastNameCol = new TableColumn("Last Name");
        lastNameCol.setMinWidth(100);
        lastNameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("surname"));

        final TableColumn emailCol = new TableColumn("Phone");
        emailCol.setMinWidth(200);
        emailCol.setCellValueFactory(new PropertyValueFactory<Person, String>("phoneNumber"));

        // Create the table with all the columns
        final TableView<Person> table = new TableView<Person>();
        table.setEditable(true);
        table.getColumns().addAll(firstNameCol, lastNameCol, emailCol);

        // Create the submit button
        final Button button = new Button("Find persons by lastname");

        // Create the input field
        final TextField textField = new TextField();
        textField.setPromptText("Lastname (try 'FooBar' or 'Bar')");

        // Create the event handler for the button and text field
        final EventHandler eventHandler = new EventHandler<ActionEvent>() {
            @Override
            public void handle(final ActionEvent actionEvent) {
                if (peopleFinder != null) {
                    final ObservableList people = FXCollections.observableArrayList(
                            peopleFinder.findPeopleBySurname(textField.getText()));
                    table.setItems(people);
                }
            }
        };
        button.setOnAction(eventHandler);
        textField.setOnAction(eventHandler);

        // Create an HBox panel with the input field and button
        final HBox pane = new HBox();
        pane.getChildren().add(button);
        pane.getChildren().add(textField);
        pane.setVisible(true);
        pane.setSpacing(10d);

        // Create a VBox panel with the input fields and the result table
        final VBox vbox = new VBox();
        vbox.getChildren().add(pane);
        vbox.setSpacing(10d);
        vbox.setPadding(new Insets(10,10,10,10));
        vbox.getChildren().add(table);

        // Create the root group
        final Group root = new Group();
        root.getChildren().add(vbox);

        // Create the scene which contains all components
        final Scene scene = new Scene(root, 500, 500);
        return scene;
    }


You can now compile and redeploy the OCGi client bundle to the OSGi server. This should give you the JavaFX screen immediately after the server is started.



1 comment: