Michael Yuan

“Science is a wonderful thing if one does not have to earn one’s living at it” — Albert Einstein

October 19th, 2006

3 ways to easy Ajax in Seam + JSF + Facelets (3)

Third approach: use the Seam Remoting JavaScript library to access backend Seam components directly when a page event happens.

[plug]
This is part of my blog series on Ajax with Seam+JSF+Facelets. Sample code and detailed explanations are also covered in my book on Seam.
[/plug]

Advantage: You can access the backend components via JSF EL in the JavaScript calls. This approach works with any third party JavaScript library, and gives us the most flexibility. Plus, the Seam remoting library itself automatically handles the XML call and asynchronous callback.

Disadvantage: Some JavaScript coding is needed.

The following example shows how to use Seam remoting to implement the username check (check if the input username already exists in the database). The JavaScript obtains a stub reference to the manager component from the backend Seam context. The checkName() JavaScript method invokes the manager.checkName() backing bean method via AJAX to check whether the username is already in the database when the input field loses focus. When the backing bean method finishes, it makes a callback to the JavaScript method checkNameCallback(), which decides what message to render.

JavaScript:
  1. <script type="text/javascript"
  2.   src="seam/remoting/resource/remote.js">
  3. </script>
  4.  
  5. <script type="text/javascript"
  6.   src="seam/remoting/interface.js?manager">
  7. </script>
  8.  
  9. <script language="javascript">
  10.   // Seam.Remoting.setDebug(true);
  11.  
  12.   // don't display the loading indicator
  13.   Seam.Remoting.displayLoadingMessage = function() {};
  14.   Seam.Remoting.hideLoadingMessage = function() {};
  15.  
  16.   // Get the "manager" Seam component
  17.   var manager =
  18.       Seam.Component.getInstance("manager");
  19.  
  20.   // Make the async call with a callback handler
  21.   function checkName () {
  22.     var e = document.getElementById("form:name");
  23.     var inputName = e.value;
  24.     manager.checkName(inputName, checkNameCallback);
  25.   }
  26.  
  27.   function checkNameCallback (result) {
  28.     if (result) {
  29.       hideCheckNameError ();
  30.     } else {
  31.       showCheckNameError ();
  32.     }
  33.   }
  34. </script>

The JSF component here is just a regular component with an ID that can be referenced from JavaScript methods, and UI event handler (e.g., onblur) associated with the above JavaScript methods.

XML:
  1. <h:inputText id="name"
  2.             value="#{greeter.name}"
  3.             onfocus="hideCheckNameError()"
  4.             onblur="checkName()"
  5.             size="15"/>
  6. <span id="nameError" style="display:none">
  7.   You have already said hello! :)
  8. </span>

In order to access the backing bean method via Seam remoting, we have to tag it with the @WebRemote annotation in the EJB3 session bean interface.

JAVA:
  1. @Local
  2. public interface Manager {
  3.   ... ...
  4.  
  5.   @WebRemote
  6.   public boolean checkName (String name);
  7. }
  8.  
  9. @Name("manager")
  10. public class ManagerAction implements Manager {
  11.  
  12.   ... ...
  13.  
  14.   public boolean checkName (String name) {
  15.     System.out.println("CheckName() is called");
  16.    
  17.     List <Greeter> existing = em.createQuery(
  18.         "select g from Greeter g where name=:name")
  19.         .setParameter("name", name)
  20.         .getResultList();
  21.    
  22.     if (existing.size() != 0) {
  23.       return false;
  24.     } else {
  25.       return true;
  26.     }
  27.   }
  28. }

Of course, the example here is very simple and it can be easily accomplished by the Ajax4jsf approach as we have seen. But the real power of Seam remoting is that it exposes an API to JavaScript and hence allows us to work with any JavaScript library, such as Dojo and Rico, to integrate UI with server side components.

This concludes our blog series on Ajax and Seam+JSF+Facelets.

October 17th, 2006

3 ways to easy Ajax in Seam + JSF + Facelets (2)

Second approach: use a generic AJAX component library for JSF. The library we talk about here is the open source Ajax4jsf library.

[plug]
This is part of my blog series on Ajax with Seam+JSF+Facelets. Sample code and detailed explanations are also covered in my book on Seam.
[/plug]

