9.12. Concurrent calls to conversational components
Section 6.1.9, “Concurrency model” contains a general discussion of concurrent calls to Seam components. This section discusses the most common situation in which you will encounter concurrency — when accessing conversational components from AJAX requests. This section also describes the options provided by an AJAX client library, and RichFaces, to control events originating from the client.
Conversational components do not allow true concurrent access, so Seam queues each request for serial processing. This allows each request to be executed in a deterministic fashion. However, there are some limitations to a simple queue. If a method, for whatever reason, takes a long time to complete, running it whenever the client generates a request can lead to Denial of Service attacks. AJAX is also often used to provide quick status updates to users, so continuing to run an action after a long time is not useful.
Therefore, when you work inside a long-running conversation, Seam queues the action even for a period of time (the concurrent request timeout). If Seam cannot process the event before timeout, it creates a temporary conversation and prints a message for the user, informing them of the timeout. It is therefore important not to flood the server with AJAX events.
You can set a sensible default for the concurrent request timeout (in milliseconds) in the
components.xml file:
<core:manager concurrent-request-timeout="500" />
The concurrent request timeout can also be adjusted on a page-by-page basis:
<page view-id="/book.xhtml" conversation-required="true" login-required="true" concurrent-request-timeout="2000" />
The above section discussed AJAX requests that appear serially. The client tells the server that an event has occurred, and then rerenders part of the page based on the result. This approach is sufficient when the AJAX request is lightweight (the methods called are simple, for example, calculating the sum of a column of numbers), but complex computations require a different approach.
A poll-based approach is where the client sends an AJAX request to the server, causing actions to begin immediate asynchronous execution on the server. The client then polls the server for updates while the actions are executed. This approach is useful when it is important that no action in a long-running action sequence times out.
9.12.1. How should we design our conversational AJAX application?
The first question is whether to use the simpler "serial" request method, or a polling approach.
If you want to use serial requests, you must estimate the time required for your request to complete. You may need to alter the concurrent request timeout for this page, as discussed in the previous section. A queue on the server side is probably necessary, to prevent requests from flooding the server. If the event occurs often (for example, a keystroke, or onblur of input fields) and immediate client update is not a priority, set a request delay on the client side. Remember to factor the possibility of server-side queuing into your request delay.
Finally, the client library may provide an option to abort unfinished duplicate requests in favor of the most recent requests.
A polling approach requires less fine-tuning — simply mark your action method
@Asynchronous and decide on a polling interval:
int total; // This method is called when an event occurs on the client // It takes a really long time to execute @Asynchronous public void calculateTotal() { total = someReallyComplicatedCalculation(); } // This method is called as the result of the poll // It's very quick to execute public int getTotal() { return total; }
9.12.2. Dealing with errors
However carefully you design your application to queue concurrent requests to your conversational component, there is a risk that the server may become overloaded. The overloaded server will be unable to process all the requests before the request has to wait longer than the
concurrent-request-timeout. In this case, Seam generates ConcurrentRequestTimeoutException that can be handled in the pages.xml file. We recommend sending an HTTP 503 error:
<exception class="org.jboss.seam.ConcurrentRequestTimeoutException" log-level="trace"> <http-error error-code="503" /> </exception>
Note
The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay.
Alternatively you could redirect to an error page:
<exception class="org.jboss.seam.ConcurrentRequestTimeoutException" log-level="trace"> <end-conversation/> <redirect view-id="/error.xhtml"> <message>The server is too busy to process your request, please try again later</message> </redirect> </exception>
Seam Remoting and JSF 2 can both handle HTTP error codes. Seam Remoting will pop-up a dialog box showing the HTTP error. JSF 2 provides support for handling HTTP errors by providing a user definable callback. For example, to show the error message to the user:
<script type="text/javascript"> jsf.ajax.addOnError(function(data) { alert("An error occurred"); }); </script>
Note
More details about JSF 2 JavaScript API can be seen at: http://javaserverfaces.java.net/nonav/docs/2.0/jsdocs/symbols/jsf.ajax.html
If instead of an error code, the server reports that the view has expired, perhaps because the session timed out, you can use a standard javax.faces.context.ExceptionHandler to handle this scenario.
9.12.3. RichFaces (Ajax4jsf)
RichFaces (Ajax4jsf) is the most common AJAX library used with Seam, and provides all the controls discussed in the previous section.
-
attachQueue - Customizes queuing for specific components and behaviors. It can override queue settings of a component or attach specific requests to a queue.
-
ignoreDupResponses - Ignores the response produced by a request if a more recent "similar" request is already queued.
ignoreDupResponses="true"does not cancel the processing of the request on the server side; it only prevents unnecessary updates on the client side.With Seam conversations, this option should be used with care, as it allows multiple concurrent requests. -
requestDelay - Defines the time in milliseconds for which the request remains on the queue. If, at this time, the request has not been processed, the request is either sent (regardless of whether a response has been received), or discarded (if there is a more recent "similar" event queued).With Seam conversations, this option should be used with care, as it allows multiple concurrent requests. The delay that you set (in combination with the concurrent request timeout) must be longer than the time the action will take to execute.
-
<a:poll render="total" interval="1000" /> - Polls the server and renders an area, as required.