Chapter 37

Bringing JavaFX to the Mix

JavaFX was designed to serve as a tool for creating rich Internet applications (RIAs). It is meant to compete with such RIA tools as Adobe Flex and Microsoft Silverlight. The competing technologies use two languages for creating RIAs — one for declarative definition of the UI components, and another for the coding of processing logic. Adobe Flex uses MXML for UI components and ActionScript for processing logic. Microsoft Silverlight uses XAML for UI components and one of the .NET languages (such as C#) for processing.

JavaFX was originally designed to use JavaFX Script for UI components and Java for processing logic. But recently things changed, and up to the last minute I wasn’t sure if this lesson should be included in the book. Oracle has announced that JavaFX will be undergoing a major redesign in 2011. JavaFX 1.3 includes the JavaFX Script language, but JavaFX 2.0 will be completely Java-based.

Currently, no pre-release version of JavaFX 2.0 is available, and there is a chance that whatever you read about the client portion of this lesson’s application will be outdated. But no matter what changes in JavaFX 2.0, the deployed application will run in JVM with the installed JavaFX run time. There is also hope that the Java community will continue supporting the open-source version of JavaFX Script.

This lesson is not an introduction to JavaFX programming, but rather an overview of an application that uses multiple technologies you are already familiar with, plus a client created with JavaFX 1.3.

Consuming Stock Quotes with JavaFX

In this lesson you’ll see how a web client written in JavaFX can subscribe to price quotes published from the server side via messaging. Multiple technologies used in this version of the Stock Server example are depicted in Figure 37-1.

These are the players that provide the generation and processing of the price quote feed:

1. The singleton session bean PriceProvider (see the source code in Listing 37-1) knows how to generate random price quotes. It gets instantiated on startup of the GlassFish application server.

2. A stateless session bean called MessageService (Listing 37-2) gets the references to JMS-administered objects via resource injection and publishes the price quote to the JMS topic known as jms/StockPrices, which is mapped to the physical topic Prices in the Open MQ messaging server.

3. The Java client subscribes to the topic Prices. Listing 37-3 shows the Java classes Messages and JMSListener, and the interface MessageHandler. These client classes do not have any presentation logic.

4. The presentation client Main.fx (Listing 37-4) is written in JavaFX Script. It wraps the Java client messaging classes and gets the price quotes from them. As soon as the new quote comes in, the old one is pushed up and off the screen with a special animation effect.

5. To illustrate another way of sending the message, this application has a simple HTTP client (Listing 37-5) that allows the user to enter any text, which is then routed via the servlet SendMessage (Listing 37-6) to the MessageService EJB. The rest of the data flow is as described in Steps 2 through 4.

Code Walkthrough

By now you should be familiar with each of the Java technologies used in this application, except JavaFX, so I’ll provide only very short explanations for the code listings.

The PriceProvider singleton bean shown in Listing 37-1 serves as an emulator of the data feed with stock quotes. Replace the random-number generator with a real-time Wall Street stock market feed, and this sample application will show the real data. I’d like to point out two annotations: @Startup and @Schedule. The first ensures that the Java EE application server automatically instantiates the PriceProvider bean whenever the server starts. The second is a calendar-based timer that will call the method getPriceQuote() every five seconds.

download.eps

Listing 37-1: PriceProvider singleton bean

@Singleton
@Startup
public class PriceProvider {
 
    @EJB
    private MessageService messageService;
 
    // In this example the method getQuotes() simply generates
    // prices, but in the real world it will get the quote
    // from some external data feed
 
    @Schedule(second = "*/5", minute="*", hour="*")
    public void getPriceQuote() {
        
        String price = Double.toString(generator.nextDouble() * 100.0);
        String symbol = symbols[generator.nextInt(symbols.length)];
 
        messageService.sendMessage(symbol + " " + price);
    }
 
    private static final Random generator = new Random();
 
    private static final String[] symbols = 
                    {"AAPL", "MSFT", "YHOO", "AMZN", "MOT"};
}

The stateless session bean MessageService publishes the text message given as an argument to sendMessage() to the JMS topic that has been bound to the JNDI tree of GlassFish under the name jms/StockPrices. Both the topic connection factory and the topic objects are injected into the bean by the EJB container. The JMS topic will be mapped to the physical topic Prices of the MOM provider Open MQ.

download.eps

Listing 37-2: Stateless bean MessageService

@Stateless
public class MessageService {
 
    @Resource(mappedName = "jms/StockPrices")
    private Topic topic;
 
    @Resource(mappedName = "TopicConnectionFactory")
    private TopicConnectionFactory topicConnectionFactory;
 
    public void sendMessage(String text) {
        try {
            Connection connection = topicConnectionFactory.createConnection();
            Session session = connection.createSession(true, 0);
            MessageProducer producer = session.createProducer(topic);
 
            TextMessage textMessage = session.createTextMessage(text);
            producer.send(textMessage);
 
            connection.close();
        }
        catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }
}

The web clients of this application are written in Java, JavaFX Script, and HTML. The Java class Messages is a part of the JavaFX applet that directly subscribes to the Open MQ topic Prices. Originally, I wanted the applet to use a JNDI call to GlassFish to get the topic jms/StockPrices from there. This would be a cleaner way of subscribing to the topic — no dependency on the particular MOM provider, and no client code changes needed should we decide to change the MOM provider or the application server.

Unfortunately GlassFish v3 doesn’t offer a lightweight means of adding JNDI-related classes to the applet. It comes with the utility appclient, which simply packages 40 MB worth of Java classes into the one jar. Sending such a heavy extra weight over the network with the applet is not right, and that’s why I decided to use a lighter solution: the applet’s class Messages (Listing 37-3) doesn’t perform JNDI lookup but directly connects to Open MQ. The Java class Messages uses an inner class called JMSListener and the interface MessageHandler.

download.eps

Listing 37-3: The Java client subscriber

public class Messages {
 
    public static void startListening(MessageHandler messageHandler) 
                                                     throws Exception {
       JMSListener jmsListener = new JMSListener(messageHandler);
 
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setProperty(ConnectionConfiguration.imqAddressList,
                                                      "mq://localhost:7676");
 
        connection = connectionFactory.createQueueConnection("admin","admin");
 
        Session session = connection.createSession(false, 
                                                  Session.AUTO_ACKNOWLEDGE);
 
        Topic topic = session.createTopic("Prices");
        MessageConsumer consumer = session.createConsumer(topic);
 
        consumer.setMessageListener(jmsListener);
        connection.start();
   }
 
    public static void stopListening() {
        try {
            connection.stop();
        }
        catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }
 
    private static Connection connection;
 
    private static class JMSListener implements MessageListener {
        private MessageHandler messageHandler;
 
        private JMSListener(MessageHandler messageHandler) {
            this.messageHandler = messageHandler;
        }
 
        @Override
        public void onMessage(Message message) {
            
            TextMessage textMessage = (TextMessage) message;
 
            try {
                System.out.println("Got message: " + textMessage.getText());
 
                messageHandler.handle(textMessage.getText());
            }
            catch (JMSException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
 
public interface MessageHandler {
    void handle(String message);
}

Reading JavaFX Code

The JavaFX client shown in Listing 37-4 is responsible for the UI. It’s deployed as an applet and is played in the client by the Java plug-in. Starting from Java 6 Update 10, this plug-in runs not as a part of the web browser, but as an independent JVM.

This applet is deployed on the server and is described in a special text file with the extension .jnlp (see Listing 37-7). The client’s machine has to have the JavaFX run time installed to play this applet. Because you have the latest Java SE installed on your computer, you won’t have any problem playing JavaFX applets. But users with older versions of Java are not guaranteed to have the same results. This is one of the reasons JavaFX has difficulties competing with Adobe Flex or Microsoft Silverlight in a non-corporate uncontrolled environment.

JavaFX Script can access the Java API and in this sample the JavaFX code communicates with the preceding Java message listener. JavaFX Script files are compiled into Java classes and then run on JVM. JavaFX developers need to have JavaFX SDK installed in addition to the run time.

The code in Listing 37-4 shows you the syntax of JavaFX Script. Variable declarations start with the keyword var, and specifying the data type is optional — the compiler will try to figure out what type to use based on assigned values.

The keyword bind represents binding — the value of the variable on the right is assigned to the variable on the left, and all the changes to the right-side values will be immediately reflected in the variable on the left.

Functions in JavaFX are similar to methods in Java, but they don’t have to be defined inside a class and can just appear anywhere in the script. Functions can be defined dynamically, which makes them much like Java anonymous inner classes, but simpler. To call a function asynchronously, much as you would call Java’s invokeLater(), just give this function FX.deferAction() as an argument, where FX is a class with system utilities. This is done at the beginning of Listing 37-4.

The instance of JavaFXMessageHandler is given to the class Messages (Listing 37-3), and every time the new message is published to the topic the addNewText() method is invoked.

download.eps

Listing 37-4: The JavaFX client Main.fx

class JavaFXMessageHandler extends MessageHandler {
    override public function handle(message:String):Void {
        FX.deferAction( function() { addNewText(message) } );
    }
}
 
Messages.startListening(new JavaFXMessageHandler());
 
var oldText = "";
var newText = "";
 
var oldTextOpacity = 0.0;
var oldTextYShift = -40.0;
 
var font = Font {
    name: "Verdana"
    size: 16
}
 
var oldTextLabel:Label = Label {
    font: font
    text: bind oldText
    textFill: Color.web("#808020")
    translateY: bind oldTextYShift
    opacity: bind oldTextOpacity
}
 
var newTextOpacity = 0.0;
var newTextYShift = 40.0;
 
var newTextLabel:Label = Label {
    textAlignment: TextAlignment.CENTER
    font: font
    textFill: Color.web("#808020")
    text: bind newText
    translateY: bind newTextYShift
    opacity: bind newTextOpacity;
}
 
var oldTextAnimation = Timeline {
    repeatCount: 1
    keyFrames: [
        at (0s) {
            oldTextOpacity => 1.0 tween Interpolator.LINEAR;
            oldTextYShift => 0.0 tween Interpolator.LINEAR;
        }
 
        at (1s) {
            oldTextOpacity => 0.0 tween Interpolator.LINEAR;
            oldTextYShift => -40.0 tween Interpolator.LINEAR;
        }
    ];
}
 
var newTextAnimation = Timeline {
    repeatCount: 1
    keyFrames: [
        at (0s) {
            newTextOpacity => 0.0 tween Interpolator.LINEAR;
            newTextYShift => 40.0 tween Interpolator.LINEAR;
        }
 
        at (.5s) {
            newTextOpacity => 1.0 tween Interpolator.LINEAR;
            newTextYShift => 0.0 tween Interpolator.LINEAR;
        }
    ];
}
 
function addNewText(text: String): Void {
    oldText = newText;
    newText = text;
 
    oldTextAnimation.playFromStart();
    newTextAnimation.playFromStart();
}
 
Stage {
    title: "Application title"
 
    scene: Scene {
        width: 600
        height: 100
 
        content: VBox {
            content: [
                Stack {
                    nodeHPos: HPos.CENTER
                    content: [
                        Rectangle {
                            width: 600
                            height: 80
                            opacity: 0.3
                            fill: LinearGradient {
                                startX: 0, startY: 0.0, endX: 0, endY: 80.0
                                proportional: false
                                stops: [
                                    Stop { offset: 0.0 color: 
                                                   Color.web("#303030") }
                                    Stop { offset: 1.0 color: 
                                                   Color.web("#FFFFFF") }
                                ]
                            }
                        },
                        oldTextLabel,
                        newTextLabel ]
                }]
        }
    }
 
    onClose : function() { Messages.stopListening(); }
}

The price quotes are displayed on a gray gradient background (see Figure 37-2) and each new price quote pushes the previous one from the screen. Both old and new message texts have animation effects (oldTextAnimation and newTextAnimation) that make the view more engaging.

The JavaFX UI components are not created to live inside a specific top-level container. You add a group of components to a scene, which is added to the stage. At run time the stage will be linked to a top-level container according to the platform on which the application has to work. A mobile device and a desktop computer will use different top-level containers to display your stage. Each of the components that you add to the scene is an instance of a Node class.

The content of a Scene is listed in its content property. The code in Listing 37-4 creates the Stage object and adds an instance of a Scene, which contains a VBox — a container with a vertical layout. Inside the VBox are three nodes: Rectangle with LinearGradient (a color effect changing from dark to light gray) and two Label nodes: oldTextLabel and newTextLabel.

Another interesting JavaFX component is Timeline — think of a reel of film in a projector. Spooling through frames rapidly produces the illusion of movement. If you need to repeat some actions in JavaFX you can create a Timeline with keyFrames that will occur at specific intervals.

The last line in the code of the Stage instance handles the onClose event. This event instructs the Messages class to stop listening to the price quotes.

The HTML Client

To provide another door to the messaging system you can use an HTML client that can send any text you want. This may be useful for managing the application. For example, an administrator may want to arrange it so that as soon as the client application receives the word STOP as a text message, it will stop listening to price quotes.

The simple HTML form in Listing 37-5 has just one input field, and when the user presses the Enter key the text is posted to the server by the action send.

download.eps

Listing 37-5: Index.html — the HTML client

<html>
<head>
    <title>JMS Demo</title>
</head>
<body>
    Click this link to open <a href="prices.html">
                   JavaFX client consuming price quotes</a>
    <p>Enter any text in the input field below and watch how JavaFx 
client will receive it</p>
    <form action="send" method="POST">
        <label title="Text to send">
            <input value="" name="text"/>
        </label>
    </form>
</body>
</html>

The Java EE application server will route all HTTP requests with the URL pattern send to the servlet SendMessage from Listing 37-6, which will get the text entered by the user and invoke sendMessage() on the session bean MessageService. This text will be published to the topic jms/StockPrices as will any other messages that originate from the singleton PriceProvider.

download.eps

Listing 37-6: SendMessage servlet

@WebServlet("/send")
public class SendMessage extends HttpServlet {
 
 @EJB
 private MessageService messageService;
 
 protected void doPost(HttpServletRequest request, 
HttpServletResponse response) throws IOException, ServletException {
  String text = request.getParameter("text");
  messageService.sendMessage(text);
 
  response.sendRedirect("index.html");
 }
}

When this JavaFX applet is ready for deployment on the server, you need to add resources that are needed on the user’s machine. Our Main.fx client from Listing 37-4 uses the JMS API to subscribe to the messages from the Open MQ MOM provider. Therefore the classes implementing the JMS API have to be downloaded to the user’s computer along with Main.fx.

The JNLP file shown in Listing 37-7 has to be deployed on the server. It includes the resources section that lists what has to be downloaded to the client: the run time for JavaFX and the javafx-ui.jar file that includes our sample code and all related JMS supporting classes. The <applet-desc> tag includes the main-class parameter with a fully qualified name of the JavaFX applet itself with the name of the main class.

download.eps

Listing 37-7: JNLP deployment file

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+">
    <information>
        <title>jms-send-receive</title>
        <vendor>practicaljava</vendor>
        <homepage href=""/>
        <description>jms send and receive</description>
        <offline-allowed/>
    </information>
    <resources>
        <j2se version="1.5+"/>
        <extension name="JavaFX Runtime"
 href="http://dl.javafx.com/1.3.1/javafx-rt.jnlp"/>
 
        <jar href="javafx-ui.jar" main="true"/>
    </resources>
    <applet-desc name="javafx-ui" 
    main-class="com.sun.javafx.runtime.adapter.Applet" 
width="200" height="80">
        <param name="MainJavaFXScript" value="com.practicaljava.lesson37.ui.Main"/>
    </applet-desc>
    <update check="always"/>
</jnlp>

The hyperlink in the main HTML page (Listing 37-5) downloads the HTML file Prices.html (Listing 37-8), which includes a special JavaScript function called javafx (its source code is available at http://dl.javafx.com/1.3/dtfx.js). This function is a part of the JavaFX deployment toolkit. It ensures that the client’s computer has the right version of JRE and automatically generates the reference to the JNLP file shown in Listing 37-7.

download.eps

Listing 37-8: Prices.html

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
    <head>
        <title>Prices</title>
    </head>
    <body>
        <p>Real time price updates</p>
 
        <script src="http://dl.javafx.com/1.3/dtfx.js" 
type="text/javascript" language="JavaScript"></script>
        <script type="text/javascript" language="JavaScript">
            javafx(
            {
                archive: "javafx/javafx-ui.jar",
                draggable: true,
                width: 600,
                height: 80,
                code: "com.practicaljava.lesson37.ui.Main",
                name: "Messages"
            }
        );
       A </script>
    </body>
</html>

Try It

This is the final hands-on exercise in this tutorial. The goal is to build the application and make it work on your computer. This time you’ll be building and deploying the application and starting the servers from the command window without any help from Eclipse IDE.

Lesson Requirements

You should have Java and GlassFish v3 installed. You’ll need to install Mercurial version control software to get the code for this example from the Google source control repository where all code samples used in this book are stored. You’ll also need to install the build tool Maven.

note.ai

You can download the code and resources for this Try It from the book’s web page at www.wrox.com. You can find them in the Lesson37 folder in the download.

Hint

After this application is deployed from the command line, download the JavaFX plug-in for Eclipse IDE from http://download.oracle.com/javafx/1.2/gettingstarted/eclipse-plugin and try to create, deploy, and test this project inside Eclipse.

Step-by-Step

1. Download and install the distributed source control management tool Mercurial from http://mercurial.selenic.com. In the Terminal window enter hg –version and you should see the version of Mercurial installed.

2. Check out the code samples for the book into a folder called java24hourtrainer from http://code.google.com/p/practicaljava. You can do this by entering the following command in the Terminal (command) window:

hg clone https://practicaljava.googlecode.com/hg/ java24hourtrainier 

3. Download and install JavaFX 1.3 SDK, available at http://javafx.com/downloads/all.jsp?bundle=javafx_sdk. In Windows, JavaFX SDK will be installed in the default location c:Program FilesJavaFX, and in Mac OS in /Library/Frameworks/JavaFX.framework.

4. Check to see if you have the Maven build tool already installed on your computer by entering mvn –v at the command prompt. If you don’t have it, download and install it from http://maven.apache.org/download.html. All you have to do is unzip the downloaded file into a directory and add Maven’s bin directory to the system PATH variable. In Windows you do this via Control Panel, and on Mac OS by entering the following command in the Terminal window (enter the exact name of your maven directory):

export PATH=~/development/apache-maven-3.0/bin/:$PATH

You’ll be using the Maven build scripts that were created specifically for this lesson. Maven is out of the scope of this tutorial, but if you are curious to see where all the build logic is located, open up the three files called pom.xml in subdirectories lesson37, webdemo, and javafx-ui under java24hourtrainer.

5. To build the project for this lesson, in the Terminal or command window go to java24hourtrainer/java-ee/lesson37 and run mvn install.

6. As a part of Maven’s install, it’ll create a directory called.m2. Create a plain text file in this directory, named settings.xml, with the following content:

    <settings>
      <pluginGroups>
         <pluginGroup>com.practicaljava.maven.plugin</pluginGroup>
      </pluginGroups>
    </settings> 

7. Change to the directory java24hourtrainer/maven-plugins and run mvn install.

In several seconds Maven will have built two plug-ins — one for GlassFish and another for JavaFX.

8. Now go to the /java-ee/lesson37/web-demo directory and run the script to create a new domain in GlassFish; start it and deploy the web application there. In this example you are not going to run the instance of GlassFish that you’ve been using in previous lessons.

           mvn gf:create-domain
           mvn gf:start-domain
           mvn gf:deploy

9. After the instance of GlassFish is running start Open MQ on port 7676 and its admin console, as described in the section “Administering Objects of Open MQ” in Lesson 30. You need to create a topic called Prices there.

10. Now start the admin console of GlassFish by entering the following URL in your web browser: http://localhost:5000/. Create there a TopicConnectionFactory and a topic jms/StockPrices mapped to Prices. This process was described in Lesson 31.

11. Finally, test this application from end to end. Make sure that the Open MQ and GlassFish servers are up and running, and point your web browser to http://localhost:8080/javafx-web-demo/. First you’ll see a scary-looking message that an applet from dl.javafx.com is requesting access to your computer. Accept it and you’ll see a simple web page with a hyperlink and an input field, as per Listing 37-5. Click the hyperlink and you should see price quotes being pushed to your JavaFX client every five seconds, as shown in Figure 37-2.

12. Test the second way of publishing messages through the HTTP request to the servlet. To do this, open another web browser page and direct it to the same URL: http://localhost:8080/javafx-web-demo/. Arrange both browsers on your computer monitor so you can see them at the same time. This time do not click the link, but type any text in the input field and hit the Enter key on the keyboard. The text you entered should be received and displayed by the JavaFX client in the second web browser.

cd.ai

Please select Lesson 37 on the DVD with the print book, or watch online at www.wrox.com/go/fainjava to view the video that accompanies this lesson.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.149.74