JSF 2 AJAX/Submit issues with @ConversationScoped Beans

When working with ConversationScoped Beans and AJAX, you always have to be careful not to run into BusyConversationExceptions.

Lets consider the following scenario where the inputField triggers an ajax event when the value is changed:

<h:inputField value="#{conversationScopedBean.bar}" ...>
  <f:ajax event="change" render="@this" ... />
</h:inputfield>
...

<h:commandButton ... action="fooAction.proceed" />

When you change the input and click on the commandButton, without triggering the change event by exiting the field first, you will encounter a BusyConversationException. This happens because the click triggers the change event firing an AJAX request, immediately followed by the button’s submit.

Richfaces 4 a4j library offers a solution for this issue in the form of a4j:commandButton and a4j:commandLink (a4j:queue may also be of interest). If you are using RichFaces/a4j or are willing to add the required library I would strongly suggest that you use these instead of the described custom implementation.

As described, the issue occurs due to the two request hitting the server in short succession. The AJAX request hasn’t finished processing yet and so the submit runs into a BusyConversationException.

To avoid this we have to postpone the submit until the AJAX event is completed successfully.
This can be achieved by adding an onclick action to the commandButton that checks if an AJAX requests is waiting for a response.

<h:commandButton onclick="return deferSubmit(event)" ... />

The onclick action will have to return false if an AJAX request is pending which will stop the submit from being executed.

As I have previously demonstrated in my post about JSF 2 AJAX Element Focus, it is possible to register a listener on JSF ajax events.

var ajaxBusy = false;
jsf.ajax.addOnEvent(function(data) {
  var status = data.status;
  switch(status) {
   case "begin":
     ajaxBusy = true;
     break;
   case "complete":
     ajaxBusy = false;
     break;
   ...
  }  
}

Just as was the case with JSF 2 AJAX Element Focus we can use this to check if an AJAX request is currently pending.

Since JavaScript execution in browsers run single threaded, you cannot simply keep the submit executing thread in a busy wait until the response arrives. Otherwise the JSF AJAX event listener will never be triggered and the busy wait will never be informed that the response has arrived.

It is therefore necessary to release the thread with a return false (stops the submit) and store the submit for later execution. Whilst waiting for the response, further requests should be avoided, AJAX or otherwise. In this case I have decided to block the UI with the jQuery extension BlockUI by Mike Alsup.

function deferSubmit(event) {
  $.blockUI({message:'', overlayCSS:{opacity:0, cursur:'wait'}});

  if(ajaxBusy) {
    deferedSubmit = function() {
      var element = event.srcElement;
      element.click();
    };
    return false;
  } 
  return true;
}

The submit will be executed as soon as the JSF AJAX request is completed. It is not necessary to unblock the UI or reset the deferedSubmit function since the submit results in a new document overwriting all document client state.

...
var deferedSubmit = function() {};

jsf.ajax.addOnEvent(function(data) {
  var status = data.status;	
  switch (status) {
    case "begin":
      ajaxBusy = true;
      break;
    case "complete":
      // invoked after AJAX response is returned.
      ajaxBusy = false;
      deferedSubmit();
      break;
  }
});
...

You can find the complete JavaScript here.

Related resources:
JSR-314 JavaServer Faces 2.0 Final Release
Keeping focus on element with JSF 2 AJAX render

Acknowledgements

I would like to thank Björn Sonntag and Robert Meyer for reviewing my posts.

One thought on “JSF 2 AJAX/Submit issues with @ConversationScoped Beans”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.