Archive for 2011

April 12th, 2011
3:28 pm
Action, Listener and Validator Retargeting in a JSF composite component

Posted under JSF
Tags , , , ,

Update 6/7/2011

There appear to be major issues with the design of retargeting when using it in nested composite components – it fundamentally does not work. See this JSF JIRA issue here for details on this. There is quite a robust debate in the comments on the post as to whether to deprecate and redesign retargeting completely, but it looks like that will not happen.

Original Post

This is very much an undocumented feature of JSF 2, but it is possible for the action method for a command component such as a button or link to be passed in to the CC by its client.

You might think that you can just pass in a method expression string as an attribute and use that attribute within the CC on the action attribute of a button, but this fails as there is more that needs to go on behind the scenes. To make it work, you use the targets= attribute when declaring the action attribute on the CCs interface using composite:attribute – note that the attribute in the CC interface must be called action.

This is discussed in a comment (by Cay Horstmann no less) in this StackOverflow post here.

Note that according to the link here from the above post, you can also retarget actionListeners, validators and valueChangeListeners in the same way. In these cases, the attribute name must be called actionListener, validator or valueChangeListener respectively in the same manner as is done with action above. Check the link out for full details – its not exactly clear on its own but it is easier to understand once you have the concepts from Cay Horstmann’s post above.

No Comments »

April 12th, 2011
2:21 pm
Composite Components–Best Practice

Posted under JSF
Tags , , , ,

Update

The original post below mentioned at the beginning #{cc.attrs.clientId}. Whilst this does work and is mentioned on the net, the correct form is #{cc.clientId}. I have amended accordingly.

Original Post

This post here is an IBM Developerworks post on CC best practice. Some comments/observations follow:-

  1. Wrap a cc in a div (NOT a panelgroup) and give it id=“#{cc.attrs.clientId }” “#{cc.clientId }” to give it the ID given to the actual composite. Note that if you use a div, it will NOT be given the CC’s Nameingcontainer prefix (and hence will not have the “double ID” issue whereby it prefixes the ID you specify with the naming container prefix again).
  2. You cannot specify an h:panelGroup inside a CC without an ID, or the resulting div is not rendered on the page at all.
  3. The id= and rendered= are both ‘standard’ attributes of a CC that you can use by virtue of the fact that a CC is a jsf component – you don’t need to roll your own ‘display=’ attribute for example. This is helpful, as otherwise you would need to wrap a CC in a div in order to give it the CC naming container ID as in 1/, but you would also need to wrap it additionally in an actual h:panelGroup in order to roll your own display= ‘rendered’ style attribute. This may very well be the reason why you get an illegal argument exception if you try to specify your own rendered= attribute on a CC (as per this Mantis issue).
  4. cc.id is a built in reference for the CC’s declared ID without any naming container prefixes. cc.clientId is the full monty with all the prefixes. These are analagous to component.id and component.clientId which can be used for any JSF component. Sadly I have not seen any full documentation for the cc or component built in objects.

The second part of the IBM Developerworks article is here. It details how to add Ajax behaviour to a CC, and in particular how to add Ajax behaviour to a component inside a CC from outside. This is useful for less complex CCs, and partners with the other features like retargeting which allows you to pass a listener to a CC which is attached to a component inside the CC.

