Saturday, July 9, 2005

Creator and AJAX: The Demo

WARNING: This blog entry was imported from my old blog on blogs.sun.com (which used different blogging software), so formatting and links may not be correct.



I did a
demo at JavaOne
during Monday's keynote on Creator 2
and AJAX. Being a keynote demo, it was obviously stripped down to the
bare minimum with a three minute timeslot. Therefore, I didn't have time
to motivate the example, or build up the demo in gradual steps.
But in three minutes, starting from an empty project and using no code clips,
I built a web page that does a google-suggest
like thing where as you're typing in a textfield,
you get autocompletion on words from a 180,000 word dictionary.
I've received a number of questions about it, and so have the
people who were working in the Creator booth at the conference, so
I'm hoping a blog entry will clear some things up.



First, let's talk about AJAX. One of my favorite uses of AJAX is
providing auto completion in text fields. You've probably used address
forms where the State or Country fields use dropdowns with 50 or
more items. You typically KNOW which state or country you want to
select, so being able to type three or four characters to limit your
visible choices is a lot more usable than scrolling through large popups.



However, doing autocompletion smoothly as a partial page refresh
(e.g. no page submission) is somewhat tricky. It takes lots of
JavaScript plumbing to do that - and to get it working correctly
on lots of browsers.



This is an ideal domain for a JSF component. The JSF component
can encapsulate all the hard work in its renderer, meaning that
your JSP files contain no JavaScript code whatsoever.
You get the benefits of AJAX without its usual high development cost.



So let's jump right into it. One of the points that was made in the
keynote, but people have confused in their JavaOne summaries that I've
read, is that the AJAX component I demonstrated is not part of Creator 2;
it's developed in open source on
java.net.
Thus, you can get all the source code for this and enhance the component
(or create similar components) to your heart's desire.



In fact, it gets even better than that. There's a whole
write-up
on this component which explains exactly how it works. It comes with a working
sample application - a JSF address form providing city completion for all 25,000 U.S. cities.
And if you want a more generic overview on how to create JSF and AJAX components, check
out this paper
written by Greg Murray, Ed Burns and me, which covers
some different approaches and trade-offs for this technology.






One of the things you'll find in there is code to wrap the
component up as a Creator component library that you can import
right into the IDE. Once you've
downloaded it,
you can install it by right clicking on a palette category, bringing
up Manage Component Libraries..., select Import, and drill to the
component library jar. Ok your way out. You should now find a new
"Ajax Component Library" palette category at the bottom containing
the completion text field.



(What, only one AJAX component? Yes, we'll add more components there. If you look at the blueprints
page I pointed to earlier, you'll see several additional JSF components
written by Greg Murray, Ed Burns, and more are on the way. These will
be packaged up as Creator importable components as well.)







Now, drop the text field on the page. Then double click on it.
Notice how it will warp to the source code and show you a new event
handler. This event handler has already been bound to the text field
when you double clicked on the text field. (If you study the JSP
you'll see that it has a completionMethod attribute
set to a method binding expression pointing to the method you're
currently editing.)



Take a look at the screenshot. That's what it looks like in my
editor - and what it looked like at JavaOne. But it's not what you're
seeing in Creator 2 Early Access. Here you have a component-specific
event handler, with properly named method parameters, and a default
event body that gives you some suggestions for what to do.
This was added right after Early Access froze so your bits don't
include this functionality, but needless to say it will be in the
shipping release. This will make it easier to write editor validation
event handlers or even button action handlers too, since the default
bodies will guide you in the right direction.



Study the trivial sample code in the comment:


public void ajaxTextField1_complete(FacesContext context, String prefix, CompletionResult result) {
// TODO: Return your own list of items here based on the prefix
//result.addItem("Hello");
//result.addItem(prefix.toUpperCase());

}


If you simply uncomment the last two lines, and hit Run, you'll get an
editor which contains two items in the popup: "Hello", and whatever the user
has typed in the browser - but uppercased:







That's a big deal. Notice that we've written simple Java code which runs
on the server, yet as the user is typing in the browser,
results are dynamically shown without any kind of page submit. The user
gets nearly instant feedback. All the plumbing is taken care of
by the component. So while writing one of these components is tricky
(see the documents referenced above), using an AJAX component
is downright trivial.



Ok, I know what you're thinking. "What, every keystroke in every client's
browser is hitting the server? You're crazy!!". I addressed this point
in the AJAX full hour session at JavaOne. AJAX functionality does
have a price. Your users are getting a better user experience. It doesn't
come for free. You need to have good server resources to support this
type of interaction. (I know a company which will sell you a good server! :-)
Think of Google Suggest for example. Every time one of their users types
a character*, it hits their servers and returns a portion of their index.
(Yes, I'm aware that if users are typing fast multiple keystrokes get
coalesced into a single request). This is clearly not free - they're
using additional servers to be able to handle this load. But to them,
and perhaps to you, the added user benefit justifies the additional
cost.



