Skip to main content

Weather on SunTechDays

Posted by malenkov on April 6, 2009 at 3:00 AM PDT

The SunTechDays conference will take place in Saint-Petersburg this week. I'm going to participate in it and hold the "Developing Innovative Multimedia JavaFX Applications" seminar together with my colleague. I'll be talking about accessing the web services and deploying applications. My presentation will be based on the WeatherWidget sample that was slightly refactored. So, what kind of weather to expect on the conference?

Start desktop applications to track the weather forecasts in Tallinn, Saint-Petersburg, Prague, Menlo Park and Santa Clara.

Module 6: Accessing Web Services

Consider a task of accessing web services by using the WeatherForecast application that displays weather forecasts retrieved from Yahoo! Weather RSS feed. The RSS details are available here.

The HttpRequest's onInput variable is implemented to call the PullParser's parse function. The PullParser's onEvent function processes attributes of the elements with the yweather prefix. I would recommend to read the "JavaFX HTTP Networking and XML Parsing" article first.

6.1. HTTP Networking

The HttpRequest class provides an API to make asynchronous HTTP requests. It can also invoke RESTful Web Services. This class enables developers to specify the location and method variables, and to start an HTTP operation by invoking the enqueue function. Other variables of this class such as started, connecting, writing, reading and done change their state as the operation progresses. For operations that transfer large amounts of data, the percentage progress can be computed using the read, toread, written and towrite variables. Callback functions are also called to indicate changes in states as the operation progresses.

This class supports the following methods of the HTTP request: GET, POST, PUT and DELETE. The GET method passes arguments directly to the request string and is used by default:

http://weather.yahooapis.com/forecastrss?d=5&p=ENXX0004&u=c

When using the POST or PUT methods, the output variable should contain an OutputStream object that transfers data to the server. In addition, you may change the request header that contains the name-value pairs before the enqueue function is called. Use the setHeader function to set a value for the particular name. Some commonly-used header names and values are defined in the HttpHeaders class.

Once the enqueue function is called, a server response gets a response Node, a header and a content. The response code resides in the responseCode integer variable. Usually it is included into the responseMessage string variable. Commonly-used response Nodes are defined in the HttpStatus class.

Use the getResponseHeaderNames function to obtain a list of the HTTP header names set on the HTTP response. The values corresponding to the names can be retrieved through the getResponseHeaderValue function. A response content resides in the input variable in a form of an InputStream object. The onInput variable might be specified by a callback function that is invoked once to indicate that the request body is now available. The provided InputStream object must be closed when done reading in a finally block. Note that the HttpRequest object can be used for a single request only. If you need another request you have to create a new HttpRequest object.

      HttpRequest {
        location: url
        ...
        onInput: function(input) {
          try {
            // read data from input stream
            ...
          } finally {
            input.close()
          }
        }
      }.enqueue()

A request's HTTP operation can be canceled by calling the cancel function at any time after the enqueue function has been called. This removes the request from the queue if it is queued, or interrupts the request if it has started executing. Both of these cases cause the done variable to change its value to true.

6.2. Data Processing

The PulParser class provides a parser for structured data. The parser generates a sequence of Event objects as it processes the document. Its implementation is quite similar to SAXParser for XML. The PulParser supports only two formats: XML (default) and JSON. Use the documentType variable to specify the format.

The parser can be used in combination with the onEvent callback function. This function notifies the application on each pull event encountered in the input stream after the parse function has been called. Note that the PullParser object can be reused. Setting the input variable while parsing is in progress will reset the parser.

def parser = PullParser {
  ...
  onEvent: function(event) {
    if (event.type == PullParser.START_ELEMENT) {
      if (event.qname.prefix == "yweather") {
        if (event.qname.name == "location") {
          location = event.getAttributeValue("city");
          ...
        }
        ...
      }
    }
  }
}

Also the PullParser class can be used in linear mode where the application simply pulls events, discarding or skipping over those that are not processed. The current event is available in the event variable or through the onEvent callback function. For example,

parser.seek(QName{prefix:"yweather" name:"location"});
location = parser.event.getAttributeValue("city");

Advantages of the PullParser class:

  • The client gets data only when it explicitly requests it.
  • An application can use the seek function to skip over elements that are not assigned to be processed.
  • A single parser API for both XML and JSON documents.
  • A reusable streaming parser.

6.3. XML Supporting

There are two classes to work with XML: XMLConstants and QName. The XMLConstants class contains several useful constants. The QName class represents a qualified name as defined in the XML specification. It contains the following variables: namespace, name and prefix. The prefix is included in the QName class to retain lexical information that is presented in an XML input source. Note that the prefix is not used in the equals function or to compute the hash code.

Module 7: Deploying a JavaFX Application