For more complex CCs, I tend to take a different approach, as follows:-

  • I code a controller class in Java for use with the CC, and pass it in as an attribute of the CC (typically controller=). Any behaviour of components inside the CC such as Ajax and listeners are all handled by this controller bean.
  • The controller bean is injected by CDI as an @dependent bean, and is typically injected into the page bean which acts as controller for the page. In this way, the lifecycle of the controller is tied to that of its containing bean, which is clear and exactly as it should be.
  • As the controller is a dependent bean, CDI allows it to be generic and to use parameterised types (you can only do this for a dependent bean). For example, I have my own breadcrumb control CC (see here) which has its own controller class. This accepts a list of crumbs, where a crumb is a parameterised type for the class.
  • A page may have multiple instances of such a CC, in which case it just injects multiple controllers, one per CC that it manages.
  • In order to provide event handling, I take a simple approach. A controller just has an event interface which it calls out to in order to pass on any relevant events such as listener or action events. Typically, these will mimic the interface for a listener or action and just pass it on. I generally add an additional argument to the start of all such calls, containing a reference to the controller itself (i.e. the controller passes this in the argument). This is useful in cases where for example the containing page bean itself implements the event interface and handles the events, as where there are multiple components/controllers on a page this argument can be used to determine which one the event was for.
  • A key concept with this technique, which works better for more complex cases, is that I am not just exposing internal component behaviour of components in the CC to the outside as you might using JSF retargeting for example. Rather, I am using the controller to provide its own abstraction of the internal behaviour and to provide an external interface which reflects its overall function. In other words, I can map the internal behaviour to the external interface in any way I choose, which gives me much greater control and design flexibility.
  • Another approach would be to use a JSF CC Backing Component, as detailed in Core Javaserver Faces on p373. This allows Java behaviour to be added to a CC transparently to the CC’s clients. However, it does involve using some of the JSF internal interfaces that would also be used to develop full blown custom components. In that sense it provides a halfway house to a full custom component implementation. I have deliberately not used this approach in my use cases – the ‘controller’ approach I have outlined is simple and clear, and easy to develop. The controller is not hidden, being declared as a dependent by a calling backing bean, and being passed in to the CC on the facelet page. I feel that in my use cases the visibility, clear lifecycle control, and use of a separate controller instance per component on the page are a benefit and keep things clear and simple.  In design pattern terms, JSF uses the MVC pattern. The JSF components form the view, and my controllers and page beans form the controller. It could be said that a backing component for a CC is part of the view rather than part of the controller, as it uses internal JSF interfaces used by JSF components to implement the view part of the pattern.

No Comments »

April 4th, 2011
2:18 pm
How to obtain the current default directory in Java

Posted under Java
Tags ,

This simple line of code does it, by looking up the current directory (“.”) and fetching its canonical path :-

 

System.out.println(new File(".").getCanonicalPath());

No Comments »

April 2nd, 2011
5:50 pm
Updating components such as JSF, JPA in Glassfish

Posted under Glassfish
Tags , , , , ,

Update 04/10/2011

An essential step was omitted below, which is to clear out the OSGI cache. If you do not do this, and for example manually update Mojarra, the old version will still be loaded from the OSGI cache so your update will not take effect (and probably leave you wondering where glassfish can possibly still be getting the old version from!)

The simple solution is to ensure that you clear the OSGI cache after an update. The cache will be automatically rebuilt by Glassfish.

Simply delete all files in the  <glassfish-root>/glassfish/domains/domain1/osgi-cache/ folder. (Typically this just has a felix  subfolder to be deleted).

This solves the problem.

 

Original

Glassfish has its updatetool which automatically lists and installs updates from known repositories. You can add to the list of repositories, but they have to be of the type that Glassfish will accept – it will not install from any old download site.

When I wanted to update Mojarra in GF 3.0.1 from 2.0.2 FCS to 2.0.4 FCS with the updatetool, I could not find an update site with that version on.

