Design of RichFaces <a4j:queue/>

Overview

RichFaces team is going to develop a new component that will take ajax event queues to the next level.  This wiki page will contain the  design and usability details of the component. Here the current design decisions, functionality, and any open issues are described.

 

The community is encouraged to participate in the discussions, we want to hear what you think and how you will use such a component.  The Design of RichFaces forum will be used for general discussions on the <a4j:queue/>.

 

Below are the current forum topics related to the component:

NEW 4.0 related pages

All the changes proposed to be implemented in 4.x placed in separate 4.x section at the bottom of this document. Basic functionality proposed to be implemented in the same way.

Request generation RFC (Initially approved and awaiting for confirmation)

 

And here are some of the related Jira Issues:

  • RF-3131 - Implement properly requests queue for a AJAX submit.
  • RF-4387 - Event queues for groups of components
  • RF-4328 - Give default values to eventsQueue/requestDelay/ignoreDupResponses

 

Introduction

Tag name: <a4j:queue/>

 

With this tag users will be able to create queues that various components can reference and use.  There is a common set of options that can be set to govern the queue.  Any Ajax4jsf or RichFaces component can then reference that queue as shown below, and its requests will be governed by that queue's settings.

 

There are two types of queues associated with the <a4j:queue/> component: named and default queues.  Named queues are defined with a specific name and will only be used when specifically referenced by a component.  Default queues are not defined using a name, and if enabled, will be used by all components not specifically referencing a different queue.

 

There is a "global" default queue that, if enabled, will be the default queue for all elements in the view.  Users can override the global queue at the form level.  This means that users will be able to define a queue that will be used by all components within a specific form if they are not specifically referencing a different queue.

 

How <a4j:queue/> Can Be Used

How to define a new named queue

You can define a new queue anywhere on the page like this:

 

<!-- Create a new named queue called fooQueue -->
<a4j:queue name="fooQueue" ... />

 

Named queues are view scoped. Thus if a named queue is defined as it is shown it could be referenced by any components within the view.

 

Also when defining a queue users can define any of the attributes as described in Settings and Properties section below.  All items that enter the queue will use these settings to govern their behavior. But action components could redefine some of the params on the component level and it will be used instead of a queue value.

 

How to reference a named queue

You can reference a named queue from any Ajax4JSF or RichFaces component that supports the "eventsQueue" attribute.  For a complete listing see the RichFace documentation.  Below there is an example of two components referencing a named queue.

 

<a4j:queue name="fooQueue" ... />
...
<h:inputText value="#{foo2.bar1}">
    <a:support id="onblur" event="onblur" reRender="bar21" eventsQueue="fooQueue"/>
</h:inputText>

<a4j:commandButton eventsQueue="fooQueue" ... >

 

Now all requests generated through the onblur of the inputText component, and clicking of the commandButton will be funneled through the fooQueue.

 

If you reference a named queue that does not exist a new named queue will be created with all default settings.

 

Creating a form-based default queue

Users will often be using queues within forms, but defining the "eventsQueue" attribute on every component within a form can be tedious work.  To avoid that you can create a default queue for a form ( overriding the global default queue ).

 

<h:form ... >
   <!-- note no name specified -->
   <a4j:queue ... /> 
   ...
</h:form>

 

or

 

<a4j:form ... >
   <!-- note no name specified -->
   <a4j:queue ... /> 
   ...
</a4j:form>

 

or if using <a4j:form/> - you can reference a named queue as the form's default

 

<a4j:form eventsQueue="fooQueue" ...>
   ...
</a4j:form>

 

OPEN ISSUE: Seems should not be a big deal. Nick should we plan this to next release or this implementation possible in current one?

 

All elements within the form that support eventsQueue will now use the default form queue defined.  However to maintain flexibility components can still reference other named queues like this:

 

<a4j:queue name="fooQueue" ... />
...
<h:form ... >
    <!-- default queue for the form is created -->
    <a4j:queue ... />
    <a4j:commandButton ... > <!-- uses the forms default queue -->
    <a4j:commandButton eventsQueue="fooQueue" > <!-- uses fooQueue -->

 

Enabling the Global queue

The global default queue is disabled by default.  This is because artificial serializing of all ajax requests on a page can significantly effect expected behavior.  To enable the global default queue on all views for a given application you must add the following context variable to the applications web.xml

 

<context-param>
   <param-name>org.richfaces.queue.global.enabled</param-name>
   <param-value>true</param-value>
</context-param>

 

NOTE: Next highlighted block will not be implemented in the first version. Awaiting for users feedback about this addition.

 

Also in order to provide default scope for implicit queues next additional context param colud be discussed:

<context-param>
   <param-name>org.richfaces.queue.global.scope</param-name>
   <param-value>//form or view(default)///</param-value>
</context-param>


It could be discussed to be used not only as a global queue scope but also if the command component defines a queue using eventQueue attribute and the queue could not be found.

 

You can also programmatically enable/disable the global queue on a view by view basis using the following:

 