There are three basic models of deploying JavaFX application: desktop, browser and mobile. Two first models are based on JavaSE and use the desktop profile of the JavaFX API. This profile enables developers to apply all the beneficial features including visual effects from the javafx.scene.effect package. The mobile model is based on the extended implementation of JavaME to compile applications. JavaFX SDK 1.1 supports only the common API for the mobile applications. Use the __PROFILE__ pseudo-variable to specify the deployment model for your application.

var applet = __PROFILE__ == "browser";
        // = FX.getProperty("javafx.runtime.isApplet") != null
var mobile = __PROFILE__ == "mobile";
        // = FX.getProperty("javafx.me.profiles") != null
var normal = __PROFILE__ == "desktop";
        // = not applet and not mobile

The current implementation uses the properties given in the commented lines.

7.1. Desktop: Standalone Application

This is the easiest model of deployment. All you need is to have a jar-file of your application and run it by using one of the following commands:

javafx -jar application.jar [arguments]
javafx -cp application.jar package.mainclass [arguments]

A disadvantage of this model is that you have to ensure required JavaSE and JavaFX versions are installed on your computer.

7.2. Desktop: Java WebStart by using JNLP

Java WebStart is a tool for deploying standalone applications on desktop by using the Java Network Launching Protocol. To deploy your application by using Java WebStart, create a jnlp-file or modify the file that has been generated automatically. Below is an example of the jnlp-file.

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+"
      codebase="http://my.server.com/path/to/app/"
      href="Weather.jnlp">
    <information>
        <title>Weather Widget</title>
        <vendor>Sergey Malenkov</vendor>
        <homepage href="http://weblogs.java.net/blog/malenkov/"/>
        <description>Yahoo! Weather Forecast</description>
        <offline-allowed/>
        <shortcut>
            <desktop/>
        </shortcut>
    </information>
    <resources>
        <j2se version="1.5+"/>
        <extension name="JavaFX Runtime"
                   href="http://dl.javafx.com/1.2/javafx-rt.jnlp"/>
        <jar href="Weather.jar" main="true"/>
    </resources>
    <application-desc main-class="Weather">
        <argument>code=RSXX0091&u=c</argument>
    </application-desc>
</jnlp>

The codebase attribute specifies the target location of jar- and jnlp-files. You may delete the offline-allowed tag, because our application should have access to on-line web services. Arguments are passed to the application by using the argument tags.

7.3. Browser: Java Plug-in with Applet

Java Plug-in is a tool for deploying Java applets that run inside a web browser. Java 6 update 10 has a new Java Plug-in architecture that unifies deployment between Java WebStart and the Java Plug-in.

JavaFX provides a wrapper class that enables executing JavaFX applications as applets. To deploy an application as an applet, create a jnlp-file or modify the file that has been generated automatically. The file name should end with _browser.jnlp. Below is an example of the jnlp-file to run your application in a browser.

<?xml version="1.0" encoding="UTF-8"?>
<jnlp spec="1.0+"
      codebase="http://my.server.com/path/to/app/"
      href="Weather_browser.jnlp">
    <information>
        <title>Weather Widget</title>
        <vendor>Sergey Malenkov</vendor>
        <homepage href="http://weblogs.java.net/blog/malenkov/"/>
        <description>Yahoo! Weather Forecast</description>
        <offline-allowed/>
        <shortcut>
            <desktop/>
        </shortcut>
    </information>
    <resources>
        <j2se version="1.5+"/>
        <extension name="JavaFX Runtime"
                   href="http://dl.javafx.com/1.2/javafx-rt.jnlp"/>
        <jar href="Weather.jar" main="true"/>
    </resources>
    <applet-desc name="Weather"
                 main-class="com.sun.javafx.runtime.adapter.Applet"
                 width="200"
                 height="200">
        <param name="MainJavaFXScript" value="Weather"/>
    </applet-desc>
</jnlp>

Whenever your JavaFX application is launched by using the Java WebStart technology, the JNLP client checks for updates. You can improve the startup time of your JavaFX application if you enable update checks to be performed in the background. Add the following line to the jnlp-file:

    <update check="background"/>

Use JavaScript to call the javafx function and embed your application into a web page:

<script src="http://dl.javafx.com/1.1/dtfx.js"></script>
<script>
javafx(
    {
          draggable: true,
          archive: "Weather.jar",
          width: 200,
          height: 200,
          code: "Weather",
          name: "Weather"
    },
    {
          code: "RSXX0091&u=c"
    }
);
</script>

Note, the second parameter is used to pass arguments to the application. You may omit it, if not needed. Set the draggable parameter to true to enable dragging the applet to desktop.

Once the application is launched, press the ALT key and drag the application from the browser. This is the default behavior that can be changed by using the AppletStageExtension.

Stage {
  ...
  extensions: AppletStageExtension {
    useDefaultClose: false
    shouldDragStart: function(event) {
      return applet and event.primaryButtonDown
    }
    onDragStarted: function() {
      applet = false
    }
    onAppletRestored: function() {
      applet = true
    }
  }
}