The alternative (which I did) is to update manually – in this case to stop Glassfish and copy the jsf-api.jar and jsf-impl.jar into the <glassfish-root>\glassfish\modules directory, then restart. The only issue with this is that the new version will not be correctly listed in the installed components under the update tool or in the glassfish administration if you do it manually. However, you can verify that the correct version is running by starting glassfish and one of your web apps running in it, then look in the log for Mojarra and you will see that Mojarra logs its version when it loads. (Be careful if using Notepad++ – I found that its search was unreliable when searching backwards in the log for Mojarra, and it missed the relevant matches.

This can also be done for JPA/Eclipselink. However, the issue with this is that in the update tool it is listed with the name Glassfish JPA, with a different version number to the one used for Eclipselink. It is therefore not possible to work out which version of Eclipselink you are upgrading to. Some of the forums suggest just copying in a later set of Eclipselink OSGI jars but this just does not feel right for OSGI!

The only think I can be certain of is the installed version of Eclipselink with a base version of Glassfish, as this is documented as part of the Glassfish release.

At present I am sticking with the Eclipselink 2.0.2 version that ships with Glassfish, as I have not had any problems with this.

No Comments »

March 28th, 2011
11:02 am
Eclipselink error in Eclipse–Schema “null” cannot be resolved….

Posted under JPA
Tags , , ,

This error came up while building a new development environment with the Eclipse Helios SR2 and Eclipselink 2.2.0.
The error appeared for every JPA entity, even after the database drivers/connections were correctly defined.

The simple answer  as detailed here is to right click the project and do a validate – hey presto, all the errors disappear!
This appears to have been around a while and is still not fixed in Eclipselink 2.2.0, but at least it is simple to resolve.

The tip was found on this Oracle tutorial post :-

Note:

If you encounter a Schema “null” cannot be resolved for table “<TABLE_NAME>” error, you’ve hit a known Eclipse bug.
To resolve the issue, right-click the project and choose select Validate from menu. Eclipse will clear the errors.

No Comments »

March 25th, 2011
6:43 pm
Creating Javascript Objects using new – the pros and cons

Posted under Javascript
Tags , ,

There are a number of pros and cons regarding this practice, and they are debated on StackOverflow here. A w3schools tutorial on it may be found here.

I have used this e.g. here, following the lead of the Primefaces dialog.js. A downside is that your code fails if you forget the new and just call the function instead. However, this can be detected and an error thrown as per the StackOverflow post. In my case, the thrown error was not easily detected so I elected to pop an alert as well.

I do not discuss the issue further here – just follow the links and you will find plenty of information on it.

No Comments »

March 25th, 2011
6:42 pm
Implementing confirmation dialogs with Primefaces p:dialog

Posted under JSF
Tags , , , , ,

Update 7/3/2012

Primefaces p:ajax partial refreshes can also be blocked by returning false to the onclick event.  In this case, an If statement needs to be used, so that “return false” is done to block the refresh, but no return at all is done to allow the refresh. This allows the Primefaces ppr logic to execute – returning true explicitly in the onclick actually stops the ppr/ajax logic from executing at all:-

onclick="if (!confirmAction(Arguments)) {return false};"

The nice feature of this method is that it works in an identical fashion for both full page and partial refreshes, so that is is not necessary to mess with logic which couples both the onstart and onclick attributes based on the ajax setting.

The downside is that as before we are adding bare Javascript logic into an element attribute. In defence, this situation has already been forced on us by the JSF/Primefaces design – the interface already requires us to return false to block a request, so whilst this might be distasteful from a purist Javascript point of view, it is a fact of life we have to live with.

 

Original Post

Confirmation dialogs are typically used when for example a form is being edited and a navigation to another page is attempted.

Traditionally, this might be done by using the Javascript confirm function which pops a browser dialog, and returns true or false depending on the user choice. However, modern web apps shy away from using browser popup dialogs. Their styling is browser dependant and they cannot be restyled. A modern app will use custom styled dialogs with features like fade in/out and lightboxing to give a more subtle appearance and to allow the dialog to be displayed in-page to avoid popup blocking problems. Also, component libraries such as Primefaces allow custom themes and theme switching for an application, which would therefore allow their own custom dialogs to be dynamically restyled.

The fundamental complication when using such a custom dialog is that unlike the built-in Javascript confirm function, it does not operate synchronously. This immediately raises major issues when using it to confirm navigation, as it cannot be used inline in a navigation confirmation event on a button or link.  This post looks at how to get around this issue and use a Primefaces p:dialog to implement a confirmation dialog.

 

Allowing or blocking navigation attempts

The first point to address is how we can actually allow or block a navigation attempt on a Primefaces/JSF button or link. There are two options available, which are straightforward and applicable to all buttons and links:-

  • For full page refreshes, returning false from the onclick event aborts the navigation. This is a standard JSF feature which is described here. It is also mentioned in Core Javaserver Faces Edition III on p573, although it must be said that it is rather hidden near the back of the book and hard to dig out.
  • For Primefaces p:ajax partial refreshes, returning false from the onstart event aborts the ajax call.

Note that in both cases, it is important to prefix the function call with the return statement, otherwise the function return value is not correctly returned by the event call. For example, using the traditional Javascript confirm function:-

  • onclick=”return confirm(‘Pending edits will be cancelled, do you want to continue?’);”
  • onstart=”return confirm(‘Pending edits will be cancelled, do you want to continue?’);”

When applying a default case, it is also possible to just return true or false without calling anything, e.g. using onclick=”return true;”

 

Using the p:dialog  to allow/block the navigation

This is a little tricky to get right, but actually turns out to be fairly straightforward, and in particular, we can still initiate the whole process from a single function call in the onclick or onstart event as above. The idea makes use of the fact that we can issue a soft click on an HTML (or in this case jQuery) element using the .click() call. The steps are as follows :-

  1. The user clicks on a button, which causes our own Javascript confirm(elementId) function to be called by say the onstart event. Importantly, the clientId of the target element which was clicked is passed and stored, as it may be needed later.
  2. if edit mode is inactive, the function just returns true to allow the action.
  3. If edit mode is active, then the function sets an internal confirmInProgress flag and shows the confirmation p:dialog via its widget show() function. The function then returns false to (initially) block the Ajax action.
  4. When the dialog is displayed and the user clicks No, a Javascript cancelAction() function is called which hides the dialog and clears the confirmInProgress flag. The Ajax action is not then performed.
  5. When the dialog is displayed and the user clicks Yes, a Javascript performAction() function is called which hides the dialog and forces a second (software) click on the button, using the previously stored elementId. This results in confirm() being called a second time.
  6. When confirm() is called the second time via the software click (because the user clicked yes to confirm), we can detect this as the confirmInProgress flag will be set. The function then clears the confirmInProgress flag and returns true to allow the Ajax action to proceed.

 

Implementation Points

  • When implementing this, my confirmation dialog is a facelets custom tag. Within the tag, I create a Javascript object (whose precise name is passed by the caller) which contains the above logic and manages the confirmation process.
  • The Javascript object is created via the new keyword – see my other post here with links on this topic.
  • I then pass the confirm function call used on this object as a text attribute value to any tags, buttons or links that need to implement confirmation. For a high level tag I would pass it as an onNavigate attribute, and within the tag it would be passed on to any links or buttons to which it applies.
  • In order to do the software click, the clientId of the element that was clicked is passed to the confirm function and stored on the Javascript object. To do this, the confirm function call is passed as a MessageFormat style string with a placeholder which will take the clientId, as this must be added by the target button typically via a reference to #{component.clientId}. In this way the final target could add additional parameters if required, but is nicely decoupled from the actual Javascript call. The only interface contract is the placeholder parameter index, and the fact that a boolean must be returned to permit/deny navigation. I typically add MessageFormat.format (in addition to string concatenation) as custom EL java functions declared in a tag library – in the example below, these functions have the el: prefix.
  • In our case, the actual element clientId is a constant string as far as the Javascript call is concerned, so it must have single quotes added. These are added in the context of the target element – they are not passed in the MessageFormat string. This way, the caller does not decide whether or not a parameter is a Javascript String – this is decided in the target element environment. For example, if an additional argument such as a JavaScript reference such as this was passed in the target element environment, it would not need to be quoted, and the caller’s MessageFormat string containing the Javascript call would not have to know. This maintains good decoupling.
  • The Javascript object also has an internal confirmMode property which indicates whether confirmation mode is actually on, i.e. if edit mode is active. If not, it just permits everything as per the above logic steps. This JS property would normally be set from an edit mode bean property on the actual edit form which triggers the confirm. To do this, I pass the JS property reference in to the tag handling the edit form. and it calls a standard utility tag to issue an inline script statement to assign the property reference from the bean. This assignment needs to be within a div which is updated by ajax when the edit mode changes, to ensure the property is reassigned.

 

Example Code Fragments

Facelets Page declaring the Confirmation Dialog

<util2:confirmActionDialog tagId="confirmDialog" widgetVar="confirmWidget" />           

<cheep:campaignTreeBrowser id="campaignTreeBrowser" idPath="#{form}" currentNodeChange="#{form}:pnlCampaignDetail"
                           currentNodeChangeOnComplete="ss_PanelExpand(widgetVarPnlCampaignDetail)"
                           controller="#{campaignsPage.campaignTreeBrowser}" edit="#{true}"
                           onNavigate="confirmWidget.confirm({0})"/>
                                                             
<cheep:campaignDetailPanel tagId="pnlCampaignDetail" idPath="#{form}" editModeFlag="confirmWidget.confirmMode"
                           controller="#{campaignsPage.campaignDetailPanel}"/>

confirmActionDialog.xhtml custom tag

<!DOCTYPE HTML>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:c="http://java.sun.com/jsp/jstl/core"   
xmlns:p="http://primefaces.prime.com.tr/ui"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:fn="http://java.sun.com/jsp/jstl/functions"
xmlns:util="http://java.sun.com/jsf/composite/uk.co.salientsoft/util">

<ui:composition>
    <h:outputScript library="uk.co.salientsoft/util" name="confirmActionDialog.js" target="head"/>
       
    <script type="text/javascript">#{widgetVar} = new uk.co.salientsoft.ConfirmActionDialog();</script>   
   
    <p:confirmDialog id="#{tagId}" widgetVar="#{widgetVar}.dialogWidget" styleClass="ss-confirmaction-dialog"
                message="#{empty confirmMessage ? mainMsg.confirmEditCancel : confirmMessage}"
                showEffect="fade" hideEffect="fade"
                header="#{empty confirmTitle ? mainMsg.confirmEditCancelTitle : confirmTitle}" severity="alert">
               
        <util:iconTextButton id="cmdAbort"  image="ui-icon ui-icon-close"
                label="#{mainMsg.optionNo}" title="#{mainMsg.optionTitleNo}" onclick="#{widgetVar}.cancelAction()" />                                                    
       
        <util:iconTextButton id="cmdConfirm"  image="ui-icon ui-icon-check" 
                label="#{mainMsg.optionYes}" title="#{mainMsg.optionTitleYes}" onclick="#{widgetVar}.performAction()" />
    </p:confirmDialog>
</ui:composition>
</html>

Setting the EditMode flag in the campaignDetailPanel edit form

<!– Set the edit mode state in the specified Javascript flag used by the navigation confirmation dialog when in edit mode –>
<util2:setScriptVar name="#{editModeFlag}" value="#{controller.editMode}" defaultValue="false" />

Custom tag util2:setScriptVar

<!DOCTYPE HTML>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core"   
xmlns:el="http://salientsoft.co.uk/EL">

    <ui:composition>   
        <!– Set the specified Javascript variable/property from the given EL expression –>
        <c:if test="#{not empty name}">
            <script type="text/javascript">#{name} = #{empty value ? defaultValue : value};</script>
        </c:if>
    </ui:composition>
</html>

Code within a link which adds the actual onstart event (this example is taken from a composite component)

<p:commandLink id="link" ajax="#{cc.attrs.ajax}" async="#{cc.attrs.async}" disabled="#{cc.attrs.disabled}"
         action="#{cc.attrs.controller.iconButtonAction(cc.attrs.eventHandler, cc.attrs.actionMethod,
                              cc.attrs.actionParam, cc.attrs.passController, cc.attrs.passActionParam)}"
         actionListener="#{cc.attrs.controller.iconButtonActionListener}"
         process="#{cc.attrs.process}" update="#{cc.attrs.update}" immediate="#{cc.attrs.immediate}"
         href="#{cc.attrs.href}" title="#{cc.attrs.title}" tabindex="#{cc.attrs.tabIndex}"
        onstart="return #{empty cc.attrs.onNavigate ? ‘true’ : el:format1(cc.attrs.onNavigate, el:concat3(‘\”, component.clientId, ‘\”))}"
         oncomplete="#{cc.attrs.oncomplete}" onsuccess="#{cc.attrs.onsuccess}" onerror="#{cc.attrs.onerror}">
