Fork me on GitHub

Application Starter

How to create your first start class.

Application

JRebirth Application Framework offers a custom class that extends the basic javafx.Application class. Its aim is to automatically start the JRebirth underlying Application Framework without doing complex stuff.

Short UML Diagram:

Application Class Diagram

To trigger the start-up of your JavaFX application you must add a static void main method in order to call one of the static protected method provided :

100
110
121
130
140
protected static void preloadAndLaunch(final String... args) {
protected static void preloadAndLaunch(final Class<? extends Preloader> preloaderClass, final String... args) {
protected static void preloadAndLaunch(final Class<? extends Application> appClass, final Class<? extends Preloader> preloaderClass, final String... args) {
protected static void launchNow(final String... args) {
protected static void launchNow(final Class<? extends Application> appClass, final String... args) {

In example, SampleApplication will be launched with default JRebirth preloader (Application and Preloader classes are omitted) like below:

30
31
32
public static void main(final String... args) {
    preloadAndLaunch(args);
}

If you want to use the JRebirthPreloader, you must include the JRebirth preloader artifact to your pom.xml file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
<?xml version="1.0" encoding="UTF-8"?>
    <modelVersion>4.0.0</modelVersion>
  
    <parent>
        <groupId>org.jrebirth</groupId>
        <artifactId>af</artifactId>
        <version>8.5.0</version>
        <relativePath>..</relativePath>
    </parent>
  
    <groupId>org.jrebirth.af</groupId>
    <artifactId>sample</artifactId>
    <packaging>jar</packaging>
  
    <name>Sample Application</name>
    <url>http://www.sample.org</url>
    <description>Built with JRebirth Framework</description>
  
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  
        <storepass>storepass</storepass>
        <storetype>JKS</storetype>
        <keyalias>keyalias</keyalias>
        <keypass>keypass</keypass>
  
        <deletekeystore>true</deletekeystore>
        <genkeystore>true</genkeystore>
  
        <permissions>all-permissions</permissions> <!-- or sandbox -->
        <deployUrl>http://apps.jrebirth.org</deployUrl>
        <deployPath>${project.artifactId}/${project.version}</deployPath>
        <codebase>${deployUrl}/${deployPath}</codebase>
  
        <jnlpFilename>sample.jnlp</jnlpFilename>
        <preloaderClass>org.jrebirth.af.preloader.JRebirthPreloader</preloaderClass>
        <appClass>org.jrebirth.af.sample.SampleApplication</appClass>
  
        <appletWidth>1024</appletWidth>
        <appletHeight>768</appletHeight>
  
        <updateCheck>background</updateCheck>
        <updatePolicy>prompt-update</updatePolicy>
  
    </properties>
  
    <organization>
        <name>JRebirth</name>
        <url>http://www.jrebirth.org</url>
    </organization>
  
    <build>
  
        <resources>
            <resource>
                <filtering>false</filtering>
                <directory>${basedir}/src/main/java</directory>
                <includes>
                    <include>**/*.fxml</include>
                    <include>**/*.properties</include>
                    <include>**/*.txt</include>
                </includes>
            </resource>
            <resource>
                <filtering>true</filtering>
                <directory>${basedir}/src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
                <excludes>
                    <exclude>**/*.ttf</exclude>
                </excludes>
            </resource>
            <resource>
                <filtering>false</filtering>
                <directory>${basedir}/src/main/resources</directory>
                <includes>
                    <include>**/*.ttf</include>
                </includes>
            </resource>
        </resources>
  
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
  
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>2.4</version>
  
                    <configuration>
                        <archive>
                            <manifestEntries>
                                <JavaFX-Version>2.0</JavaFX-Version>
                                <Main-Class>${appClass}</Main-Class>
                                <JavaFX-Application-Class>${appClass}</JavaFX-Application-Class>
                            </manifestEntries>
  
                            <manifest>
                                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
                                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
                                <addClasspath>true</addClasspath>
                            </manifest>
                        </archive>
                    </configuration>
                </plugin>
  
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>webstart-maven-plugin</artifactId>
                    <version>1.0-beta-6</version>
  
                    <dependencies>
                        <dependency>
                            <groupId>org.codehaus.mojo</groupId>
                            <artifactId>keytool-api-1.7</artifactId>
                            <version>1.4</version>
                        </dependency>
                        <dependency>
                            <groupId>org.codehaus.mojo</groupId>
                            <artifactId>webstart-pack200-impl</artifactId>
                            <version>1.0-beta-6</version>
                        </dependency>
                    </dependencies>
  
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>jnlp-inline</goal>
                            </goals>
                        </execution>
                    </executions>
  
                    <configuration>
  
                        <filenameMapping>full</filenameMapping>
  
                        <updateManifestEntries>
                            <Application-Name>${project.name}</Application-Name>
                            <Trusted-Library>true</Trusted-Library>
                            <Permissions>${permissions}</Permissions>
                            <Codebase>${codebase}</Codebase>
                            <Trusted-Only>true</Trusted-Only>
                        </updateManifestEntries>
  
  
                        <jnlpFiles>${jrebirth.jnlp.filename}</jnlpFiles>
                        <excludeTransitive>false</excludeTransitive>
  
                        <libPath>lib</libPath>
                        <codebase>${codebase}</codebase>
  
                        <jnlp>
                            <outputFile>${jnlpFilename}</outputFile>
                            <mainClass>${appClass}</mainClass>
                            <offlineAllowed>true</offlineAllowed>
                            <allPermissions>true</allPermissions>
                        </jnlp>
  
                        <sign>
                            <keystore>${keystore}</keystore>
                            <keypass>${keypass}</keypass>
                            <storepass>${storepass}</storepass>
                            <storetype>${storetype}</storetype>
  
                            <alias>${keyalias}</alias>
  
                            <validity>360</validity>
                            <dnameCn>JRebirth Self-signed Certificate</dnameCn>
                            <dnameOu>JRebirth OSS</dnameOu>
                            <dnameO>JRebirth</dnameO>
                            <dnameL>Toulouse</dnameL>
                            <dnameSt>Haute-Garonne</dnameSt>
                            <dnameC>FR</dnameC>
                            <verify>true</verify>
  
                            <keystoreConfig>
                                <delete>${deletekeystore}</delete>
                                <gen>${genkeystore}</gen>
                            </keystoreConfig>
  
                        </sign>
  
                        <unsignAlreadySignedJars>true</unsignAlreadySignedJars>
  
                        <pack200>
                            <enabled>true</enabled>
                        </pack200>
                        <gzip>true</gzip>
  
                        <outputJarVersions>false</outputJarVersions>
  
                        <install>false</install>
                        <verbose>true</verbose>
                    </configuration>
  
                </plugin>
  
  
                <!-- Project Quality -->
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>sonar-maven-plugin</artifactId>
                    <version>2.5</version>
                </plugin>
  
            </plugins>
        </pluginManagement>
  
        <extensions>
            <extension>
                <groupId>org.apache.maven.wagon</groupId>
                <artifactId>wagon-ftp</artifactId>
                <version>2.2</version>
            </extension>
        </extensions>
    </build>
  
    <dependencies>
        <!-- Use logback logger -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.0.13</version>
        </dependency>
  
        <dependency>
            <groupId>org.jrebirth.af</groupId>
            <artifactId>core</artifactId>
            <version>8.5.0</version>
        </dependency>
  
        <dependency>
            <groupId>org.jrebirth.af</groupId>
            <artifactId>preloader</artifactId>
            <version>8.5.0</version>
        </dependency>
    </dependencies>
  
</project>

You can create your own Preloader class, JRebirth send only ProgressNotification with two kind of values:

Hereafter you have the default JRebirthPreloader implementation.

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
public class JRebirthPreloader extends AbstractJRebirthPreloader {
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected Scene createPreloaderScene() {
  
        final StackPane p = new StackPane();
  
        final ImageView logo = new ImageView(new Image("JRebirth_Title.png"));
        p.getChildren().add(logo);
        StackPane.setAlignment(logo, Pos.CENTER);
  
        this.progressBar = new ProgressBar(0.0);
        this.progressBar.setPrefSize(460, 20);
        p.getChildren().add(this.progressBar);
        StackPane.setAlignment(this.progressBar, Pos.BOTTOM_CENTER);
        StackPane.setMargin(this.progressBar, new Insets(30));
  
        this.messageText = new Text("Loading");
        p.getChildren().add(this.messageText);
        StackPane.setAlignment(this.messageText, Pos.BOTTOM_CENTER);
        StackPane.setMargin(this.messageText, new Insets(10));
  
        return new Scene(p, 600, 200, Color.TRANSPARENT);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void hideStage() {
        final Stage stage = this.preloaderStage;
  
        final ScaleTransition st = new ScaleTransition();
        st.setFromX(1.0);
        st.setToX(0.0);
        st.setDuration(Duration.millis(400));
        st.setNode(stage.getScene().getRoot());
        st.setOnFinished(actionEvent -> stage.hide());
        st.play();
    }
  
}

You can extends the AbstractJRebirthPreloader to just have to populate your scene within createPreloaderScene method, and to add a nice animation used to hide the preloader stage within hideStage method.

To define your own Application class you have 2 options:

The AbstractApplication class will do all extra stuff required to launch JRebirth engine. You don’t have to bother about it.

This class skeleton provides some hooks to allow customization of the application start up.

Please note that one method is mandatory ! You must define a first Model Class to load the first Model that will initialize the first Node attached to the RootNode (automagically created) of your Scene.

If you have used the Maven archetype org.jrebirth.archetype you have obtained this source code (otherwise that you can copy-paste it):

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public final class SampleApplication extends DefaultApplication<StackPane> {
  
    /**
     * Application launcher.
     *
     * @param args the command line arguments
     */
    public static void main(final String... args) {
        preloadAndLaunch(args);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    public Class<? extends Model> firstModelClass() {
        return SampleModel.class;
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void customizeStage(final Stage stage) {
        stage.setFullScreen(false);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected void customizeScene(final Scene scene) {
        addCSS(scene, SampleStyles.MAIN);
    }
  
    /**
     * {@inheritDoc}
     */
    @Override
    protected List<? extends ResourceItem<?, ?, ?>> getResourceToPreload() {
        return Arrays.asList(new FontItem[] {
                SampleFonts.SPLASH,
        });
    }
  
}

The SampleModel.class is shown and explained into the Ui page.

AbstractApplication & DefaultApplication classes are using a generic type that represents the first JavaFX node hold by the scene. This node will be instantiated automatically by the framework and could be accessed by calling the protected method getRootNode(). You must define it in the class definition as generic type, this type must extend Pane class.

The method Class<? extends Model> firstModelClass() is mandatory to define which UI model will be attached to the root node of the scene.

This first model will be created into the JRebirth Thread Pool (JTP), then it will be attached to the root node into the JavaFX Application Thread (JAT).

The method String applicationTitle() is simply used to define the name of the application displayed by the stage (OS window) and used by OS task bar.

By default it will retrieve values from properties file (default is jrebirth.properties): - applicationName={} powered by JRebirth ({} is replaced by application class simple name) - applicationVersion=1.0

The first stage object is provided by the JavaFX launcher, the method void customizeStage(final Stage stage) allows doing some stage customizations.

The scene object automatically attached to the default stage stage is built by the protected method Scene buildScene() that could be overridden as needed. By defaut it creates a default scene with these attributes :

Theses properties are customizable with a properties file, this is explained below into the Configuration section.

Another method let you customize the scene : - void customizeSscene(final Scene scene).

For example you can listen some key binding to perform a global action. The Presentation application uses it to listen <Ctrl> + <+> and <Ctrl> + <-> key combo to zoom in/out the whole scene.

This method is also useful to attach a stylesheet to the scene like this : - scene.getStylesheets().add(loadCSS(“style/sample.css”));

XXX TODO review with @Preload

Custom case for fonts: JavaFX applications are able to use fonts through programmatic declarations or with CSS declaration (in .css files. or inline).

If font used by CSS are not provided by the platform running the application, it must be provided and loaded by the application.

JRebirth provides a simple way to embed and declare font: this mechanism is explained in the custom topic: Managing Fonts.

The method List<ResourceEnum> getResourceToPreload() is used to preload resources to allow them to be used earlier (ie font in CSS declaration). They are loaded at boot in the same time than stylesheets.

The JRebirth Application class allow running Waves before and after the creation of the first model class.

A Wave is a JRebirth Event that could be process by any JRebirth components, they are carrying throught JRebirth classes and threads.

You can add your own wave with the two following methods :

The waves returnes will be processed sequentially althought they could be processed by different threads.

In this method you are allowed to call visible methods from the javafx.application.Application class, in example the getParameter() will give you the arguments passed to command line.

Don’t forget that you can chain your waves if you need to do more than one thing.

JRebirth Analyzer example :

The AbstractApplication class is using two defaults hotkey:

These methods could be overridden if you want to change them, you can avoid these shortcut by returning null.

JRebirth creates its own uncaught exception handlers in order to log exceptions that were not caught by application code.

It’s possible to customize them by overriding methods listed hereafter:

The Initialization phase is composed by:

Customizable steps are handled by 2 methods to override:

203
204
205
206
/**
 * Perform custom task before application initialization phase.
 */
protected abstract void preInit();

You can use notifyPreloader(new ProgressNotification(PROGRESS)) method where PROGRESS value is between 0.11 and 0.29 in order to update finely the progression. You can also display custom message (understandable by your preloader) by calling _notifyPreloader(new ProgressNotification(MESSAGEID)) where MESSAGE_ID is 200 or 300.

208
209
210
211
/**
 * Perform custom task after application initialization phase and before starting phase.
 */
protected abstract void postInit();

You can use notifyPreloader(new ProgressNotification(PROGRESS)) method where PROGRESS value is between 0.71 and 0.89 in order to update finely the progression. You can also display custom message (understandable by your preloader) by calling _notifyPreloader(new ProgressNotification(MESSAGEID)) where MESSAGE_ID is 800 or 900.

The Start phase will build and attach the scene object. Then it will start the JRebirth Thread (JIT) and show the stage.

Manage Configuration

JRebirth provides a configuration engine that allow to parse configuration files and inject values into application.

Your application class can use the dedicated @Configuration annotation. The AbstractApplication class use a default one:

73
@Configuration(".*jrebirth")

Hereafter you have a full annotation usage:

14
@Configuration(value = ".*-jrebirth", extension = "properties", schedule = 60)

This annotation has 3 properties: - value - extension - schedule

Define the wildcard used to find configuration files.

The format is the same as Regex Pattern (ie: .-jrebirth => for abc-jrebirth.EXTENSION files, abc matches the . regex part)

The default value is empty, no search will be done.

Define the file extension to find configuration files.

The extension must not included the first dot (ie: properties => for abc-jrebirth.properties files)

The default value is “properties” to load properties files

Define the delay used to check if the file has changed in order to reload configuration files.

This value is in seconds.

The default value is 0, no check will be done (this feature is still under development)

It’s possible to avoid configuration mechanism, for example if you want to use your own and don’t need another process at start-up.

You can disable it by setting an empty @Configuration annotation.

14
@Configuration

Manage Localization

JRebirth provides a Internationalization engine that allow to localize internal resources and also your resources. It parses properties files and inject values into application.

Your application class can use the dedicated @Localized annotation. The AbstractApplication class use a default one:

74
@Localized(".*_rb")

This annotation has 2 properties: - value - schedule

Define the wildcard used to find configuration files.

The format is the same as Regex Pattern (ie: ._rb => for MyMessages_rb.properties files, MyMessages matches the . regex part).

The default value is empty, no search will be done.

Define the delay used to check if the file has changed in order to reload properties files.

This value is in seconds.

The default value is 0, no check will be done (this feature is still under development)

It’s possible to avoid localization mechanism, for example if you want to improve performance by avoiding any translation into logs.

You can disable it by setting an empty @Localized annotation.

15
@Localized