The Source for Java Technology Collaboration
User: Password:



Ed Burns's Blog

December 2006 Archives


Adding Dynamic Faces Support to a jMaki widget

Posted by edburns on December 07, 2006 at 09:09 AM | Permalink | Comments (1)

In this entry, I show how I added Dynamic Faces support to the Dojo Inline Editor widget in jMaki.

Background and History

If you've read my blog before, you know that I think JSF is a good fit for handling the motherhood and apple pie requirements of web applications. What you may not know is that jMaki intentionally leaves the work of satisfying these requirements as an exercise for the developer. No problem, just use jMaki and JSF together. This is indeed a valid answer, but there is a subtlety that bears clarification.

When using jMaki + JSF under J2EE 1.4 (the default target audience for jMaki), you miss out on the following features:

  • Ability to have the jMaki widget POST back to the faces page while preserving the JSF view state.

  • Ability to use jMaki widgets that represent components that accept user input within an iterating component such as dataTable.

  • Ability to attach valueChangeListeners to jMaki components in the view.

  • Ability to attach converters and validators to jMaki components in the view.

  • Ability to attach action and/or actionListeners to jMaki components in the view.

These deficiencies stem both from missing features in the built-in jMaki JSF support and from missing features in JSF 1.1 commonly used with J2EE 1.4.

The story about jMaki and Dynamic Faces

By using jMaki with Dynamic Faces on top of JavaEE 5, however, all of these deficiencies can be addressed (with the exception of the last two, and issues have been filed for these features). This section discusses how using jMaki and Dynamic Faces addresses all of the deficiencies mentioned above.

Ability to have the jMaki widget POST back to the faces page while preserving the JSF view state.

This is the big one, and it was at the heart of the JavaOne presentation in which I participated. The lack of this feature in many Ajax + JSF solutions is what prompted us to title the session, "Ajax done right". When you do not have the JSF view state, you cannot do any of the things that JSF does well. Including the JSF View state in the Ajax transaction is essential.

By using Dynamic Faces to send and receive Ajax transactions from the browser to the JSF view, the JSF view state is correctly preserved, allowing the entire JSF lifecycle to be used, including conversion, validation, events, etc.

Ability to use jMaki widgets that represent components that accept user input within an iterating component such as dataTable.

This one is near and dear to Jacob Hookom's heart. Not only do you need the JSF view state, but you need a way to apply individual values to an individual components within a composite component (such as a table or tree). This capability did not exist in JSF until version 1.2, and Dynamic Faces takes full advantage of it.

Ability to attach valueChangeListeners to jMaki components in the view.

This didn't exist in jMaki until issue 3 was fixed. However, even though you can add a valueChangeListener to a jMaki component now, you still need to preserve the JSF view state in order for it to ever have a chance of being invoked. Again, Dynamic Faces is the answer.

Ability to attach converters and validators to jMaki components in the view.

Ability to attach action and/or actionListeners to g jMaki components in the view.

As with valueChangeListener, these two need to be first implemented in jMaki, then they need to be used with Dynamic Faces.

How To

The whole point of jMaki is to put today's hot JavaScript widget libraries in the hands of Java web application developers. Each widget library has different ways of doing things, and indeed, each widget within each library does too. jMaki solves this by having specific files with each widget. Therefore, adding Dynamic Faces support to each widget involves making some modifications to the widget specific files. Currently, the only widgets that have been modified are the Dojo FishEye and Inline Editor, and the Scriptaculous Inplace Editor. This section shows some high level guidelines for how to add Dynamic Faces support to an arbitrary jMaki widget.

The widget specific files for a jMaki widget are:

File Name Purpose
component.css Any CSS style associated with an instance of this widget.
component.htm The markup that will be included in the page an instance of this widget.
component.js The JavaScript adapter file for this widget. This adapts between the widget library specific way of doing things and the jMaki server side framework. This is also the easiest place where Dynamic Faces support can be added.
widget.json Any name/value pairs that aid in the configuration of an instance of this widget.

In general, you'll probably just need to modify component.js to add Dynamic Faces support. Please follow Jennifer's Tutorial for how to get started with jMaki and Dynamic Faces. Once you've done that, you need to make sure to include the Dynamic Faces taglib and script tag in the page on which you are using the jMaki widgets.

Syntax highlighting courtesy of http://www.nopaste.com/.