…   

</p:commandLink>

Javascript object ConfirmActionDialog.js

/*
* This object handles the logic for confirming Primefaces Ajax actions via a confirmation dialog.
* It is typically used to pop a confirmation when in edit mode on a form and e.g. a navigation link/button has been clicked.
* A navigation link/button should call confirm() in its onstart Ajax event. The following then happens:-
*
* 1/ if EditMode is inactive, confirm() just returns true to allow the action.
* 2/ If EditMode is active, then confirm() pops the confirm dialog and returns false to (initially) block the Ajax action.
* 3/ When the dialog is popped and the user clicks No, hide the dialog and return to the idle state (Ajax action not performed).
* 4/ When the dialog is popped and the user clicks Yes, we hide the dialog and force a software click on the Ajax link/button.
* 5/ When confirm() is called the second time via the software click (because the user clicked yes to confirm),
*    return to the idle state and return true to allow the Ajax action.
*/

var uk=uk||{}; uk.co=uk.co||{}; uk.co.salientsoft=uk.co.salientsoft||{};
uk.co.salientsoft.ConfirmActionDialog = function () {
    this.confirmInProgress = false; //initialise to the idle state
    this.confirmMode = false; //set no confirmation needed initially (e.g. not in edit mode)
};

uk.co.salientsoft.ConfirmActionDialog.prototype.show = function() {
    this.dialogWidget.show();
};