Advantage: The benefit is that it allows us to add AJAX functionality to any existing JSF component. Again, it does not require any JavaScript or AJAX servlet code. Since all the Ajax requests happen within the JSF component lifecycle, the backend value binding just works without any additional code.

Disadvantage: It cannot render visual effects beyond re-rendering certain JSF components. It would be hard to implement the auto-completion text popup box using this approach. In addition, it bandwidth intensive to wrap JSF requests in AJAX calls, especially if you use client side state saving.

In the following example, we show how to use AJAX to check whether a user input username already exists in the database. The a4j:support tags adds AJAX support to the existing h:inputText component. Every time the user blurs (de-focuses) the input field, the Ajax4jsf framework submits the value of the input field into the backing bean property via an AJAX call. Once the AJAX call returns, the framework re-renders the "usernameCheck" component.

XML:
  1. <h:inputText value="#{manager.tryUsername}" size="15">
  2.     <a4j:support event="onblur" reRender="usernameCheck"/>
  3. </h:inputText>
  4. <span id="usernameCheck">#{manager.usernameCheck}</span>

When the #{manager.tryUsername} backing bean property is set, the backing bean checks the database for existing usernames. Depending on the name availability, the bean sets the usernameCheck property to be re-rendered on the web page.

JAVA:
  1. @Name("manager")
  2. public class ManagerBean implements Manager {
  3.  
  4.   ... ...
  5.  
  6.   private String tryUsername;
  7.   private String usernameCheck;
  8.  
  9.   public String getTryUsername() { return tryUsername; }
  10.   public void setTryUsername(String tryUsername) {
  11.     this.tryUsername = tryUsername;
  12.    
  13.     List existing = em.createQuery(
  14.         "select u from User u where username=:username")
  15.         .setParameter("username", tryUsername)
  16.         .getResultList();
  17.    
  18.     if (existing.size()==0) {
  19.       usernameCheck = tryUsername + " is available";
  20.     } else {
  21.       usernameCheck = tryUsername + " is NOT available";
  22.     }
  23.   }
  24.  
  25.   public String getUsernameCheck() { return usernameCheck; }
  26.   public void setUsernameCheck(String usernameCheck) {
  27.     this.usernameCheck = usernameCheck;
  28.   }
  29. }

Ajax4jsf is a very cool library. It works out of the box with Seam and Facelets. Learn to use it!

Courtesy of Todd Smart, here is an Ajax4jsf port of the Seam Hotel Booking demo application. We will have an official Ajax4jsf example for the Seam Hotel Booking demo in the next Seam release!

October 16th, 2006

3 ways to easy Ajax in Seam + JSF + Facelets (1)

First approach: reuse AJAX-enabled JSF UI components

[plug]
This is part of my blog series on Ajax with Seam+JSF+Facelets. Sample code and detailed explanations are also covered in my book on Seam.
[/plug]

Advantage: The benefits of this approach are simplicity and power: you do not need to write a single line of JavaScript or AJAX servlet code. Yet, the component itself knows how to render just about any JavaScript and AJAX visual effects -- the JavaScript and backend communication mechanism is encapsulated in the component itself. AJAX services are implemented in Seam backend components bound to the UI component. Due to the high level of encapsulation, you can buy or download such components from market place and just drop them into your application to have Ajax within minutes.

Disadvantage: There is a steep learning curve for implementing your own JSF components. So, you are probably limited to what component vendors sell on the market place. Also, the component license fee can be pretty expensive.

To show how this approach works, let's look at some sample code. The following is an example JSF UI component for an auto-completion text box. With each keystroke, the web page sends the partial input text to the backing bean method #{manager.suggestName} via AJAX, which returns a list of autocompletion suggestions to be rendered on the page. All the JavaScript and communication code are encapsulated in the JSF component.

XML:
  1. <ajax:autoComplete
  2.          size="15" id="name"
  3.          completionMethod="#{manager.suggestName}"
  4.          value="#{greeter.name}"
  5.          required="false" />

The backing bean method for AJAX service is as follows.

JAVA:
  1. @Name("manager")
  2. public class ManagerAction implements Manager {
  3.  
  4.   ... ...
  5.  
  6.   public void suggestName (FacesContext context,
  7.                             String partialName,
  8.                             CompletionResult result) {
  9.     ... use "partialName" to construct a list of
  10.         potential autocompletion suggestions
  11.         in the "result" variable ...
  12.   }
  13. }