<!-- enable the global queue even if the context variable is false -->
<a4j:queue name="org.richfaces.queue.global" disabled="false"... />

<!-- disable the global queue even if the context variable is true -->
<a4j:queue name="org.richfaces.queue.global" disabled="true"... />

Global view scoped queue could be also added by just definition of the queue without name specified. But in this case it should be placed anywhere outside the forms in order not to be recognized as a form based queue.

<!-- enable the global queue even if the context variable is false -->
<a4j:queue ... />
Adjusting the global queue's default settings

 

The global queue is created with all default values, to override those setting on a view by view basis you can add the following to your view.

 

<!-- any attributes you set here will override global queue's defaults for this view -->
<a4j:queue name="org.richfaces.queue.global" ... />

or just without name specified (but as it was mentioned it should be outside the
forms in order not to be recognized as a form queue)

<a4j:queue ... />

 

This is basically exactly the same as overriding the enabled/disabled attribute described above.

 

Settings and Properties

Both named and default queues support a set of common attributes that govern the behavior of all items in the queue.  All of these attributes can be set via specific values or using standard EL.

 

 

These attributes are:

  • disabled
  • size
  • sizeExceededBehavior
  • requestDelay
  • timeout
  • ignoreDupResponces
  • onsubmit
  • oncomplete
  • onbeforedomupdate
  • onsizeexceeded
  • onerror

 

disabled

boolean - true/false

This queue will not influence any requests. (The only case when the queues with the same names could be used - case

when only one of them is enabled)

 

size

The number of items allowed in the queue at one time ( -1 = unlimited, x = some number)

 

 

sizeExceededBehavior

 

Four oversize strategies will be available on the queues

  • dropNext (default) drops next request that should be fired
  • dropNew drops the incoming request
  • fireNext  immediately fires the next request in line to be fired
  • fireNew immediately fires the incoming request
    • fireExceedEvent(FUTURE) only  event fired. A user should take care about new events by himself, a request and a queue should be passed as a parameter to handler.

 

In all of these cases the onSizeExceeded callback will be triggered.

It should be fired after the request is generated, a request object and queue should be passed as parameters.

 

requestDelay

Defining requestDelay="1000" on the queue means that all the requests which fired using this queue will have this 1 sec delay.

 

timeout

The amount of time an item can be in the queue before the sent event will be aborted and dropped from the queue.

Standard behavior of the attribute : if the request is sent and response is not returned within the timeframe defined in this attribute - the request is aborted, and the next one is sent.

 

ADDITION https://jira.jboss.org/jira/browse/RF-5788

ignoreDupResponces

If true it abborts a request client processing if new similar request comes to queue. But new request waits till the previous response in order to prevent parallel requests.

 

status

defines status component which will be activated on any request from this queue.

 

onsubmit

Сalled on request sent. Not cancelable. (onsubmit on the form or on the component only could cancel the request.) request object passed as a parameter. queue object accessible using this.

 

oncomplete

 

fired after request completed.

in this event handler request object will be passed as a parameter. Thus queue will be accessible using request.queue. And the element  which was a source of the request will be available using "this".

 

onbeforedomupdate

 

fired after responce comes before DOM updates

 

NOTE: These event handlers which are defined in a queue should be fired together with handlers on the  components (the components handlers should not override queues ones).

Also local component events will be fired and the queue events will fired after them. E.g. component oncomplete and then queue oncomplete will be fired. also form onsubmit will be fired and then queue onsubmit will be fired after.

 

For example onsubmit handler of the queue should be fired  for all the requests within this queue. And if some support defines its own onsubmit it should be also fired after the queues one before the request sending (could be used for example for confirmation dialogs on some controls).

 

onsizeexceeded

definition in previous section.

 

onerror

fired on server side errors. params the same as for standard A4J onErorr handler.

 

onrequestqueue

fired after new request added to queue.

 

onrequestdequeue

fired after request removed from queue.

Component level overrides of queue settings

Components attributes defined with some other values - will redefine queue default values for this attributes.

Design Details

Default Queues
  • Only one default queue is ever active at one time for a given view or form. 
  • If ever more are detected a warning should appears in server console during rendering. All the same named queues after the first instance should be ignored.
  • default queue is created also for components which defines next ajax attributes: (this queue has a component scope)
    • requestDelay
    • ignoreDupResponce
  • default queue iscreated if defined in a component eveQueue attribute but not found in the view. it has a scope the same as defined in corresponding context param.

 

Global queues
  • Only one global queue will ever exist on a view
    • If a user defines more that one with this name while attempting to set its attributesa warning should appears in server console during rendering. All the same named queues after the first instance should be ignored.
  • The queue name will be "org.richfaces.queue.global"
    • As stated above this is the root for adjusting the enabled attribute in the web.xml.