Anyway, back to the AJAX demo. The previous step (where I simply
do a trivial computation to show server computation reflected in the browser)
was eliminated from the keynote demo due to time constraints.
I jumped right to the part where I do something more complicated on the
server with the prefix we're getting from the user's text field.
I call a web service, which returns a small set of words that start
with the given prefix. Calling a web service is easy. First I have
to import the web service (by pointing to the relevant WSDL file),
then drag the web service onto the canvas (to get a local proxy object
I can make calls on), and finally I change the autocompletion event
method to simply call the web service to get a String array of words,
and then I add those words to the completion result object.
(It has an addItem(String[]) method as well). Thus,
after dragging the dictionary web service onto the page (which gives
me a local field dictionaryServiceClient1), I replace
the code in the event body with:


public void ajaxTextField1_complete(FacesContext context, String prefix, CompletionResult result) {
try {
String[] items = dictionaryServiceClient1.matchPrefix(prefix);
result.addItems(items);
} catch (Exception ex) {
log("Error Description", ex);
}
}

The try/catch skeleton here came from me typing "trc ",
and I just typed dict followed by Ctrl-K to type in the
full name of the service client object.



During the keynote demo, I had a typo - I wrote


result.addItems(result);

rather than the correct code above. When I hit Deploy the compiler rightly complained.
Somebody asked afterwards if that was intentional, since having errors in my code
has happened a couple of times during keynote demos in the past. For example
during James
Gosling's keynote last year
:

In another coding process, Norbye skipped a step, which Gosling noticed. Norbye quipped, "It's really great to have your very own James Gosling to help you code," which drew a big audience laugh.


Let me assure you that the step last year was accidentally missed, not skipped, and again
this year it was definitely not intentional. You try to code in front
of ten thousand people and see if you can do it flawlessly :) I'm only
glad the mistake was trivial and easy to spot. It sure would have been
annoying if I hadn't been able to catch it before getting chased off
the stage. I gave a full-hour Creator 2 demo at NetBeans day the day before JavaOne,
and towards the end I was going to build a nice demo utilizing the new
file upload component. But each time I tried to submit, the file upload component
threw an exception. We spent 5-10 minutes debugging it with the audience's
help but to no avail. Annoyingly, shortly after the demo, Craig McClanahan
(who was in the audience, and also works on Creator) discovered that the problem
had been that I was doing the demo on Internet Explorer (which neither Craig
nor I normally use). I hadn't noticed that on the demo box I was using
it was the default. And it turns out that there is a bug in the EA version
of the upload component where it doesn't work correctly with IE in a couple
of cases. Oh well.



Anyway. Back to the scheduled programming. With the above code change,
and deployment, you run and get the screen shown below. As you type,
dictionary completions from a dictionary of 180,000+ words are instantly
updated:







In the AJAX session I also went on to hook it up so that there's
a lookup button which looks up the definition for the selected word and
displays it below. I'll leave that simple exercise to the reader (hint:
it took less than a minute in the session demo.)



One of the things I wished I would have thought of before the keynote, is
a better "sample word" to type to show the application working.
I typed "Java", but it would have been a lot more fun if I had typed "Brazil" !!
(For those who don't know, many keynote speakers make a point
of mentioning the word "Brazil" in their speeches because it invariably
draws cheer and applause from the Brazillian delegation, each and every
time.)



So that's the spiel. At this point you may wonder where the dictionary
web service came from. I had really wanted to motivate the AJAX example
with the simple deployment step first, such that it gets the "look how easy"
point across. The web service call makes everything more
"magical", especially when it happens so quickly during a 3 minue demo - but
it's important to understand that it's just a method call
which returns some good matches for a string.



The web service was deployed locally. It was extremely easy to create.
I did it in one evening - and that includes the learning curve, since I
had never created a web service before. However, I found
this tutorial
which shows how trivial it is to build one with NetBeans 4.1.
So I went on the internet, found a free dictionary file, wrote a small program
to take all those html files, extract all the words and definitions from
them, and then sorted those and wrote them into two files: and index file,
and a definition file.



The web service is very trivial. It has an interface with two methods:
a method for looking up the definition of a word, and a method for
returning the 10 closest matches to a prefix. The second method simply
does a binary search in the word index, but rather than returning the
matched word or null it returns the index of the closest match, so that
the web service can return the 10 closest words in the sorted word array.
Trivial, eh? You can
download the web service project yourself,
as a .zip file. Unzip it and open it in NetBeans 4.1.



It might complain about a missing server. Use the Server Navigator
in the Tools menu to point to your Creator App Server 8.1 installation;
it's running on port 28080 with username admin and
password adminadmin.



You can now
first run the project to deploy it, and then register the web service
by going to the Web Service node in the project manager, right click
and select Add To Registry... (or something like that). It will show
you the WSDL URL for the deployed web service. In my case it was


http://localhost:28080/DictionaryService/DictionaryService?WSDL