uk.co.salientsoft.ConfirmActionDialog.prototype.hide = function() {
    this.dialogWidget.hide();
};

uk.co.salientsoft.ConfirmActionDialog.prototype.confirm = function(elementId) {
    if (this.confirmMode && !this.confirmInProgress) {
        var escapedId = ‘#’ + elementId.replace(/:/g, ‘\\3A ‘);
        this.element = jQuery(escapedId);
        this.dialogWidget.show();
        this.confirmInProgress = true;
        return false; //first time in, cancel the Primefaces Ajax action until confirmed
    }
    else {
        /*
         * Either we are not in confirm mode (e.g. edit mode is off),
         * or we are in confirm mode and this is the second time in, called as a result of a confirm.
         * Either way we allow the Primefaces Ajax action, and return to the idle state.
         */
        this.confirmInProgress = false;
        return true;
    }
};

uk.co.salientsoft.ConfirmActionDialog.prototype.performAction = function() {  
    this.dialogWidget.hide();
    this.element.click(); //This will cause confirm() to be called again; this time the Ajax action will be allowed.
};

uk.co.salientsoft.ConfirmActionDialog.prototype.cancelAction = function() {  
    this.dialogWidget.hide();
    this.confirmInProgress = false; //we will not be performing the Ajax action, so return to the idle state
};

