Using injected resources from JavaFX Script in an app client

My recent posting about running a JavaFX script in an app client used only a simple U/I that did not interact with the back end.  I said at the time I would add an example that worked with back-end resources, and I have now done so.

Last spring I wrote a number guessing game demo example app for the
GlassFish JavaOne booth.  My main purpose was to illustrate the Java
Web Start
launch facility for app clients that is built in to
GlassFish.  With that in mind I built quite a rich Swing U/I that worked with back-end EJBs to help highlight why it can still sometimes make sense to build a Swing U/I deployed as an app
client when so much of the buzz around is about Web 2.0, Ajax, and so on. 

Just for some background about the number guessing game... The game client invokes an EJB method to get what the next "target" number should be and then runs the game with its user, timing the user the whole time.  Once the user guesses the correct value for that game the client invokes another EJB method to report the outcome of the game: the player's name, the target value that was used, and the total time it took the player to guess the value.  The server keeps statistics on each game reported.  The client, in response to a user button click, can also request from the EJB the last 100 results of all games played by any player for the "top scores" display on video games, laser tag, etc.  I am the first to admit that this is a somewhat artificial application for Java EE, but it served my purpose then...and now!

Here is a part of the Main class showing the convenience methods that are used from the original Swing U/I:

public class Main {
/\* the injected EJB reference \*/
static GameManagerRemote manager;

/\* reports the 100 most recent game outcomes \*/
public static ArrayList<Game> getAllGames() {
return manager.getAllGames();

/\* dispenses the target value for a new game \*/
public static GuessPrompt getNewGuessPrompt() {
return manager.getNewGuessPrompt();

/\* records a game's outcome \*/
public static void recordGame(String player, long duration, int target) {
manager.recordGame(player, duration, target);

Since I had that example around I decided to try converting its U/I to use JavaFX script.  I probably spent more time than I should have (much, much more - especially after hours!) crafting the JavaFX script so it would render a U/I similar to the original Swing-from-Java implementation in the JavaOne demo.  (I must confess I did find this process very interesting and instructive.  Apart from the new syntax there are some very powerful new concepts which I felt compelled to try!  A topic for a later blog.)

Here is the unpolished JavaFX U/I I came up with (relatively) quickly for this example
(red buttons represent the wrong guesses so far, disabled buttons represent choices known to be impossible given the guesses so far, and enabled buttons remain possible answers; 1 was my most recent guess):

JavaFX script number guess screen - game in progress


The key point I want to mention here is that it was nearly trivial to write the JavaFX script code needed to use the convenience methods in the Main class. Here is an excerpt of the JavaFX script as an example:

operation GameState.stop() {
inProgress = false;
totalTime = System.currentTimeMillis() - startTime;
timeSoFarLabel = "Your time";
Main.recordGame(player, totalTime, actualAnswer);

Another part of the script invokes this method when the player clicks on the button corresponding to the correct answer for that game. The last line is all that's required for the JavaFX script to report the game results to the back-end server!  This is so simple because the convenience method already existed in Main and because the newly-added technology preview GlassFish support for JavaFX script performs injection into the app client's main class even though GlassFish is running the "main script" instead of the main class. 

I cannot claim to have left the Main class completely untouched to make this all work.  I had to make one change and I chose to make one other change.

  1. The original Main class declared the convenience methods to be package-visible, not public.  Even though I placed the JavaFX script in the same package the JavaFX script engine reported that it could not find the methods.  Changing the convenience methods to public fixed that.  I think of this as a JavaFX script bug and as soon as my request for project membership in OpenJFX is accepted I'll file the issue.
  2. JavaFX script does not deal with generics - or at least I do not yet know how to make it deal with generics.  So I would have to have written a bunch of extra JavaFX script logic to deal with the ArrayList<Game> result from the Main.getAllGames method.  Instead, I just added a new convenience method getGames() that returned Game[] instead, which JavaFX finds much easier to deal with.

I came away from this experience with these key learnings:

  1. I am not nearly proficient enough with JavaFX script to create really nice U/Is.  (As I expected!)  I believe this is my problem and not JavaFX script's problem.
  2. I was very glad I had written convenience methods in the Main class to encapsulate the use of the injected resource.  That's a good design approach to begin with, and those methods were immediately reusable from JavaFX script as soon as I made them public.
  3. It looks as if the script needs to invoke System.exit when the user closes the main frame in order for the app client's JVM to exit.
  4. It works! (Also as I had expected.)


Post a Comment:
  • HTML Syntax: NOT allowed

News and musings on the technology I work on at Oracle.

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.


« July 2016