It is important to point out that when using those component libraries in Seam, you will need to re-package the *.tld files in a seperate JAR in your WAR file's WEB-INF/lib directory, and then place the main component library JAR file in the EAR application's class path (get the book for more on this! :) ).

There are several component vendors selling a variety of Ajax-enabled JSF components. Exadel and IceSoft are two well-known commercial vendors. The Sun Blueprints provides some good open source component examples. Interested readers should check out this TSS article and the JSf Matrix site for a comparison of AJAX JSF components libraries.

October 16th, 2006

3 ways to easy Ajax in Seam + JSF + Facelets (Prelude)

Still implementing Ajax using brute force JavaScript and XML? That is sooooo 2005! To make Ajax really useful in enterprise applications, we have to integrate it with state-of-the-art server-side web frameworks. In this 4-part blog series, I will attempt to give you a short tutorial on 3 ways to add Ajax functionalities into your Seam + JSF + Facelets applications. They range from very easy (zero JavaScript or XML code) to very powerful (works with any JavaScript Ajax library). So, stay tuned. All the approaches with full sample applications are also covered in my book on Seam. In this first part, I will explain why it is a challenge to integrate Ajax into server side Java application.

So, what is the issue to use AJAX with JSF and Seam web applications? After all, JSF and Seam, especially with Facelets, allow you to use arbitrary HTML tags and JavaScripts on the web page. You can certainly use any JavaScript library to build whatever web UI you want. Well, the real challenge is how to integrate those JavaScript-rendered UI with backend business components. For instance, you might be able to use off-the-shelf JavaScript library to render a rich text editor on the web page, but how do you bind the user input text in the editor box to a backend component (e.g., a string property on a Seam EJB3 entity bean)? The JavaScript rendered dynamic UI is not a JSF component and it does not interpret the JSF EL (i.e, the #{obj.property} notation) for backing bean references.

A naive approach is to write a special HTTP servlet to handle AJAX requests from the JavaScripts. The servlet can then interact with objects in the FacesContext or HttpSession to save the user input or generate AJAX response to the JavaScript. However, the problem with this approach is that it includes a lot of manual coding on both the client-side JavaScript and the server-side Java Servlet. The AJAX servlet developer must be very careful with the states of the server-side objects. It is obviously not the ideal solution. Is there an easier, simpler way to support AJAX in JSF and Seam applications?

Fortunately, as a cutting edge web framework, JSF and Seam provide several elegant ways to integrate AJAX support in your web applications. In the next 3 blog posts, I will cover the following three approaches.


1. Use Ajax-enabled JSF components

2. Use generic Ajax enabler library for existing JSF components

3. Use specialized JavaScript for backend access (Seam remoting)

October 5th, 2006

Ajax4jsf 1.0.2 works out-of-the-box with Seam

Ajax4jsf 1.0.2 now works out-of-the-box with JBoss Seam and Facelets running inside JBoss AS (Ajax4jsf + Seam + plain JSP still has some issues though!). There is no more shared JAR files or ugly hacks. You just need to package the Ajax4jsf library JARs in the same JAR as your EJB3 beans:


myapp.ear
| + myapp.war
| + myapp.jar
     | + ajax4jsf.jar
     | + oscache-xxx.jar
     | + mybean.class

Congratulations to the Ajax4jsf team! So, why do we no long need the separate *-war.jar files in myapp.war? As it turns out, more recent JSF implementations (i.e., JBoss AS 4.0.4 and above) search files by

JAVA:
  1. Thread.currentThread.getContextClassLoader().getResources()

So, it sees all configuration files in classpath including those in myapp.jar.

September 11th, 2006

Use Ajax4jsf with Seam + Facelets

This post is out-dated for Ajax4jsf 1.0.2+. Please refer to this post instead.

Ajax4jsf is a very nice open source framework for adding Ajax features into existing JSF components. I have been playing with it in my Seam + Facelets applications. It is great fun. However, as most other Ajax JSF components, Ajax4jsf requires some tweaking to get around the JBoss ClassLoader when working with Seam. The official docs recommend that you put all Ajax4jsf and Facelets library jars into $JBOSS_HOME/server/default/lib as shared libraries. However, the problem is that those frameworks are not designed to be shared. If you have multiple web applications deployed on the same server, the shared lib approach might causes some problems. In this post, I present an alternative approach to use Ajax4jsf inside Seam.

The short answer:

  • Bundle the ajax4jsf-seam-war.jar and jsf-facelets-seam-war.jar (if using Facelets) in your WAR file's WEB-INF directory.
  • Bundle the regular required JAR files of Ajax4JSF and Faclets in the EJB3 JAR file in your EAR file.
  • Follow instructions in Ajax4JSF docs to setup web.xml and faces-config.xml to use Ajax4JSF with Facelets.

The explanation:

The problem with AJAX JSF frameworks and Seam is that the framework libraries need to be "seen" from both the WAR and EJB3 JAR files. (Non-Ajax frameworks do not have this requirement since they do not need to access the EJB3 "backing beans".) But libraries in the WAR file is invisible outside of the WAR. So, the solution is to place the libraries in the EJB3 JAR file, which on the classpth of all components in the EAR. But it is not that simple: JSF only searches the WAR for tag definitions and configuration files. So, all the .tld, .taglib.xml, and faces-config.xml files in the library JAR's META-INF directory needs to re-pakcaged into a seperate JAR and be placed into the WAR file. That's where the *-seam-war.jar files come from.

But then, why do we need to re-package jsf-facelets.jar as well? The reason is that Ajax4JSF needs to load Facelets by itself. So, it needs to "see" jsf-facelets.jar from outside of the WAR as well.

January 17th, 2006

Google versus Yahoo — Comparing the Map APIs

Over the holidays, I added map-based navigation to our photo albums. In the process, I tried out both Yahoo and Google's AJAX map APIs. In fact, I implemented the album in both APIs (Yahoo / Google). That was a lot of fun and very educational for me. So, here are some observations I'd like to share.

First, I have to note that the AJAX APIs for the two services are extremely similiar. In fact, to port my album from Google to Yahoo, I only need to substitute Google API objects (e.g., GMap, GMarker, GEvent) with corresponding Yahoo API objects (e.g., YMap, YMarker, YEvent). The two implementations of my album have almost the same amount of code. So, whichever service you choose, your investment in the code is pretty much protected if you decide to switch to the other later. Okay, so now the differences:

Google has satellite images while Yahoo does not. Satellite maps not only are a major "coolness" factor but also have practical uses. For instance, in my photo album application, I would like to show points of interests along hiking trails or beyond paved roads. Google also allows you to draw lines or even polygans on the map. Very handy if you want to illustrate a route. I am surprised that Yahoo does not provide this rather basic functionality.

Yahoo's AJAX API does not support line drawing but it handles more mouse events tha the Google API. For instance, I can use mouse over to display smart info windows in Yahoo. Overall, the Yahoo AJAX API is no more superior than Google's. So, why should you consider Yahoo at all? The real reason to use Yahoo Maps API is to take advantage of its Flash API. The Yahoo Flash API produces much better looking markers. But even more importantly, it integrates other Yahoo web services into the API calls. For instance, with the Yahoo Flash API, you can do the following with a single API call:

  • Instantiate markers using street addresses. Google does not offer geo-coding services and you have to somehow get the latitude/longtitude of the marker before you can plot on a Google map.
  • Plot results from Yahoo Local Search. Google does not expose their local search data.
  • Load in large number of markers from external XML files. In contrast, in Google, you'd have to make an AJAX call to the XML file and parse it yourself.

It puzzles me why Yahoo would NOT integrate those valuable services in their AJAX API. If they had done so, the Yahoo AJAX API would have a big edge over Google's. But on the plus side, Yahoo also makes the geocoding / local search / traffic services available as REST style web services. That allows you to integrate them into AJAX applications, where the native map API does not provide those features. You can do that either from the browser (via AJAX calls) or from the server side (when you dynamically assemble the JavaScript for the page). You can even integrate those Yahoo services in Google AJAX Map applications. Kudos to Yahoo!

So, what's verdict? I think for most pure mapping applications, Google is sufficient unless you want the additional Flash UI eye candy. But if you want to develop a serious location application, you will probably need the geocoding and/or local search services from Yahoo. At this moment, the most powerful breed of AJAX mapping applications can combine Yahoo's geocoding / local search services together with Google's satellite maps and polygon plotting.