Pet Catalog : JavaFX Example Application


Pet Catalog Photos : JavaFX Example Application

Pet Catalog Photos is a simple JavaFX application that displays pet photos retrieved from a RESTful Pet Catalog app (implemented using JAX-RS) described in an earlier blog entry and in this screencast.  This JavaFX example is a modification of the Interesting Photos : JavaFX Example Application.

catalogclient.jpg


Understanding the Code


Code Sample from: main.fx

// Application User Interface
def stage = Stage {
    title: "Pet Catalog"
    resizable: false
    x: bind stageX with inverse
    y: bind stageY with inverse
    width: stageWidth
    height: stageHeight
    visible: true
    style: StageStyle.TRANSPARENT
    scene: Scene {
        content: Group {
            content: bind stageContent
            clip: Rectangle {
                width: stageWidth
                height: stageHeight
                arcWidth: 20
                arcHeight: 20
            }
        }
        fill: Color.TRANSPARENT
    }
}


Stage is the  top level container window required to display any visible JavaFX objects. The variables title, width and height define the the text that appears on the window's top border and its height and width.
The scene variable defines an instance of the Scene object literal, which sets the area in which you can place the JavaFX objects.
Scene is a drawing surface for graphical content and a container that holds the scene graph nodes. It can be added to Stage, and JavaFX renders everything on a scene.
The scene instance variable has a content variable that is used to hold JavaFX graphical elements and defines the graphical content of the application. Here the content consists of a Group Node . The Group Node has a  content variable which is a sequence of child Nodes that will be rendered in order whenever this Group is rendered. The Group Node has a clip variable which specifies a Rectangle which defines the the clipping shape for this Group Node.

stage5_1_1_1.JPG

The Group Node content variable has a data binding  with stageContent.  Data binding allows creating a direct and immediate relationship between two variables or between a variable and the outcome of a function or an expression.   stageContent  is a sequence of Nodes as shown in the code below.  Node - is an element in a scene graph. The following visual objects are examples of javafx.scene.Node implementations: javafx.scene.image.ImageView, javafx.scene.media.Mediaview, javafx.ext.swing.\*, javafx.scene.shape.\*, and javafx.scene.text.Text. These are leaf nodes, which cannot have a child element.

Code Sample from: main.fx

// Application User Interface
var stageContent: Node[];

stageContent = [
        bgImage, titleBar, nextButton, backButton, closeButton,
        titleText, statusText, thumbImageViewGroup, fullImageView
];

stage4.gif
A grid of ImageView is added to Scene of Stage.

The thumbImageViewGroup variable has a data binding  with thumbImageViews which is a sequence of ThumbImageView which extends ImageView. ImageView is a Node used for painting images loaded with Image class.  The code below shows how the  thumbImageViews sequence is initialized to a  grid of  3 columns and 3 rows of  ThumbImageView .

Code Sample from:  main.fx

var thumbImageViews: ThumbImageView[]; // Thumbnail images
for(col in [0..2]) {
    for(row in [0..2]) {
        def thumbImageView = ThumbImageView {
            x: thumbBaseX + (col \* (thumbSize + thumbSpace))
            y: thumbBaseY + (row \* (thumbSize + thumbSpace))
            fitWidth: thumbSize
            fitHeight: thumbSize
        }
        insert thumbImageView into thumbImageViews;
    }
}
var thumbImageViewGroup = Group {
    content: bind thumbImageViews
}


calling the RESTful  Pet Catalog Web Service 


Information and URLs for pet photos is obtained by performing an HTTP GET request on a RESTful Catalog service by using the JavaFX asynchronous HTTP API (javafx.io.http.HttpRequest).  HttpRequest allows one to specify a location and method and start a HTTP operation with the function enqueue().  The content of the HTTP response can be accessed in the onInput callback function. onInput calls the PhotoPullParser.parse function to parse the XML.The onDone Callback is invoked when the request has finished execution, onDone calls the updateImages() function, explained later, which updates the images displayed in the Photo-Grid. 


Code Sample from: main.fx

function loadImageMetadata() {
    var start=page \* 9;  
    var request: HttpRequest = HttpRequest {

        location: "http://localhost:8080/catalog/resources/items/?start={start}&max=9"
        method: HttpRequest.GET

        onInput: function(input: java.io.InputStream) {                            
             var parser = PhotoPullParser{};
             photos = parser.parse(input);    
        }

        onDone: function() {
             updateImages();
        }
    }
    request.enqueue();
}