In the preceding code the applet is draggable only when it is located in the browser window. It is implemented by using the local variable named applet. The onDragStarted and onAppletRestored functions change the state of the applet variable when the applet is being dragged from the browser and to the browser correspondingly. Note that the preceding code fragment makes the dragged applet looking like a desktop application, however, the AppletStageExtension class resides only in the desktop API, therefore, the application can't be compiled for the mobile environment.

7.4. Mobile: JavaFX Mobile Emulator

JavaFX Mobile Emulator is provided with the JavaFX 1.1 SDK. It displays the application as it would look on a typical mobile device. JavaFX 1.1 Mobile Emulator supports the common API only. Also it limits the size of a jar-file. Modify the descriptor as follows to pass arguments to your application:

MIDlet-Name: Weather
MIDlet-Version: 1.0
MIDlet-Vendor: Sergey Malenkov
MicroEdition-Profile: JAVAFX-1.1
MicroEdition-Configuration: CLDC-1.1
MIDlet-1: Weather,,Weather_MIDlet
MIDlet-Jar-URL: Weather.jar
MIDlet-Jar-Size: 79095
id: RSXX0091&u=c

Note, the same descriptor is used to pass arguments to the application. For example, the last line specifies the string value for the id argument. Use the following command line to run the application on the Mobile Emulator:

emulator -Xdescriptor:Weather.jad

7.5. Arguments Parsing

From the previous sections you've learned how to pass arguments to your application. Now consider parsing arguments in the application. It worth mentioning that the browser and mobile deploying models pass arguments as key-values. Use the following function to retrieve the value by using the key. This function returns null, if there is no value set for this key.

var code = FX.getArgument("code");

You can use two methods to retrieve arguments when using the desktop model. First, you can define a global function named run:

function run(args: String[]) {
  var code: Object;
  for (arg in args) {
    code = if (indexof arg > 0)
      then "{code} {arg}"
      else arg
  }
  println('code: {code}');
}

Second, you can use the following utility function:

def args = FX.getArguments();

There is no big difference between two those approaches. When using the first one, all variables defined within the run function are local. For the second approach, all variables are global. Use the FX.getArgument function to receive a value by using a key, similar to the implementation in browser, mobile models.

7.6. JavaFX Packager

JavaFX Packager utility creates an application in a format that is specific to a target profile, either desktop or mobile. Its documentation is included in the JavaFX 1.1 SDK, however, is not available on web. The NetBeans IDE incorporates this utility and makes it available to users when they choose an execution model. Use the following command to run the JavaFX Packager utility:

javafxpackager -src D:\projects\Weather\sources -appClass Weather

Consider the basic options you can use to run this application:

-src | -sourcepath <source root1>[;<source root2> ...]
Specifies the root directory of the source tree holding the application to be packaged. At least one source root is required.
-cp | -classpath | -librarypath <library1>[;<library2> ...]
Specifies the classpath.
-res | -resourcepath <resource root1> [;<resource root2> ...]
Specifies the resource path.
-d | -destination <target directory>
Specifies target directory for application files. The default target directory is ./dist.
-workDir <working directory>
Specifies the folder for temporary files. If the folder is not specified all temporary files will be removed.
-appClass <application class>
Specifies the main class of the application.
-appName <application name>
Specifies the application name that is the application class name by default.
-appVendor <application vendor>
Specifies the application vendor that is the user.name Java system property by default.
-appVersion <application version>
Specifies the application version that is 1.0 by default.
-appWidth <applet width>
Specifies the applet width that is 200 pixels by default. It is used to generate html-file and jnlp-file for browser.
-appHeight <applet height>
Specifies the applet height that is 200 pixels by default. It is used to generate html-file and jnlp-file for browser.
-appCodebase <application code base URL>
Specifies the codebase for the application. It is used to generate jnlp-files. The default codebase is a local path to generated jnlp-files.
-p | -profile [ desktop | mobile ]
Specifies the JavaFX platform. JavaFX 1.1 SDK supports desktop and mobile profiles. The default profile is desktop. Use the mobile profile to generate files needed by the JavaFX Mobile Emulator. Internally this option specifies the configuration file that sets environment variables.
-draggable
Makes the application draggable from the web browser. Adds draggable: true into generated html-file.
-pack200
Compresses an application using Pack200 to a file with jar.pack.gz extension. It is used to generate jnlp-files.
-sign
Builds a signed application. If it is not specified, a self-signture is not created.
-keystore <keystore for signing>
Specifies keystore for signed application. A temporary self-signature is created when keystore is not specified.
-keystorePassword <password>
Specifies the password when signing with specific keystore. It is mandatory in such case.
-keyalias <alias>
Specifies the keyalias when signing with specific keystore. It is mandatory in such case.
-keyaliasPassword <password>
Specifies the password for specific keyalias. If it is not specified the keystore password is used instead.
-paramFile <property file>
Specifies the named parameters for the application that are stored in the specified property file. It is used to generate jad-file, html-file and jnlp-file for desktop.

original post

Related Topics >>