No Comments »

March 24th, 2011
6:41 pm
Escaping quotes and other characters in JSF Expression Language

Posted under JSF
Tags , , ,

Some quick points on this are as follows:-

  • If you are using double quotes at the out level e.g. for HTML attributes, use single quotes inside the EL expressions
  • If you need quotes inside the EL, escape them with backslash as per normal Java standards

An example of an attribute containing EL with escaped quotes follows (note that the blog post may mess up the quote appearance slightly) :-

onstart="return #{empty cc.attrs.onstart ? ‘true’ : el:format1(cc.attrs.onstart, el:concat3(‘\”, component.clientId, ‘\”))}"

No Comments »

March 23rd, 2011
4:08 pm
Mapping Browser Keyboard events to Element Clicks

Posted under Javascript
Tags , , ,

I wanted to do this as a way to add keyboard accelerators, mainly for enter and escape when doing editing etc.

The code was already implemented to be driven from mouse click events, so causing keyboard events to soft click particular target buttons for save and cancel was convenient as this was then completely isolated from the rest of the code, which only had to be concerned with being button driven.

I found the following issues during the implementation:-

A keypress event was fine for trapping Enter, but it did not trap escape correctly. For escape, I needed to drive from the keyup event.

My enter event was bubbling up and causing unwanted navigation. I initially tried cancelling bubbling and propagation as detailed here on Quirksmode.org:-

function doSomething(e)
{
    if (!e) var e = window.event;
    e.cancelBubble = true;
    if (e.stopPropagation) e.stopPropagation();
}

This involves catering for browser differences , as shown, as the Microsoft/IE model uses cancelBubble, whereas the W3C model uses stopPropagation. In my case, this did not solve the issue.

My simple solution was for the event handler to return false when it had trapped and handled a keyclick such as enter, and to return true otherwise. Returning false blocked any further action on the event, and was also a standard mechanism. I did not bottom out the actual problem in my case, but this solved it completely.