Listing 1, the JSP Page

  1. <%@ taglib prefix="a" uri="http://java.sun.com/jmaki-jsf" %>
  2. <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
  3. <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
  4. <%@ taglib prefix="jsfExt" uri="http://java.sun.com/jsf/extensions/dynafaces"%>
  5.  
  6. <h2>Editor Using JSF</h2>
  7.  
  8. <f:view>
  9.  
  10. <jsfExt:scripts />
  11.  
  12. <h:form prependId="false">
  13. <h:dataTable id="table" rows="10" binding="#{ResultSetBean.data}"
  14. value="#{ResultSetBean.list}" var="customer">
  15.  
  16. <h:column>
  17. <f:facet name="header">
  18. <h:outputText value="Account Id"/>
  19. </f:facet>
  20. <h:outputText id="accountId"
  21. value="#{customer.accountId}"/>
  22. </h:column>
  23.  
  24. <h:column>
  25. <f:facet name="header">
  26. <h:outputText value="Customer Name"/>
  27. </f:facet>
  28. <a:ajax name="dojo.inlineedit" value="#{customer.name}" />
  29. </h:column>
  30.  
  31. <h:column>
  32. <f:facet name="header">
  33. <h:outputText value="Symbol"/>
  34. </f:facet>
  35. <h:outputText id="symbol"
  36. value="#{customer.symbol}"/>
  37. </h:column>
  38.  
  39. <h:column>
  40. <f:facet name="header">
  41. <h:outputText value="Total Sales"/>
  42. </f:facet>
  43. <h:outputText id="totalSales"
  44. value="#{customer.totalSales}"/>
  45. </h:column>
  46.  
  47. </h:dataTable>
  48.  
  49. </h:form>
  50.  
  51. </f:view>
  52.  

Line 4 has the taglib for Dynamic Faces, line 11 uses the scripts tag. Line 29 has the use of the jMaki ajax component.

Let's take a look at the component.js for the Dojo Inline Edit widget, before Dynamic Faces support was added.

Listing 2, the component.js before Dynamic Faces is added

  1. dojo.require("dojo.widget.*");
  2. dojo.require("dojo.widget.InlineEditBox");
  3. dojo.require("dojo.event.*");
  4.  
  5. var container = document.getElementById(widget.uuid);
  6. var w = dojo.widget.createWidget(container);
  7.  
  8. w.getValue = function() {
  9. return w.textValue;
  10. }
  11.  
  12. // add a saveState function
  13. if (typeof widget.valueCallback != 'undefined') {
  14. w.onSave = function(newValue, oldValue) {
  15. // we need to be able to adjust this
  16. var url = widget.valueCallback;
  17. dojo.io.bind({
  18. url: url + "?cmd=update",
  19. method: "post",
  20. content: { "value" : newValue },
  21. load: function (type,data,evt) {
  22. // do something if there is an error
  23. }
  24. });
  25. }
  26. }
  27. w.saveState = w.onSave;
  28. jmaki.attributes.put(widget.uuid, w);

On lines 1 - 3 we have the dojo includes: uninteresting. Line 5 of listing 2 is interesting. The "widget" refererred to is this instance of this widget in the page. In this case, it's the widget on line 29 of Listing 1. However, keep in mind that line 29 of listing 1 is inside of a dataTable, therefore, there will be 10 instances of this widget in the page, each corresponding to an actual JavaScript object in the JavaScript VM. Therefore, the value of widget.uuid will be different for each widget, and will, most importantly, correspond to the JSF Client Id for the widget in the JSF view. This is important because in order to address the widget in the JSF view, you need its client id.

For example, the widget.uuid values for this page happen to be table:n:j_id_id34 where n is the numbers 0 to 9.

Returning to line 5 of listing 2, the actual DOM element returned by the call to document.getElementById is what jMaki renders based on the content of the component.htm file. For the dojo inline editor, this file is:

  1. <div id="${uuid}" dojoType="inlineEditBox" class="dInlineEdit">${value}</div>

The jMaki AjaxWrapperRenderer will cause the component.htm file to be rendered like this in the page from listing 1.

  1. <div id="table:0:j_id_id34" dojoType="inlineEditBox" class="dInlineEdit">name_0</div>

Therefore, when line 5 of listing 2 executes, the value of container is the div element with id table:n:j_id_id34, where n, of course, is the current row in the table.

Line 6 of listing 2 uses the dojo createWidget to create an instance of the inline edit box inside of the container element.

Lines 14 - 25 of listing 2 declares the JavaScript function that will be called when the user presses the "save" button in the widget UI.

Now, let's take a look at the changes to component.js necessary to add Dynamic Faces support, expressed as a diff.