Form queues
  • Only one ENABLED form based default queue can be active at one time. 
    • a warning should appears in server console during rendering if more than one enabled queue is present.  All the same named queues after the first instance should be ignored.
    • Users can define more than one form queue, however all but one must be disabled.
  • The form based queue will have a name, but this is really for internal purposes and not really something users will deal with.
    • The client names for form based queue will be org.richfaces.queue.form.<form_id>
Named Queues
  • Named queues must have a unique name, if a second queue with the same name is defined  All the same named queues after the first instance should be ignored.
  • NOTE: form elements used as naming container for the queue . So custom queue defined within the form could not be used by the components outside this concrete form.

 

OPEN ISSUE: I think this is a reasonable limitation, but is there a way to avoid this?  If not we need a very clean warning message to the user if this happens.

    Queue functionality

    Events Similarity

    By default all the events raised by the same component are similar to the queue (according to clientId of event source). This means that if new requests come from the same component it will be combined with the previous one. For example: if we use a4j:support on an input field and the user types frequently all the request raised by key up during requestDelay will be combined into one.

     

    Users can also manually specify multiple components which will produce similar requests. similarityGroupingId attribute will be added to all the ajax action components. Hence for example the user could add two a4j:support components to the input (one for key up and the second for blur) and define that request events are similar by specifying the same similarityGroupingId.

    Similar request during request delay

    As written above requests should be collected in the queue during requestDelay and similar ones should be combined.  But similar requests can only be combined if they are raised sequentially. This is done in order not to block the queue and not to change the requests order.

     

    Example:
    A request with some delay comes to queue(A) the delay counter for this request should be started. If similar request(e.g. from the same component - A') appears - these two requests should be combined(AA') and a counter should be reset.

     

    But if some not similar request comes to the queue(B) - it is placed after the first one(AA',B). And if the AA' request doesn't exit the queue and another request similar to A appears again - these requests should not be combined with the first one but should be placed after B. (AA', B, A'').

     

    Such behavior will allow us
    1) to maximize similar requests throughput
    2) to send only the latest fields state for similar requests
    3) not to block the queue if the different types of requests comes to queue and should wait one for another.

     

    And one more addition. Poll component will be defined as not delay-able (delay=0 specified on component by default) in order not use the queue delay(its own value for this param will redefine queue param) to avoid blocking periodical update in the queue. A user will be able to redefine this for sure on the component level if need.

    View Changes and requests waiting in queue

    Use Case: one request(from component A) sent and the other(from component B) waits in the queue. But after response from A comes it reRenders part of page where the B component located. We plan to implement two features in order to reduce the glitches in such situations.

     

    2.1) We will not collect the events in the queue only as it was before. We will generate the request info right after event and put the complete generated request to the queue. In this case actual form information will be sent with the request.
    2.2) We plan to introduce one more common attribute which should allow to define the components which should reset the queue (resetQueueAfterRequest).

     

    For example: we have a table with links in cells and pagginator for this table. All the components placed use the same queue. In this case we definitely should specify something like resetQueueAfterRequest="true" on the pagginator control in order to restrict links submits from the previous page after pagginator is switched to the table page.

    Priorities in queue

    Priorities will be introduced also within a new queue component. Priorities could be defined for any ajax action component using a new corresponding attribute. There should be three level of priorities "low", "normal" and "high". If the request with a "high" priority comes to the queue it will be placed before all the requests with lover priorities during request delay.

    OPEN ISSUE: I do not believe we should attempt this for the first version of the queue.  Let's get what we have defined in first.

    Ok kicked this from first version plans.

    OPEN ISSUE: If priorities are done they should be numeric for easy sorting, and to provide a larger array of options.

    Queue API

    First two API functions implemented for initial version! All other API methods are for FUTURE 

    The following functions will be available on the queue object:

    • <queueObject>.getSize
    • <queueObject>.getMaximumSize
    • <queueObject>.getEvents
    • <queueObject>.getEvent(i)
    • <queueObject>.removeEvent(i)
    • <queueObject>.removeAllEvents
    • <eventObject>.getEventSource()

    queue Object should be retrieved using #{rich:component('queueId')}

     

    RichFaces 4.x Queue RFC's

    1) Request generation way.

    Thanks to RonanKER for his suggestion

     

    Consider "View Changes and requests waiting in queue" section in this document. This requirement:

     

    2.1) We will not collect the events in the queue only as it was before. We will generate the request info right after event and put the complete generated request to the queue. In this case actual form information will be sent with the request.

    proposed to be changed  to

     

    We should collect just events in the queue, and generate the request before actuall sending. This will fix the synchronization problems like with some added/changed or removed by previous ajax request inputs from form. And  in this way the information sent with request will be more actuall in difference with former approach.

     

    This RFC initially confirmed. Just add your comments on it if any and we will change the implementation to use this approach.

    2) request which send using standard JSF Ajax api/behavior

         As we decided to serve the standard JSF Ajax requests through RichFaces client side mechanism we definitelly should perform such requests handling by richfaces queue. It will handle concurency problem for any requests risen. So, sending requests called using standard jsf API - should be performed using the same rules as for our internal ones.