When implementing the solution, I used the following technique:-

  1. An object literal was created which did a reverse map from all the key codes I might want to trap, to a mnemonic name for them (such as enter, escape, leftArrow).
  2. My event handler call accepts a keyActions object literal argument where the names are the above mnemonics, and the values are the element IDs which need to be clicked when the matching key code is detected.
  3. In the event handler, I reverse lookup the key code to the mnemonic, and then look up the element Id in the passed object literal using the mnemonic.
  4. If I find an element Id, I click it and return false. If I do not find one, I return true as I am not trapping that key.
  5. I also try a lookup on the passed keyActions object using the raw key code. This means that the caller can either use a mnemonic or a key code to specify what key is handled.
  6. The event handler therefore has very little code, and does not need any loops – it just does 2 associative lookups to get the target element to click.
  7. As an additional feature, I allow an idPath key/value to be passed in keyActions. If this is present, any element value which is not prefixed with ‘#’ will be prefixed with the id path, to simplify the call site where a number of elements with a common ID prefix are specified. Elements already prefixed with ‘#’ are left untouched.

This technique would also work well when implementing global key handling attached to the <body> tag. In this case, it may be desirable to switch the global handling depending on what is open on the page. For example, there might be an in-page detail form which is opened in-place and client side using a Primefaces p:panel. In this case, the keyActions object could be switched when the detail panel is opened, and switched back when it is closed.

Another useful trick if the complexity warrants it could be to implement a stack such that detail panels on the page push their own keyActions onto the stack, perhaps inheriting the global ones and modifying them as required. When a detail level is closed, it can just pop the stack and does not need to know about the higher levels of actions on the page. To assist with this, it would be useful to be able to clone the previous level actions when adding a new level. Prototypes could be used for this, but they need constructor functions etc. and do not map well to our simple case which just uses JSON like object literals. The jQuery extend method could be a good choice for this, and is detailed here on StackOverflow by none other than Mr jQuery himself, John Resig (!)

The following example code fragment from a utility object illustrates the idea:-