Listing 3, the diffs to component.js to add Dynamic Faces support

  1. --- component-pre-DF.js 2006-12-07 10:32:25.000000000 -0500
  2. +++ component.js 2006-12-07 10:31:19.000000000 -0500
  3. @@ -12,6 +12,16 @@
  4. // add a saveState function
  5. if (typeof widget.valueCallback != 'undefined') {
  6. w.onSave = function(newValue, oldValue) {
  7. +
  8. + if (typeof _globalScope.DynaFaces != 'undefined') {
  9. + DynaFaces.fireAjaxTransaction(container,
  10. + {
  11. + execute: widget.uuid,
  12. + render: "none",
  13. + postBody: widget.uuid + "=" + newValue
  14. + });
  15. + }
  16. + else {
  17. // we need to be able to adjust this
  18. var url = widget.valueCallback;
  19. dojo.io.bind({
  20. @@ -22,7 +32,8 @@
  21. // do something if there is an error
  22. }
  23. });
  24. + }
  25. }
  26. }
  27. w.saveState = w.onSave;
  28. -jmaki.attributes.put(widget.uuid, w);
  29. \ No newline at end of file
  30. +jmaki.attributes.put(widget.uuid, w);

The only change is a conditional within the onSave function. jMaki defines a _globalScope global variable that allows you to access all things globally defined in the page. It so happens that the scripts included by virtue of line 11 of listing 1 define a global variable called DynaFaces. This JavaScript object has all the functions exposed by the Dynamic Faces JavaScript library. Therefore, you can safely test for the existing of Dynamic Faces in a page as shown on line 8 of listing 3. If Dynamic faces is not being used in the page, then the normal jMaki operation of the widget takes place, as shown on lines 17 - 23 of the diff.

Lines 9 - 14 of listing 3 cause a Dynamic Faces Ajax transaction to be made back to the JSF page currently being viewed in the browser. This transaction includes all the view state for the current f:view. The first argument to fireAjaxTransaction is the DOM element from which the transaction should be fired. It is important that this be the DOM element that corresponds to the actual JSF UIComponent in the view. The second argument to fireAjaxTransaction is a JavaScript associative array. In this case, we're saying three things:

  • do the 'execute' portion of the JSF lifecycle just on the JSF UIComponent for this widget instance.

  • do not re-render anything to the browser (in this case, Dojo handles it).

  • In the HTTP POST body of the request, include the name value pair for the currently entered value of the widget.

Note that newValue, on line 6 of listing 3, is an argument to the onSave function. It is the new value entered by the user.

For more information on the JavaScript functions exposed from Dynamic Faces, see the Dynamic Faces Reference.

Summary

jMaki gives you cool widgets. Dynamic Faces + jMaki gives you cool widgets you can use in a real enterprise web application.

Technorati Tags:

Knoppix, Partimage, VMware, mmmm

Posted by edburns on December 04, 2006 at 05:56 AM | Permalink | Comments (0)

Background

I had been doing the multi-boot thing on my Toshiba Satellite 5205-S703 since I bought it in late 2002. It came factory installed with Windows XP Home Edition (monopoly anyone?) on an NTFS partition. Thanks to Partition Magic 7.0, I soon ended up with the following partition table:

Physical Geometry: 7,926 Cyls, 255 Hds, 63, Sects

Drive: C:
Partition Type: 07 (Hex) NTFS
Serial Number: F8CD:F94F
Total Physical Sectors: 80,080,632 (3,945.6 MB)

Drive: E:
Partition Type: 08 (Hex) FAT32
Serial Number: None
Total Physical Sectors: 8,353,800 (4,079.0 MB)

Drive: Extended
Partition Type: 0F (Hex) ExtendedX
Serial Number: None
Total Physical Sectors: 100,775,745 (49,206.9 MB)

Drive: F:
Partition Type: 08 (Hex) FAT32
Serial Number: None
Total Physical Sectors: 78,605,982 (38,381.8 MB)

Drive:  SWAPSPACE2
Partition Type: 82 (Hex) Linux Swap
Serial Number: None
Total Physical Sectors: 2,120,517 (1,035.4 MB)

Drive: Linux Ext2
Partition Type: 83 (Hex) Linux Ext2
Serial Number: None
Total Physical Sectors: 20,049,057 (9,789.6 MB) 

C: contained the factory Windows XP, E: contained Windows 2000 Professional, where I did most of my production work, including work on mozilla.org side projects. In the GNU/Linux partition, I ran Sun Java Desktop System Linux. I used GRUB as my bootloader, but somehow managed to have the Windows bootloader in there as well (like a sub-menu) to choose between Win XP and 2k, once "Windows" had been chosen from the main menu. I never bothered to fix that.