The response XML document contains a list of information about available photos. The document contains the following information about each photo:

  • id
  • imagethumburl
  • imageurl
  • server
  • name
  • price
  • productid
  • description
Here is example XML returned from the RESTFul Catalog Service:

 <items uri="http://localhost:8080/catalog/resources/items/">
       <item uri="http://localhost:8080/catalog/resources/items/1/">
           <description>This black and white colored cat is super friendly./description>
           <id>1</id>
           <imagethumburl>http://localhost:8080/catalog/images/anthony-s.jpg</imagethumburl>
           <imageurl>http://localhost:8080/catalog/images/anthony.jpg</imageurl>
           <name>Friendly Cat</name>
           <price>307.10</price>
           <productid>feline01</productid>
       </item>
   ...
   </items>


The response document is parsed by using the JavaFX XML pull parser (javafx.data.pull.PullParser) to extract information about the photos.
The parser supplies a sequence of Events as it process the document under application control. XML and JSON are the two data formats currently supported.  The PullParser can be used with the onEvent callback, which reports the current parse event.  The onEvent callback shown below parses the xml item elements into an instance of the Photo class. When the item END_ELEMENT , </item>, is reached,  the photo
variable is inserted into the photos sequence.

Code Sample from: PhotoPullParser.fx
import javafx.data.pull.PullParser;

public class PhotoPullParser {

  public function parse(input: InputStream): Photo[] {

    // Information about all catalog photos
    var photos: Photo[];
    var photo: Photo;

    // Parse the input data (Photo Metadata) and construct Photo instance
    def parser = PullParser {

        input: input

        onEvent: function(event: Event) {
            if (event.type == PullParser.START_ELEMENT) {
                if(event.qname.name == "item" and event.level == 1) {
                    photo = Photo { };
                }
            } else
            if (event.type == PullParser.END_ELEMENT) {
                if(event.qname.name == "item" and event.level == 1) {
                   insert photo into photos;
                } else
                if(event.qname.name == "id" and event.level == 2) {
                    photo.id = event.text;
                } else
                if(event.qname.name == "name" and event.level == 2) {
                    photo.name = event.text;
                } else
                if(event.qname.name == "imagethumburl" and event.level == 2) {
                    photo.imagethumburl = event.text;
                } else
                if(event.qname.name == "imageurl" and event.level == 2) {
                    photo.imageurl = event.text;
                } else
                if(event.qname.name == "description" and event.level == 2) {
                    photo.description = event.text;
                } else
                if(event.qname.name == "productid" and event.level == 2) {
                    photo.productid = event.text;
                }
            }
        }
    }
    parser.parse();
    return photos;
  }
}

A sequence of photo objects is constructed by parsing the response XML.

Code Sample from: Photo.fx

public class Photo {
    public var id: String;
    public var imagethumburl: String;
    public var imageurl: String;
    public var name: String;
    public var price: String;
    public var description: String;
    public var productid: String;
}



The onDone Callback is invoked when the HttpRequest has finished execution, onDone calls the updateImages() function, shown below.


Code Sample from:  main.fx

// Load image and data specified in given Photo object
function loadImage(photo: Photo, thumbImageView: ThumbImageView): Void {
    thumbImageView.image = Image {
        url: "{photo.imagethumburl}";
        width: thumbSize
        height: thumbSize
        backgroundLoading: true
        placeholder: thumbImageView.image
    };
    thumbImageView.photo = photo;

// Update images displayed in Photo-Grid
function updateImages() {
    for(i in [0..8]) {
        var photoIndex = i;
        loadImage(photos[photoIndex], thumbImageViews[i]);
    }
}

updateImages updates the images displayed in the thumbImageViewGroup by  updating the thumbImageView Image URLs to the photo URLs parsed from the HTTP response. The photos are then fetched and displayed in Stage as a grid. The imagethumburl , imageurl are used to retrieve and then display photos. The ImageView class knows how to retrieve the image using the URL and display it. Once the photo is retrieved, it is displayed in Scene of Stage through the use of data binding.


If the user clicks a thumb photo, the normal photo is loaded and shown.

catalogclientlarge.jpg


When the user clicks a thumb photo, the ThumbImageView onMouseClicked function sets the fullImageView url to the clicked photo's larger image url and sets the fullImageView show variable to true.


Code Sample from:  main.fx

// Initialize fullscreen ImageView
var fullImageView = FullImageView {
    translateX: thumbBaseX
    translateY: thumbBaseY
    visible: false
}

// To display thumb image
class ThumbImageView extends ImageView {

    public override var onMouseClicked = function(e:MouseEvent) {   
      // Load larger image
      fullImageView.image = Image {
         url: "{photo.imageurl}"
         placeholder: image
         backgroundLoading: true
       };
    }  
    fullImageView.show = true;
}

When the fullImageView show variable is set to true, The FullImageView Node's  visible variable is set to true which specifies that this Node and any subnodes should be rendered as part of the scene graph. The  fader() function causes the fullImageView to fade in or out. The FullImageView Node's  opacity variable specifies how opaque (that is, solid) the Node appears. Animation occurs along a timeline, represented by a javafx.animation.Timeline object. Each timeline contains one or more key frames, represented by javafx.animation.KeyFrame objects. The value of the time instance variable, 3s, defines the elapsed time at which the values within the key frame will be set in a single cycle of the Timeline object. The play() method plays the timeline as defined.


Code Sample from:  FullImageView.fx

public class FullImageView extends ImageView {

    public var show = false on replace {
        if(useEffects) {
            fader();
        } else {
visible = show;
        }
    }
    var timeline:Timeline = Timeline {
        rate: bind timelineRate with inverse
        keyFrames: [
            KeyFrame {
                time: 3s
                values: [ opacity => 1.0 tween Interpolator.LINEAR ]
            }
        ]
    };
    function fader() {    
        if(show) {
            timeline.time = 0s;
            timelineRate = 1.0;
opacity = 0.0;
visible = true;
        } else {
            timeline.time = 3s;
            timelineRate = -3.0;
opacity = 1.0;
        }   
        timeline.play();
    }


stage4.gif

Clicking the normal photo again restores the thumb photo grid. The user can navigate to the next or previous set of photos by clicking << and >> arrow buttons.  Clicking the nextButton calls the onNext() function which increments the page number and calls loadImageMetadata();

Code Sample from:  main.fx

// Display next set of photos
var nextButton = ImageButton {
    selectImage: Image {
        url: "{__DIR__}images/next_h.png"
    };        
    onMouseClicked: function(e) {
onNext();
    }
}

// Load image and data specified in given Photo object
function onNext() {
    fullImageView.show = false;
    page++;
    //updateImages();
    loadImageMetadata();
}


Running the code

Netbeans 6.5 has plugins for JavaFX.  If  you don't have Netbeans 6.5  download and install it, then  go to Tools..Plugins and select the JavaFX plugins.  You can also download the JavaFX SDK without Netbeans, but I'm going to focus on using JavaFX with Netbeans.
To start learning JavaFX  you can start off with the doc Creating Your First JavaFX Application and/or with the tutorial  Learning the JavaFX Script Programming Language.

Running the RESTful Catalog service

  1. Download the sample code and extract its contents. You should now see the newly extracted directory as <sample_install_dir>/catalog, where <sample_install_dir> is the directory where you unzipped the sample package. For example, if you extracted the contents to C:\\ on a Windows machine, then your newly created directory should be at C:\\catalog.
  2. Start NetBeans IDE. Click Open Project in the File menu and select the catalog directory you just unzipped.
  3. Start the MySQL or JavaDB database as follows:
    • Click the Services tab in the NetBeans IDE.
    • Expand the databases node. You should see the MySQL server or JavaDB database in the list of databases.
      databaselist.jpg
    • Right-mouse click on the MySQL or JavaDB server database and select Start.
  4. Create the catalog database as follows:
    • Right-mouse click on the MySQL or JavaDB server database and select Create Database.
    • Enter the database name petcatalog and userid root and password admin. This will open a New Database Connection window. Click O.K. to accept the displayed settings.
  5. Create the tables in the MySQL catalog database as follows:
    • Expand the Drivers node. You should see a driver for the petcatalog database in the list of drivers.
      driverlist.jpg
    • Right-mouse click on the petcatalog driver and select Connect.
    • Right-mouse click on the petcatalog driver and select Execute Command. This will open up a SQL command window.
    • Copy the contents of the catalog.sql file in the catalog directory and paste the contents into the SQL command window.
    • Click the Run SQL icon runsql.jpg (Ctrl+Shift+E) above the SQL command window.
    • Open the  catalog/setup/sun-resources.xml file and verify that the property values it specifies match those of the database you created (jndi datasource, username, password...). For example
      <resources>
        <jdbc-resource  jndi-name="catalog" />
          <property name="User" value="root"/>
          <property name="Password" value="admin"/>
          <property name="serverName" value="localhost"/>
          <property name="portNumber" value="3306"/>
          <property name="databaseName" value="petcatalog"/>
          <property name="URL" value="jdbc:mysql://localhost:3306/petcatalog"/>
      </resources> 
      Edit the property values as necessary to match the database you created .
  6. Build the project as follows:

    • Right click the catalog node in the Projects window.
    • Select Clean and Build Project.

  7. Run the project as follows:

    • Right click the catalog node in the Projects window.
    • Select Run Project.
When you run the project, your browser should display and empty html page at http://localhost:8080/catalog/  (this is the service not the client).

Running the JavaFX Pet Catalog Client

  1. Download the sample code and extract its contents. You should now see the newly extracted directory as <sample_install_dir>/catalog, where <sample_install_dir> is the directory where you unzipped the sample package. For example, if you extracted the contents to C:\\ on a Windows machine, then your newly created directory should be at C:\\catalogclient.
  2. In NetBeans IDE, click Open Project in the File menu and select the catalogclient directory you just unzipped.
  3. Run the project as follows:
    • Right click the catalogclient  node in the Projects window.
    • Select Run Project.
When you run the project, your browser should display the Pet Catalog Client.

References



Comments:

Nice explanation ! thanks.

Posted by Vaibhav Choudhary on February 08, 2009 at 03:20 PM EST #

You rock, Carol! Very nice example!

Thanks,
Jim Weaver

Posted by Jim Weaver on February 09, 2009 at 02:27 AM EST #

Carol,

Maybe I'm missing it or it's not in the code you show, but I don't see that you close the input stream after you are done parsing it. According to the docs, this is recommended "to allow the HttpRequest implementation to clean up resources related to this request as promptly as possible."

Is the close really needed?

Dean

Posted by Dean Iverson on February 09, 2009 at 04:38 AM EST #

yes you should close the input stream, I shortened the code in places to make it more concise for getting the main point across. To see the full code download the source.

Posted by Carol McDonald on February 09, 2009 at 05:12 AM EST #

This is a great job keep it up

Posted by Christopher Mwaka on February 27, 2009 at 02:03 AM EST #

Thanks.Its good to see code of working apps - this helps with adoption and students. Keep it up

Posted by javanut on February 27, 2009 at 11:07 PM EST #

This is good example for me to build apps bussiness with javafx, thanks for give me more information about it. GO GO javafx.

Posted by ferryhs on August 14, 2009 at 05:29 PM EDT #

Great example demo, with the potential to be expanded by users.

I ran the demo with JavaDB, so I made some modifications to the jdbc-connection-pool in the sun-resources.xml file:

datasource-classname="org.apache.derby.jdbc.ClientDataSource"

<property name="driverClass" value="org.apache.derby.jdbc.ClientDriver"/>

Also, since I'm running JavaFX 1.2, I made these changes to Main.fx:

request.enqueue() => request.start()
focusable => focusTraversable

Posted by David Watson on October 06, 2009 at 11:58 AM EDT #

Hi,

I need advice. Recently, I deployed a .war file with a web hosting company, it turns out that it cannot find the mysql table that requires to run the application. The application is written with netbeans 6.5, it requires mysql table and glassfish to run.

It shows error message after I uploaded the .war file to the web host server. It says "The application will not run properly. Please fix your application and redeploy. WARNING: com.sun.enterprise.deployment.backend.IASDeploymentException: java.lang.RuntimeException: javax.naming.NameNotFoundException: pocketss not found com.sun.enterprise.deployment.backend.IASDeploymentException: java.lang.RuntimeException: javax.naming.NameNotFoundException: pocketss not found" In this context, pocketss is the mysql table that I have created. How do I go about solving this? The web hosting company mentioned about 'making jdbc connections' and it requires .jar file. I thought the .war file should contain all the .jar file and if not where to find in the netbeans deploy folder?

Thank you.

Posted by sandra on December 16, 2009 at 03:45 PM EST #

goody

Posted by guest on January 03, 2011 at 06:29 PM EST #

Post a Comment:
Comments are closed for this entry.
About

caroljmcdonald

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
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
   
       
Today