var uk=uk||{}; uk.co=uk.co||{}; uk.co.salientsoft=uk.co.salientsoft||{};
uk.co.salientsoft.util= {
    clearElement: function(elementId) {
        jQuery(uk.co.salientsoft.util.escapeId(elementId)).val(”);
    },
    escapeId: function(elementId) {
        //a value tests true if it is non-null, non-blank, not NAN, not undefined, and not false
        var escapedId = (elementId) ? ((elementId.charAt(0) == ‘#’ ? ” : ‘#’)  + elementId.replace(/:/g, ‘\\3A ‘)) : ”;
        return escapedId;
    },
    keyCodeNames: {
        /*
         * Common list of keycodes, allowing reverse lookup of name from code.
         * Derived from: http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes
         * This only includes the common function/special keys which should be browser independant.
         */
        8:’backspace’,
        9:’tab’,
        13:’enter’,
        16:’shift’,
        17:’ctrl’,
        18:’alt’,
        27:’escape’,
        33:’pageUp’,
        34:’pageDown’,
        35:’end’,
        36:’home’,
        37:’leftArrow’,
        38:’upArrow’,
        39:’rightArrow’,
        40:’downArrow’,
        45:’insert’,
        46:’delete’,
        112:’f1′,
        113:’f2′,
        114:’f3′,
        115:’f4′,
        116:’f5′,
        117:’f6′,
        118:’f7′,
        119:’f8′,
        120:’f9′,
        121:’f10′,
        122:’f11′,
        123:’f12’       
    },
    clickOnKey: function(event, keyActions) {
        /*
         * This is a keyup/keydown/keypress event handler.
         * keyActions is an object literal containing key mnemonics for the keys, and target elements for the values.
         * As well as mnemonics, raw key code numbers can be used.
         * A special object literal key, idPath, may be used to specify a default ID prefix.
         * If present, it is applied to all element IDs which are not prefixed with a ‘#’. 
         * Examples:-
         *    onkeypress="return uk.co.salientsoft.util.clickOnKey(event, {enter:’frm1:cmdSaveNew:link’});"
         *    onkeyup="return uk.co.salientsoft.util.clickOnKey(event, {escape:’cmdCancelNew:link’, idPath:’frm1:’});"
         *
         * NOTE – escape handling does not work on a keypress event.
         * The event is detected, but the click fails to do anything.
         * onkeyup should be used for escape, as this does not suffer from the problem.
         */
        var keycode = (event.keyCode ? event.keyCode : event.which);
       
        /*
         * Try to fetch the target element id from the passed keyActions assuming a string key code name was used, e.g. ‘enter’
         * If this fails, try a lookup based on the raw key code.
         * Doing it this way avoids looping.
         */       
        var actionElement = keyActions[uk.co.salientsoft.util.keyCodeNames[keycode]];
        if (actionElement == null) {
            actionElement = keyActions[keycode];
        }       
       
        /* If found a target for the event keycode, click it
         * and return false to stop any propagation, else return true
         */
        if (actionElement != null) {
            if (actionElement.charAt(0) != ‘#’ && keyActions.idPath != null) {
                actionElement = keyActions.idPath + actionElement;
            }
            jQuery(uk.co.salientsoft.util.escapeId(actionElement)).click();
            return false;
        }
        return true;
    }
};

No Comments »

March 23rd, 2011
3:24 pm
Java Generics–Class literals as run time type tokens

Posted under Java
Tags , , , ,

This post refers to the section on page 16 of the Generics tutorial document by Gilad Bracha.

The example here uses the Cojen dynamic bytecode generation library in order to create custom sort comparators. The advantage of Cojen is that you end up with a fully compiled comparator and therefore do not incur the performance hit of reflective code. Furthermore, comparators generated by Cojen can be cached in a map so that they are only generated once. This particular example borrows some code originally used for Icefaces table sorting, which I adapted for use with Primefaces. Whilst Primefaces does have its own table sorting, I had some issues with it. I wanted visibility of the current sort column and direction, and I wanted control over the sorting – I wanted to reapply the sort to a table when rows were added to it from another table, so that it was always maintained in order. Furthermore, I wanted to maintain the current sort column and direction that had been selected by the user via the column headers when applying the sort when new rows were added. As I had trodden this route before for Icefaces, it was straightforward to move the code over and incorporate it into a standard table controller class which I was already developing.

One issue which I had not previously bottomed out in the Icefaces version was making the code fully generic and fully eliminating all the generic warnings when using a class literal as a run time type token. With Cojen, you call a static forClass method on its BeanComparator to construct a comparator, passing in the class and the comparator ordering details:-

if (beanComparator == null) {
    beanComparator = BeanComparator.forClass(rowClass).orderBy(key);               
    comparatorMap.put(key, beanComparator);
}   

In my first Icefaces attempt, I pulled the first row out of the current row list for the table, and passed that to Cojen to create the class. The problem with this was that I could not do it generically this way. My second attempt, with the Primefaces version, passed the class in to an init method generically. As this tied up all the generics loose ends correctly, everything was happy and the class was recognised as having the correct parameterised type. The cost of this was the need to pass it explicitly as an argument to an init method (or a setter), but this was simple and made the parameterised type clear and explicit. This is the correct approach and one that I will use from now on.

The following code fragments illustrate the mechanism:-

Call Site


    private @Inject TableCtrl<Person, RowMetadata> personTable;

        personTable.init(Person.class, RowMetadata.class, this);

 

TableCtrlImpl.java

@Dependent
public class TableCtrlImpl<C, D extends RowMetadata> implements Serializable, TableCtrl<C, D>, Iterable<C> {

    private Class<C> rowClass;

    private Map<String, BeanComparator<C>> comparatorMap = new HashMap<String, BeanComparator<C>>();

@Override public void init(Class<C> rowClass, Class<D> rowMetadataClass, TableCtrlEvent<C, D> tableCtrlEvent) {       
    rowMeta = new RowMetaMap<C, D>();
    this.rowClass = rowClass;
    this.rowMetadataClass = rowMetadataClass;
    this.tableCtrlEvent = tableCtrlEvent;
}

    @Override public void sort() {
        if (sortColumn != null && !rows.isEmpty()) {
            String key = (sortAscending ? “” : “-“) + sortColumn;
            BeanComparator<C> beanComparator = comparatorMap.get(key);
            if (beanComparator == null) {
                beanComparator = BeanComparator.forClass(rowClass).orderBy(key);               
                comparatorMap.put(key, beanComparator);
            }
           
            Collections.sort(rows, beanComparator);
        }
    }

No Comments »