At the JAOO 2006 conference, VMware was giving out free licenses to their VMware workstation 5.5 product. Seeing an opportunity to free myself from the shackles of a multi-boot existence, I took one. Several months later, I began the long and arduous process of converting my multi-boot machine into one that just boots the factory installed OS (since that's the only thing officially supported by the manufacturer anyway) and uses VMs for the other OSes.

Process

I wanted to convert the OSes installed in my real hard disk partitions into VMs. Here is the high level process I will use.

  1. Install VMware Workstation 5.5 under the Windows XP OS, using an external disk for extra space and to contain the VMs for the guest OSes.

  2. For each OS on a real partition on my disk

    1. Use a Knoppix 3.7 CD I had made years ago to boot the Toshiba laptop. Once booted into Knoppix, use partimage to create partition images of all the OS partitions on the mahcine. Swap partitions needn't be imaged, for obvious reasons, and data partitions needn't be imaged because they can simply be copied. I stored the partimage files on an external disk.

    2. Create a VM for that OS, storing it on an external disk.

    3. Boot the VM into Knoppix

    4. Install VMware tools into the running Knoppix instance so I can access the partimage files using the "shared folders" feature of VMware.

    5. Use partimage to restore the OS partition into the virtual hard disk of the VM.

    6. Re-install the appropriate boot loader for that OS to enable that OS to boot.

  3. Once VMs had been created, on an external disk, for all the OSes on real partitions, completely and totally wipe, defrag, and re-install from factory media the laptop. After the factory restore has completed, run Windows update however many times I need to get the machine totally up to snuff and current. Re-install virus protection software and other essential goodies. Note, on the core, non virtual OS, I plan to install very little real software, just stuff that absolutely has to have access to the real hardware.

Details for the hard part: Steps b. thru f. above

  1. Create the new VM using VMware.

    Set the guest OS type to be the type of the OS stored in your partimage files.

    Make sure the hard drive types (IDE or SCSI) match what is in the partimage files. I chose to pre-allocate space. Not sure if this is necessary.

    Make the same amount of disk space is allocated in the VM as was in the partition from which the partimage files were made.

    Because you are using the Knoppix live CD, you need to mount a second CDROM drive from the linux.iso file, located in the install directory of VMware. Make sure to assign IDE 1.1 to this virtual CDROM drive.

    Use the VMware shared folders feature to expose your partimage files to the VM.

  2. Boot knoppix and install VMware tools into the running Knoppix instance.

    Download the vmware-knoppix-overlay.tar.gz to a real machine on which an FTP server is running. In fact, this can be the VMware host machine, but it needn't be.

    With the Knoppix CD in the drive, start the VM, pressing escape during the VMware startup screen to enter the boot menu. Select CDROM.

    When Knoppix starts up, get root access: su - , then

    cd /tmp
    tar -zxf /mnt/cdrom1/VMwareTools-5.5.3-34685.tar.gz
    

    Use FTP to transfer the vmware-knoppix-overlay.tar.gz file to /tmp in Knoppix.

    cd /
    tar -zxf /tmp/vmware-knoppix-overlay.tar.gz
    cd /tmp/vmware-tools-distrib
    ./vmware-install.pl
    

    This will run the modified VMware installer for Knoppix. When it asks "In which directory do you want to install the binary files?" answer /ramdisk/bin.

    Accept the defaults for the rest of the questions until it asks, "What is the location of the directory of C header files that match your running kernel?" Answer /ramdisk/lib/modules/2.4.27/build/include.

    When done with these scripts, you should have access to your shared folders under the path /mnt/hgfs.

  3. Use fdisk under knoppix to partition the virtual disk, most likely it will be /dev/hda, in such a way that it is equivalent to the partimage files you are going to restore. For example, if the partimage files were created from a JFS filesystem, use JFS as the partition type in fdisk. If you need to create a swap partition, make sure to do that as well.

  4. Use partimage to restore the image files under /mnt/hgfs to the new virtual disk.

  5. Lastly, you need to make sure that the boot loader has been restored. It's very unlikely that you can use the bootloader that happened to be installed in the partimage files. Of course, bootloaders are a matter of preference, but I used GRUB. Here's what I had to do in my case.

    Back at the root prompt in Knoppix:

    mkdir /mnt/hda1
    mount /dev/hda1 /mnt/hda1
    cd /mnt/hda1/boot
    cp -r grub grub.orig
    rm -rf grub
    grub-install --no-floppy --root-directory=/mnt/hda1 --recheck /dev/hda
    

    At this pount the guest OS should boot.

Of course, your mileage may vary, but hopefully there is some useful information here. It was a PITA for me to figure this out so I thought I ought to share it once I did.

Technorati Tags:

(near) Zero (re-)Deployment Time for JSF

Posted by edburns on December 01, 2006 at 06:52 PM | Permalink | Comments (4)

One oft cited complaint about Java Web Applications is the slow and laborious deployment step. This step seriously undermines the ability to get into a flow state and is generally a major buzz kill. The absense of a deployment step is one reason why people like Ruby on Rails so much.

JSF-Extensions Design Time brings good news for flow-impaired Java Web application Developers: the JMX PhaseListener.

The implementation of this was really simple. I just took Jean-Francios Arcand's JMXDeploy class, modded it ever so slightly, as you can see in the source code, and called it from a PhaseListener.

I did a screencast about it too.

Technorati Tags:



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds