Thursday Dec 11, 2008

TOTD # 59: Alternative JSF implementations on GlassFish - MyFaces and Tomahawk


GlassFish comes bundled with an industry grade implementation of Java Server Faces codenamed Mojarra. It is the most complete, up-to-date and well-tested JSF implementation and used pretty extensively. GlassFish v2 and v3 Prelude ships with JSF 1.2.x-compliant implementation that is defined as part of Java EE 5. GlassFish v3 trunk contains JSF 2.0-compliant implementation that is getting defined as part of Java EE 6. The latest version of Mojarra can be installed from the Update Center.

But GlassFish does not restrict you to Mojarra and instead it embraces other JSF implementations easily. This blog uses MyFaces, an alternate JavaServer Faces implementation from Apache, to demonstrate that. If you are interested in a brief summary of what it takes to use these alternate implementations on GlassFish then scroll to the end of this entry.

MyFaces also provides several component libraries such as Tomahawk, Trinidad, and Tobago for building web applications. This blog shows how MyFaces Tomahawk samples can be deployed on GlassFish v2 and v3. The basic integration hooks between GlassFish and other JSF implementations remain the same and are independent of the component library.

Lets get started and understand all the glory!

Download MyFaces Tomahawk samples from here. The download consists of 4 WAR files and they are unzipeed in the current directory. So create a new directory and then unzip the bundle if you want to organize it slightly better. The getting Started instructions require you to copy "simple.war" where as the actual WAR filename is "myfaces-example-simple-1.1.8.war". Anyway, we are going to deploy this sample on GlassFish v2, v3 Prelude, and v3 trunk and what needs to be done to use the MyFaces implementation bundled within the WAR.
  1. Deploy using Mojarra on GlassFish v2
    1. Deploying "myfaces-example-simple-1.1.8.war" on GlassFish v2 using "asadmin deploy myfaces-example-simple-1.1.8.war" and it shows the following message:

      [#|2008-12-05T11:00:43.710-0800|INFO|sun-appserver9.1|javax.enterprise.system.tools.deployment|_ThreadID=22;_ThreadName=Thread-43;|
      deployed with moduleid = myfaces-example-simple-1.1.8|#]

      [#|2008-12-05T11:00:44.296-0800|INFO|sun-appserver9.1|javax.enterprise.resource.webcontainer.jsf.config|_ThreadID=21;
      _ThreadName=httpWorkerThread-4848-1;/myfaces-example-simple-1.1.8;|Initializing Sun's JavaServer Faces implementation (1.2_04-b20-p03) for context '/myfaces-example-simple-1.1.8'|#]

      The bold text indicates that Mojarra 1.2 bundled with GlassFish v2 is used as the JSF runtime, the exact version is shown in the bold text. The deployed web application is accessible at "http://localhost:8080/myfaces-example-simple-1.1.8/home.jsf" and looks like:



      The following error message is shown as you click through the samples:

      [#|2008-12-03T16:27:43.935-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=24;_ThreadName=httpSSLWorkerThread-8080-1;|
      2008-12-03 16:27:43,935 [httpSSLWorkerThread-8080-1] INFO  org.apache.myfaces.shared_tomahawk.config.MyfacesConfig - Starting up Tomahawk on the MyFaces-JSF-Implementation
      |#]

      [#|2008-12-03T16:27:43.935-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=24;_ThreadName=httpSSLWorkerThread-8080-1;|
      2008-12-03 16:27:43,935 [httpSSLWorkerThread-8080-1] ERROR org.apache.myfaces.shared_tomahawk.config.MyfacesConfig - Both MyFaces and the RI are on your classpath. Please make sure to use only one of the two JSF-implementations.

      This error message is generated by MyFaces and may be ignored. I think the message should be a WARNING instead of an ERROR.
  2. Deploy using MyFaces on GlassFish v2
    1. Create a directory "simple" and unjar "myfaces-example-simple-1.1.8.war" in there.
    2. Add "WEB-INF/sun-web.xml" and specify the contents as:

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-1.dtd">
      <sun-web-app>
      <class-loader delegate="false"/>
      <property name="useMyFaces" value="true"/>
      </sun-web-app>

      The key point to notice is the property name "useMyFaces". The name is slightly mis-leading because the switch essentially tells GlassFish runtime to use the bundled Java Server Faces runtime and is not restricted to MyFaces only. This is fixed in GlassFish v3 and is discussed later.
    3. Create a WAR file in "simple" directory as "jar cvf myfaces-simple-v2.war \*".
    4. Deploy the generated WAR on GlassFish v2 as "asadmin deploy myfaces-simple-v2.war". It shows the following message:

      . . .

      [#|2008-12-05T11:11:25.615-0800|INFO|sun-appserver9.1|javax.enterprise.system.tools.deployment|_ThreadID=24;_ThreadName=Thread-257;|
      deployed with moduleid = myfaces-simple-v2|#]

      [#|2008-12-05T11:11:26.266-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|
      2008-12-05 11:11:26,266 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Reading standard config org/apache/myfaces/resource/standard-faces-config.xml
      |#]

      [#|2008-12-05T11:11:26.290-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|
      2008-12-05 11:11:26,290 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Reading config jar:file:/Users/arungupta/tools/glassfish/v2/ur2/glassfish/domains/domain1/applications/j2ee-modules/myfaces-simple-v2/WEB-INF/lib/tomahawk-1.1.8.jar!/
      META-INF/faces-config.xml
      |#]

      [#|2008-12-05T11:11:26.309-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:26,308 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Reading config /WEB-INF/examples-config.xml
      |#]

      [#|2008-12-05T11:11:26.337-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:26,337 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Reading config /WEB-INF/testSuite-config.xml
      |#]

      [#|2008-12-05T11:11:26.349-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:26,349 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Starting up MyFaces-package : myfaces-api in version : 1.1.6 from path : file:/Users/arungupta/tools/glassfish/v2/ur2/glassfish/domains/domain1/applications/j2ee-modules/myfaces-simple-v2/WEB-INF/lib/myfaces-api-1.1.6.jar
      |#]

      [#|2008-12-05T11:11:26.350-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:26,350 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Starting up MyFaces-package : myfaces-impl in version : 1.1.6 from path : file:/Users/arungupta/tools/glassfish/v2/ur2/glassfish/domains/domain1/applications/j2ee-modules/myfaces-simple-v2/WEB-INF/lib/myfaces-impl-1.1.6.jar
      |#]

      [#|2008-12-05T11:11:26.350-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:26,350 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - MyFaces-package : tomahawk-sandbox not found.
      |#]

      [#|2008-12-05T11:11:26.350-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:26,350 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Starting up MyFaces-package : tomahawk in version : 1.1.8 from path : file:/Users/arungupta/tools/glassfish/v2/ur2/glassfish/domains/domain1/applications/j2ee-modules/myfaces-simple-v2/WEB-INF/lib/tomahawk-1.1.8.jar
      |#]

      . . .

      [#|2008-12-05T11:11:27.069-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:27,069 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.shared_impl.config.MyfacesConfig - Starting up Tomahawk on the RI-JSF-Implementation.
      |#]

      [#|2008-12-05T11:11:27.069-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:27,069 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.shared_impl.config.MyfacesConfig - Starting up Tomahawk on the MyFaces-JSF-Implementation
      |#]

      [#|2008-12-05T11:11:27.070-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:27,070 [httpWorkerThread-4848-1] ERROR org.apache.myfaces.shared_impl.config.MyfacesConfig - Both MyFaces and the RI are on your classpath. Please make sure to use only one of the two JSF-implementations.
      |#]

      [#|2008-12-05T11:11:27.070-0800|INFO|sun-appserver9.1|javax.enterprise.system.stream.out|_ThreadID=21;_ThreadName=httpWorkerThread-4848-1;|2008-12-05 11:11:27,070 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.webapp.StartupServletContextListener - ServletContext '/Users/arungupta/tools/glassfish/v2/ur2/glassfish/domains/domain1/applications/j2ee-modules/myfaces-simple-v2/' initialized.
      |#]

      The first thing to note is that the message "Initializing Sun's JavaServer Faces implementation (1.2_04-b20-p03)" is not shown. That indicates Mojarra is not used as the JSF runtime. Then you can see how MyFaces 1.1.6 API and Implementation are loaded, Tomahaw 1.1.8 is loaded and finally started using MyFaces JSF implementation.

      The main page from this deployed application at "http://localhost:8080/myfaces-simple-v2" is very similar as shown below:

  3. Deploy using Mojarra on GlassFish v3 Prelude - This is very similar experience as with GlassFish v2.
    1. Deploying "myfaces-example-simple-1.1.8.war" on GlassFish v3 shows the following message:

      Dec 5, 2008 11:27:17 AM com.sun.faces.config.ConfigureListener contextInitialized
      INFO: Initializing Mojarra (1.2_10-b01-FCS) for context '/myfaces-example-simple-1.1.8'
      Dec 5, 2008 11:27:18 AM com.sun.enterprise.web.WebApplication start
      INFO: Loading application myfaces-example-simple-1.1.8 at /myfaces-example-simple-1.1.8
      Dec 5, 2008 11:27:18 AM org.glassfish.deployment.admin.DeployCommand execute
      INFO: Deployment of myfaces-example-simple-1.1.8 done is 3470 ms

      The bold text clearly indicates that Mojarra 1.2 bundled, exact version shown in the bold text, with GlassFish v3 is used as the JSF runtime. The deployed web application is accessible at "http://localhost:8080/myfaces-example-simple-1.1.8/home.jsf". Viewing the sample in browser shows:

      Dec 5, 2008 11:54:54 AM  
      INFO: 2008-12-05 11:54:54,083 [httpWorkerThread-8080-0] INFO  org.apache.myfaces.shared_tomahawk.config.MyfacesConfig - Starting up Tomahawk on the RI-JSF-Implementation.

      Dec 5, 2008 11:54:54 AM  
      INFO: 2008-12-05 11:54:54,083 [httpWorkerThread-8080-0] INFO  org.apache.myfaces.shared_tomahawk.config.MyfacesConfig - Starting up Tomahawk on the MyFaces-JSF-Implementation

      Dec 5, 2008 11:54:54 AM  
      INFO: 2008-12-05 11:54:54,083 [httpWorkerThread-8080-0] ERROR org.apache.myfaces.shared_tomahawk.config.MyfacesConfig - Both MyFaces and the RI are on your classpath. Please make sure to use only one of the two JSF-implementations.

      and may be ignored. None of the messages that indicate classloading, as shown for GlassFish v2 above, are displayed. I suspect these are standard messages displayed by MyFaces without taking into consideration that an alternate JSF runtime can be used to run these samples.
  4. Deploy using MyFaces on GlassFish v3 Prelude - There is no way to override JSF implementation with GlassFish v3 Prelude. So even though deploying "myfaces-simple-v2.war" will work fine but viewing the web page at "http://localhost:8080/myfaces-simple-v2/" will show the following exception:



    Basically, a web application cannot use MyFaces implementation on GlassFish v3 Prelude. However Mojarra provides a fully-compliant and feature-rich JSF implementation and is already baked in GlassFish v3 Prelude.
  5. Deploy using Mojarra on GlassFish v3 Trunk
    1. Pick your GlassFish v3 Build Flavor, this blog uses the trunk.
    2. Deploy the original sample as "asadmin deploy myfaces-example-simple-1.1.8.war" and the following log messages are shown:

      Dec 5, 2008 12:35:17 PM com.sun.faces.config.ConfigureListener contextInitialized
      INFO: Initializing Mojarra 2.0.0 (SNAPSHOT b05) for context '/myfaces-example-simple-1.1.8'
      Dec 5, 2008 12:35:18 PM org.apache.catalina.core.ApplicationContext log
      SEVERE: WebModule[/myfaces-example-simple-1.1.8]PWC1275: Exception sending context initialized event to listener instance of class com.sun.faces.config.ConfigureListener
      java.lang.NoClassDefFoundError: com/sun/facelets/tag/jsf/ComponentHandler
              at java.lang.ClassLoader.defineClass1(Native Method)
              at java.lang.ClassLoader.defineClass(ClassLoader.java:675)
              at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
              at org.glassfish.web.loader.WebappClassLoader.findClass(WebappClassLoader.java:974)

      This happens because the application has dependencies on internal classes of Facelets 1.1.x. That makes the application non-compatible with Mojarra 2.x which comes bundled with Facelets 2.0.
    3. Lets instruct the application to disable the Facelets 2.0 baked in Mojarra by adding the following application wide context parameter in "web.xml":

      <context-param>
             <param-name>javax.faces.DISABLE_FACELET_JSF_VIEWHANDLER</param-name>
             <param-value>true</param-value>
       </context-param>

      Now the Facelets 1.1.x classes bundled with the application are used. With this change, the application gets deployed correctly and shows the following log message:

      Dec 5, 2008 2:50:21 PM com.sun.faces.config.ConfigureListener contextInitialized
      INFO: Initializing Mojarra 2.0.0 (SNAPSHOT b05) for context '/myfaces-simple-v3'
      Dec 5, 2008 2:50:21 PM com.sun.enterprise.web.WebApplication start
      INFO: Loading application myfaces-simple-v3 at /myfaces-simple-v3
      Dec 5, 2008 2:50:21 PM org.glassfish.deployment.admin.DeployCommand execute
      INFO: Deployment of myfaces-simple-v3 done is 1513 ms

      The important thing to note here is that Mojarra 2.0.0 implementation (as indicated by the bold text) is used as JSF runtime. This is the newest and the greatest runtime that implements JSF 2.0 specification and baked in GlassFish v3 trunk. The main page from this deployed application at "http://localhost:8080/myfaces-simple-v3/" looks like:

  6. Deploy using MyFaces on GlassFish v3 Trunk
    1. Download the latest GlassFish v3 Nightly.
    2. Lets deploy the WAR file previously created as "asadmin deploy myfaces-simple-v2.war". The main page of the application is accessible at "http://localhost:8080/myfaces-simple-v2".
    3. The "useMyFaces" property, as specified in "sun-web.xml", is expected to work for any bundled JSF implementation. The recommended way to deploy such a web application in GlassFish v3 is to use the property "useBundledJsf". The updated "sun-web.xml" is shown below:

      <?xml version="1.0" encoding="UTF-8"?>
      <!DOCTYPE sun-web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Application Server 8.1 Servlet 2.4//EN" "http://www.sun.com/software/appserver/dtds/sun-web-app_2_4-1.dtd">
      <sun-web-app>
      <class-loader delegate="false"/>
      <property name="useBundledJsf" value="true"/>
      </sun-web-app>
    4. Create a WAR file in "simple" directory as "jar cvf myfaces-simple-v3-usebundled.war \*".
    5. Deploy the generated WAR on GlassFish v3 as "asadmin deploy myfaces-simple-v3-usebundled.war". It shows the following message:

      . . .

      Dec 5, 2008 3:19:54 PM  
      INFO: 2008-12-05 15:19:54,786 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Reading config jar:file:/Users/arungupta/tools/glassfish/v3/snapshot/glassfish/domains/domain1/applications/myfaces-simple-v3-usebundled/WEB-INF/lib/tomahawk-1.1.8.jar!/
      META-INF/faces-config.xml

      Dec 5, 2008 3:19:54 PM  
      INFO: 2008-12-05 15:19:54,806 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Reading config /WEB-INF/examples-config.xml

      Dec 5, 2008 3:19:54 PM  
      INFO: 2008-12-05 15:19:54,828 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Reading config /WEB-INF/testSuite-config.xml

      Dec 5, 2008 3:19:54 PM  
      INFO: 2008-12-05 15:19:54,841 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Starting up MyFaces-package : myfaces-api in version : 1.1.6 from path : file:/Users/arungupta/tools/glassfish/v3/snapshot/glassfish/domains/domain1/applications/myfaces-simple-v3-usebundled/WEB-INF/lib/myfaces-api-1.1.6.jar

      Dec 5, 2008 3:19:54 PM  
      INFO: 2008-12-05 15:19:54,841 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Starting up MyFaces-package : myfaces-impl in version : 1.1.6 from path : file:/Users/arungupta/tools/glassfish/v3/snapshot/glassfish/domains/domain1/applications/myfaces-simple-v3-usebundled/WEB-INF/lib/myfaces-impl-1.1.6.jar

      Dec 5, 2008 3:19:54 PM  
      INFO: 2008-12-05 15:19:54,841 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - MyFaces-package : tomahawk-sandbox not found.

      Dec 5, 2008 3:19:54 PM  
      INFO: 2008-12-05 15:19:54,841 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.config.FacesConfigurator - Starting up MyFaces-package : tomahawk in version : 1.1.8 from path : file:/Users/arungupta/tools/glassfish/v3/snapshot/glassfish/domains/domain1/applications/myfaces-simple-v3-usebundled/WEB-INF/lib/tomahawk-1.1.8.jar

      . . .

      Dec 5, 2008 3:19:55 PM  
      INFO: 2008-12-05 15:19:55,763 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.shared_impl.config.MyfacesConfig - Starting up Tomahawk on the RI-JSF-Implementation.

      Dec 5, 2008 3:19:55 PM  
      INFO: 2008-12-05 15:19:55,764 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.shared_impl.config.MyfacesConfig - Starting up Tomahawk on the MyFaces-JSF-Implementation

      Dec 5, 2008 3:19:55 PM  
      INFO: 2008-12-05 15:19:55,764 [httpWorkerThread-4848-1] ERROR org.apache.myfaces.shared_impl.config.MyfacesConfig - Both MyFaces and the RI are on your classpath. Please make sure to use only one of the two JSF-implementations.

      Dec 5, 2008 3:19:55 PM  
      INFO: 2008-12-05 15:19:55,764 [httpWorkerThread-4848-1] INFO  org.apache.myfaces.webapp.StartupServletContextListener - ServletContext '/Users/arungupta/tools/glassfish/v3/snapshot/glassfish/domains/domain1/applications/myfaces-simple-v3-usebundled/' initialized.

      Dec 5, 2008 3:19:55 PM com.sun.enterprise.web.WebApplication start
      INFO: Loading application myfaces-simple-v3-usebundled at /myfaces-simple-v3-usebundled
      Dec 5, 2008 3:19:55 PM org.glassfish.deployment.admin.DeployCommand execute
      INFO: Deployment of myfaces-simple-v3-usebundled done is 1994 ms

      The first thing to note is that the message "Initializing Sun's JavaServer Faces implementation (1.2_04-b20-p03)" is not shown. That indicates Mojarra is not used as the JSF runtime. Then you can see how MyFaces 1.1.6 API and Implementation are loaded, Tomahawk 1.1.8 is loaded and finally started using MyFaces JSF implementation.

      The main page from this deployed application at "http://localhost:8080/myfaces-simple-v3-usebundled" is very similar as shown below:



Here is a brief summary of how MyFaces/Tomahawk sample is deployed using Mojarra and MyFaces on GlassFish:

JSF Implementations
Mojarra MyFaces
GlassFish v2 Default "useMyFaces" property in "sun-web.xml"
GlassFish v3 Prelude Default Not supported
GlassFish v3 Trunk Disable Facelets 2.0 in "web.xml" "useMyFaces" OR "useBundledJsf" property in "sun-web.xml"
Disable Facelets 2.0 in "web.xml" (only for Facelets 1.1.x dependencies)

The steps described above for MyFaces can be used with alternate JSF implementations as well. Which JSF implementation do you use ?

Just like Tomahawk components, Trinidad and Tobago libraries should work with Mojarra as well. Have you tried them ?

Technorati: glassfish javaserverfaces mojarra apache myfaces tomahawk

Wednesday Dec 03, 2008

JavaFX 1.0 launched - access services hosted on embedded GlassFish


Today Sun announces the availability of Java FX 1.0.

JavaFX 1.0 is a rich client platform for creating and delivering Rich Internet Applications across all screens (desktop, browser, and mobile) of your life. It consists of the following key components:


  • JavaFX SDK includes JavaFX script compiler and runtime tools, and a host of libraries to create RIAs for desktop, browser and mobile platforms, command-line tools & Ant tasks and other goodies.
  • NetBeans 6.5 support (as plugin or bundled with IDE) that allows to build, preview and debug JavaFX applications using NetBeans IDE. If you prefer CLI support then SDK can be downloaded.
  • Production Suite is a suite of tools and plugins for creative tools (such as Illustrator CS3+) that allows graphical assets to be exported to JavaFX applications.
The beauty of JavaFX is that its fully integrated with the Java Runtime and takes advantage of the performance and ubiquity of Sun's Java Runtime Environment that is installed on literally billions of devices worldwide. Hence, JavaFX applications will run on any desktop, browser, mobile device or any other connected device that runs the Java Runtime Environment.

This blog shows how to create a simple JavaFX application using NetBeans IDE. The application plays a movie, allows the viewer to cast a vote if they liked it, and see aggregate response from other viewers. The application is developed using NetBeans 6.5, JavaFX 1.0 plugin, and coded using JavaFX Script. The voting engine is deployed as a RESTful Web service using Jersey on GlassFish.

In terms of user experience, running the NetBeans project shows a window playing the movie. The first mouse hover over the window allows the viewer to click on "I love it" or "Not so great" and cast their vote as shown below:



Any subsequent mouse hover shows aggregated results from other viewers as shown below:



The results are not interesting if there is a single viewer of the movie. But for a production environment, this movie will be played by multiple users concurrently and the percentage numbers will be more meaningful. You can close the window and run the project again to vote again, as many times as you like :)

For those who like to see quick results, here is a 4-step guide to get started:
  1. In NetBeans 6.5 IDE, install JavaFX plugin as explained here and RESTful Web services plugin as explained here. Both the plugins may be installed in one step by selecting the required plugins together.
  2. Download NetBeans project for JavaFX client from here and add Jersey dependencies as explained in bullet #5 below. 
  3. Download Web service endpoint Maven project from here and deploy the endpoint as explained in bullet #4 below.
  4. Run the JavaFX application as explained here.
The remainder of this blog explains the details and shows how to construct the demo from scratch.

Lets first create the JavaFX application that plays the video movie.
  1. In NetBeans 6.5, install "JavaFX SDK" plugin.  In the "Tools" menu, "Plugins", search on "JavaFX", select "JavaFX SDK" and click on "Install".
  2. Create a new project of type "JavaFX", "JavaFX Script Application". Take the default values as shown below:



    and click on "Finish".
  3. The source code for this class can be downloaded from here or alternatively constructed as explained in the sub-bullets.
    1. In the newly created class, change the Stage (root area for all scene content) to:

      Stage {
         title: "GlassFish Media Player"
         width: 625
         height: 360
         resizable: false
         scene: myScene
      }
    2. Create a scene that contains the view of the media to be played and controls the display of the Vote or Result nodes:

      var myScene: Scene = Scene {
         content: MediaView {
                 fitWidth: 625
                 fitHeight: 360
                 mediaPlayer: bind myPlayer

                 onMouseEntered: function( e: MouseEvent ):Void {
                     println("mouse entered");
                     if (voted == false) {
                         insert Vote{} into myScene.content;
                     } else {
                         insert Result{} into myScene.content;
                     }
                 }

                 onMouseExited: function( e: MouseEvent ):Void {
                     delete myScene.content[1]
                 }

             }
      }
    3. Create a Media Player to use with the scene:

      var myPlayer: MediaPlayer = MediaPlayer{
          autoPlay: true
          media: bind myMedia
      };
    4. Create the media object to be used with the Media Player:

      var myMedia: Media = Media {
          source: "http://sun.edgeboss.net/download/sun/media/1460825906/1460825906_2957290001_DayEarth-Bluray.flv"
         };

      You can change the location of the movie here in the media player. For example, changing it to "http://mediacast.sun.com/users/ArunGupta/media/v3prelude-nb65-webapp.flv" will start playing the screencast #27.
    5. Create a Vote class that is a CustomNode and appears when a user's mouse enters the scene where the video is playing. The user can select whether he likes the clip or not and the vote is recorded making a Web service call using Jersey Client APIs:

      class Vote extends CustomNode {
         override function create():Node {
             return Group {
                 content: [
                     Rectangle {
                         fill: Color.GREEN
                         x: 185
                         y: 145
                         width: 243
                         height: 38
                         arcWidth: 20
                         arcHeight: 20
                     },

                     Text {
                         x: 195
                         y: 170
                         fill: Color.WHITE
                         font: Font {
                             size: 18
                         }
                         content: "I love it"
                     },

                     Rectangle{
                         x: 191
                         y: 148
                         smooth: false
                         width: 73
                         height: 32
                         fill: Color.TRANSPARENT

                         onMouseClicked: function( e: MouseEvent ):Void {
                             println("clicked I love it");
                             voted = true;
                             wsClient.voteLoveIt();
                             delete myScene.content[1]
                         }
                     },

                     Text{
                         x: 305
                         y: 170
                         fill: Color.WHITE
                         font: Font {
                             size: 18
                         }
                         content: "Not so great"
                         },

                     Rectangle {
                         x: 301
                         y: 148
                         smooth: false
                         width: 118
                         height: 32
                         fill: Color.TRANSPARENT
                        
                         onMouseClicked: function( e: MouseEvent ):Void {
                             voted = true;
                             println("clicked Not so great");
                             wsClient.voteNotSoGreat();
                             delete myScene.content[1]
                         }
                     }
                 ]
             }
         }
      };
    6. Create a Result class that is a CustomNode and simply reports on how many voters like this clip:

      class Result extends CustomNode {
         override function create():Node {
             var resultPercent = wsClient.showResults();
             var resultString = "{resultPercent} voters liked this clip";

             return Group {
                 content: [
                     Rectangle {
                         fill: Color.BLUE
                         x: 187
                         y: 145
                         width: 244
                         height: 38
                         arcWidth: 20
                         arcHeight: 20

                         onMouseClicked: function( e: MouseEvent ):Void {
                             delete myScene.content[1]
                         }
                     },

                     Text {
                         x: 199
                         y: 170
                         fill: Color.WHITE
                         font: Font {
                             size: 18
                         }
                         content: resultString
                     }
                 ]
             }
         }
      };
    7. Add two instance variables:

      var voted = false;
      var wsClient = new WebserviceClient;

      The first variable captures if the viewer has already voted and the second variable is an instance to the RESTful Web service client.
    8. Add the following import statements:

      import javafx.scene.\*;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.media.Media;
      import javafx.scene.media.MediaPlayer;
      import javafx.scene.media.MediaView;
      import javafx.scene.paint.Color;
      import javafx.scene.shape.Rectangle;
      import javafx.scene.text.Font;
      import javafx.scene.text.Text;
      import javafx.stage.Stage;

      "Fix Imports" should be able to fix them and bug #154307 is already filed for that.
  4. Create a new class that is used to capture the Vote as:

    @javax.xml.bind.annotation.XmlRootElement
    public class VoteBean {
        public static enum VOTE { LOVE_IT, NOT_SO_GREAT };
       
        public VOTE vote;

        public VoteBean() { vote = VOTE.LOVE_IT; }
        public VoteBean(VOTE vote) {
            this.vote = vote;
        }
    }

    This is a simple Javabean with a standard JAXB annotation. This ensures that XML is used as the data format for transfering results between client and endpoint. The source code for this class is available here.
  5. Add Jersey libraries to the project by right-clicking on Project, select Libraries, click on "Add Library...", select "JAX-RS 1.0" and "Jersey 1.0 (JAX-RS RI)", and click on "Add Library".



    If these libraries are not available then install the "RESTful Web Services" plugin from the Plugin Center.
  6. And finally add the class that invokes the RESTful Webservice endpoint:

    public class WebserviceClient {

        private static com.sun.jersey.api.client.WebResource createWebResource() {
            return com.sun.jersey.api.client.Client.create().
                    resource("http://localhost:8080/movie-feedback-webapp/webresources/myresource");
        }

        public static void voteLoveIt() {
            createWebResource().type("application/json").
                    post(new VoteBean(VoteBean.VOTE.LOVE_IT));
        }

        public static void voteNotSoGreat() {
            createWebResource().type("application/json").
                    post(new VoteBean(VoteBean.VOTE.NOT_SO_GREAT));
        }

        public static String showResults() {
            return createWebResource().get(String.class);
        }
    }

    The Webservice endpoint will be hosted at "http://localhost:8080/movie-feedback-webapp/webresources/myresource". A WebResource is created from the Client. The POST methods are used to cast the user vote and GET method is used to retrieve the aggregated results. The source code for this class is available here.
Now lets create the RESTful endpoint using Jersey and deploy on GlassFish.
  1. Create and deploy a RESTful Web service endpoint
    1. Create a template RESTful Web service endpoint as described in TOTD #56. Lets use the artifactId as "movie-feedback-webapp".
    2. Create the bean "VoteBean" in "org.glassfish.samples" package. This is the exactly same bean used earlier by the client:

      @javax.xml.bind.annotation.XmlRootElement
      public class VoteBean {
          public static enum VOTE { LOVE_IT, NOT_SO_GREAT };
          public VOTE vote;

          public VoteBean() { vote = VOTE.LOVE_IT; }
          public VoteBean(VOTE vote) {
              this.vote = vote;
          }
      }
    3. Update the generated resource
      1. Add @com.sun.jersey.spi.resource.Singleton as class annotation so that only one instance of the resource is created for the entire web application. This allows to save state (preferences from other users) in the RESTful resource.
      2. Add two instance variables:

            int loveIt;
            int noSoGreat;
      3. Add a method that will process HTTP POST requests as:

            @POST
            public void postOneVote(VoteBean bean) {
                if (bean.vote == VoteBean.VOTE.LOVE_IT) {
                    loveIt++;
                } else {
                    noSoGreat++;
                }
                System.out.println("In POST: " + bean.vote);
            }

        This method stores the vote in the resource. The handling of POST request messages by Jersey is explained in TOTD #58.
      4. Add a method that will process HTTP GET requests as:

            @GET
            @Produces("text/plain")
            public String getOpinion() {
                if (loveIt == 0 && noSoGreat == 0)
                    return "No votes cast yet!";
                return (loveIt \* 100) / (loveIt + noSoGreat) + "%";
            }

        This method calculates the percentage of viewers who liked the movie.
    4. Deploy the endpoint using "mvn glassfish:run" in "movie-feedback-webapp" directory.
Now run the JavaFX application by right-clicking on the project and selecting "Run Project" and start voting! The percentage results will vary if the movie is voted upon more than once.

This blog showed:
  • How to install JavaFX capabilities to an existing NetBeans 6.5 installation
  • How to create a simple JavaFX application that plays media files
  • Integrate it with existing Java libraries (Jersey client libraries in this case)
  • Invoke services hosted on GlassFish
The steps followed in this blog allows for rapid development/debugging of JavaFX application accessing resources using embeddable GlassFish but are not ideal for production deployments. A future blog will show how this JavaFX application can be deployed as a Java Web Start application and scaled for mulitple users.

The javafx.com/samples has loads of samples and javafx.com/tutorials shows how to build your own applications. The JavaFX Community Wiki is a great place to collaborate.

Send your Jersey questions to users@jersey.dev.java.net, GlassFish questions to GlassFish Forum, and JavaFX questions to JavaFX Forums.

Technorati: glassfish v3 jersey webservices javafx netbeans

LOTD #15: Deploying Merb application on GlassFish v3 using Warbler


GlassFish v3 with Merb and Warbler explains how to use Warbler to deploy a Merb application on GlassFish v3. Here is a quote from the blog:

this lovely server had a gem which you can use to fire up a Glassfish server. Easy as pie and within no time at all we were developing our Merb app on Glassfish.

The blog requires some workaround for Warbler and Nick is planning to fix them in the next rev. And other workaround is required because of Webrat's recently introduced dependency on Nokogiri which does not run on JRuby yet.

In addition, TOTD #52 explains how to deploy a Merb application using GlassFish gem and TOTD #53 explains how to create a scaffold in Merb.

Submit your Merb/GlassFish bugs here, talk to us using GlassFish Forum, and get the latest information on JRuby wiki.

All previous entries in this series are archived at LOTD.

Technorati: lotd glassfish v3 gem warbler merb
About

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

Archives
« December 2008 »
SunMonTueWedThuFriSat
 
1
2
4
5
6
7
8
9
10
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
   
       
Today