In Creator, you now go to the Web Services node, select Add, point
to the above WSDL file, hit OK, and you've imported the web service
into your Server Navigator. (You can test it too - look at the context
menus for the web service method nodes.)
Now you can simply drag the web service onto pages to
get a local client object like dictionaryServiceClient1
in the code above.



Hopefully the use of a web service doesn't confuse the AJAX component
issue too much. One of the nice things about separating the dictionary
code into a web service (rather than having it included locally) is
that the dictionary is only deployed once, not each time you deploy
your Creator project. Thus, the 10 megabyte dictionary file doesn't
get reparsed each time you start the project. (It takes a couple of
seconds the first time it's called to "warm up", e.g. read its
dictionary list into memory) but once it's up and running there's
no delay at all.




....and that's all I have to say about that...


F. Gump


13 comments:

  1. [Trackback] Writing javascript seems to be a scary prospect for a lot of folk. This is understandable given the tangled mess of code that can easily accumulate if you treat it as a toy language, and slap the code together in a hurry without thinking of an overall...

    ReplyDelete
  2. The AJAX components is rendered wrong in Safari on Mac OS 10.4. I dont know why, but the autocomp. box is shown a couple of cm below the textfield.
    It is rendered correctly in Firefox on Mac OS X

    ReplyDelete
  3. I really want to get this JSF/AJAX/Web Service example running... and I'm close. When I type in the text field, my NetBeans output window shows an error like:
    [#|2005-08-30T15:01:24.852-0500|WARNING|sun-appserver-pe8.1_02|com.sun.faces.el.impl.Coercions|_ThreadID=22;|Attempt to apply operator "-" to null value|#]
    [#|2005-08-30T15:01:24.902-0500|SEVERE|sun-appserver-pe8.1_02|javax.enterprise.system.container.web|_ThreadID=22;|WebModule[/ajax-auto-complete-w-web-service-1a]Error Description
    java.rmi.RemoteException: HTTP transport error: java.net.MalformedURLException: no protocol: REPLACE_WITH_ACTUAL_URL; nested exception is:
    I've posted my problem in detail on experts-exchange but haven't had any luck.
    http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_21544943.html
    Any ideas?
    Thank you.
    Greg

    ReplyDelete
  4. Following the link for "Where is AJAX?!" in AjaxFaces Forum , you will find a brief introduction about creating Google-Suggest with AjaxFaces.

    ReplyDelete
  5. Greg, sorry, I can't tell... One idea is to try some things individually first. Make sure that you can access the web service directly (not from within the separate ajax web app) - use the "Test" facility in the runtime tab once you've deployed the web service.
    The blog entry points to a simple tutorial for building a web service - try going through it, it's very fast - that might uncover some problem in the configuration of the tool or the app server or perhaps there's a necessary step somewhere you missed. Good luck!

    ReplyDelete
  6. Somehow REPLACE_WITH_ACTUAL_URL is inserted by wscompile into wsdl and .java files. It should be replaced by actual web service location.

    ReplyDelete
  7. Hai experts
    can anyone guide me in creating a ajax enabled tabbed pane(apache,myfaces-tomahak component) in my jsfcomponent.Help needed...

    ReplyDelete
  8. Hi, the demo and code is great, thanks. Does anyone know if it is possible to link the results of one Ajax completion field with another. If you try to access the value of the ajax text component in the 'complete' method of another ajax text component using .getText() then the returned value is null. Also, I can't get the component to call a method on the server when selecting an item from the dropdown. Keypresses are handled, but selecting from the dropdown is not.
    24 hrs and still stuck. Please help.

    ReplyDelete
  9. I want to include an open source ajax library into my creator project that will put a dhtml text editor on my jsf page. I am experimenting with tinymce_2_0_2 http://tinymce.moxiecode.com/. I saw a tutorial on how to use component libraries in creator (http://developers.sun.com/prodtech/javatools/jscreator/learning/tutorials/2/textcompletion.html).
    How do I make my own component libraries to import into creator 2?

    ReplyDelete
  10. Hi, very powerful stuff. I'm new to Creator, and interested in more complex applications of the Ajax JSF components. For example, how would I redirect the results of the auto-complete component to a table component, tree, or other basic component on the same page? Would I have to write a specialized component? Is there any info on how to write "new" Ajax JSF components?

    ReplyDelete
  11. Hi
    I am Naresh Cheedella.I am a software Enginner in Codec Communications Pvt Ltd Pune.
    I am very much exciting with the uess of Ajax.
    Thank U Very Much..........................

    ReplyDelete
  12. Tor this is a great post. Just did a netbean AJAX screencast. I have built a custom JSF AJAX combobox component using the JSF phaselistener. The component I built also improves address entry.
    http://blogs.ebusiness-apps.com/godfrey/?p=15
    Screencast:
    http://labs.ebusiness-apps.com/download/NetBeansJSFComboBoxDemo/NetBeansJSFComboBoxDemo.html
    If you have any advice please let me know.
    Cheers,
    Godfrey Hobbs

    ReplyDelete