Author: manaRH
Date: 2008-11-23 18:52:31 -0500 (Sun, 23 Nov 2008)
New Revision: 9611
Added:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/ConcurrentRequestTimeoutException.java
Modified:
branches/enterprise/JBPAPP_4_3_FP01/doc/Seam_Reference_Guide/en-US/Conversations.xml
branches/enterprise/JBPAPP_4_3_FP01/seam-gen/resources/WEB-INF/pages.xml
branches/enterprise/JBPAPP_4_3_FP01/seam-gen/view/layout/template.xhtml
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Conversation.java
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/ConversationEntry.java
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Manager.java
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/faces/FacesManager.java
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Page.java
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Pages.java
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.dtd
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.xsd
Log:
JBPAPP-1444
Modified:
branches/enterprise/JBPAPP_4_3_FP01/doc/Seam_Reference_Guide/en-US/Conversations.xml
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/doc/Seam_Reference_Guide/en-US/Conversations.xml 2008-11-21
20:06:57 UTC (rev 9610)
+++
branches/enterprise/JBPAPP_4_3_FP01/doc/Seam_Reference_Guide/en-US/Conversations.xml 2008-11-23
23:52:31 UTC (rev 9611)
@@ -1038,11 +1038,12 @@
</para>
<para>
- Therefore Seam queues the action event for a period of time (the
- concurrent request timeout); if it can't process the event in time, it
- creates a temporary conversation and prints out a message to the user to
- let them know what's going on. It's therefore very important not to
- flood the server with AJAX events!
+ Therefore, when you are working inside a long running conversation,
+ Seam queues the action event for a period of time (the concurrent
+ request timeout); if it can't process the event in time, it creates a
+ temporary conversation and prints out a message to the user to let them
+ know what's going on. It's therefore very important not to flood the
+ server with AJAX events!
</para>
<para>
@@ -1051,18 +1052,19 @@
</para>
<programlisting role="XML"><![CDATA[<core:manager
concurrent-request-timeout="500" />]]></programlisting>
- <!-- TODO -->
- <!-- <para>
- We can also fine tune the concurrent request timeout for a request:
+
+ <para>
+ We can also fine tune the concurrent request timeout on a page-by-page
+ basis:
</para>
- <programlisting role="JAVA"><![CDATA[public void getTotal() {
- Manager.instance().setConcurrentRequestTimeout(1000);
- return someReallyComplicatedCalculation();
-}]]></programlisting>-->
+ <programlisting role="XML"><![CDATA[<page
view-id="/book.xhtml"
+ conversation-required="true"
+ login-required="true"
+ concurrent-request-timeout="2000" />]]></programlisting>
<para>
- So far we've discussed "synchronous" AJAX requests - the client tells
the
+ So far we've discussed serial AJAX requests - the client tells the
server that an event has occur, and then rerenders part of the page based
on the result. This approach is great when the AJAX request is
lightweight (the methods called are simple e.g. calculating the sum of a
@@ -1078,9 +1080,8 @@
be dropped as duplicates, or to timeout).
</para>
- <para>
- <emphasis>How should we design our conversational AJAX
application?</emphasis>
- </para>
+ <section>
+ <title>How should we design our conversational AJAX
application?</title>
<para>
Well first, you need to decide whether you want to use the simpler
@@ -1128,61 +1129,118 @@
public int getTotal() {
return total;
}]]></programlisting>
-
- <section>
- <title>RichFaces Ajax</title>
-
- <para>
- RichFaces Ajax is the AJAX library most commonly used with Seam, and
- provides all the controls discussed above:
- </para>
+ </section>
- <itemizedlist>
- <listitem>
+ <section>
+ <title>Dealing with errors</title>
+
+ <para>
+ However carefully you design your application to queue concurrent
+ requests to your conversational component, there is a risk that the
+ server will become overloaded and be unable to process all the
+ requests before the request will have to wait longer than the
+ <literal>concurrent-request-timeout</literal>. In this case Seam
will
+ throw a <literal>ConcurrentRequestTimeoutException</literal> which
can
+ be handled in <literal>pages.xml</literal>. We recommend sending
an
+ HTTP 503 error:
+ </para>
+
+ <programlisting role="XML"><![CDATA[ <exception
class="org.jboss.seam.ConcurrentRequestTimeoutException"
logLevel="trace">
+ <http-error error-code="503" />
+ </exception>]]></programlisting>
+
+ <note>
+ <title>503 Service Unavailable (HTTP/1.1 RFC)</title>
+
<para>
- <literal>eventsQueue</literal> — provide a queue in
which
- events are placed. All events are queued and requests are sent to
- the server serially. This is useful if the request can to the
- server can take some time to execute (e.g. heavy computation,
- retrieving information from a slow source) as the server isn't
- flooded.
+ 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.
</para>
- </listitem>
- <listitem>
- <para>
- <literal>ignoreDupResponses</literal> — ignore the
response
- produced by the request if a more recent 'similar' request is
- already in the queue. ignoreDupResponses="true" does
<emphasis>not
- cancel</emphasis> the the processing of the request on the server
- side — just prevents unnecessary updates on the client side.
- </para>
- <para>
- This option should be used with care with Seam's conversations as
- it allows multiple concurrent requests to be made.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal>requestDelay</literal> — defines the time (in
ms.)
- that the request will be remain on the queue. If the request has
- not been processed by after this time the request will be sent
- (regardless of whether a response has been received) or discarded
- (if there is a more recent similar event on the queue).
- </para>
- <para>
- This option should be used with care with Seam's conversations as
- it allows multiple concurrent requests to be made. You need to be
- sure that the delay you set (in combination with the concurrent
- request timeout) is longer than the action will take to execute.
- </para>
- </listitem>
- <listitem>
- <para>
- <literal><a:poll reRender="total"
interval="1000" /></literal> —
- Polls the server, and rerenders an area as needed
- </para>
- </listitem>
- </itemizedlist>
- </section>
+ </note>
+
+ <para>
+ Alternatively you could redirect to an error page:
+ </para>
+
+ <programlisting role="XML"><![CDATA[<exception
class="org.jboss.seam.ConcurrentRequestTimeoutException"
logLevel="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>]]></programlisting>
+
+ <para>
+ ICEfaces, RichFaces Ajax and Seam Remoting can all handle HTTP error
+ codes. Seam Remoting will pop up a dialog box showing the HTTP error
+ and ICEfaces will indicate the error in it's connection status
+ component. RichFaces Ajax provides the most complete support for
+ handling HTTP errors by providing a user definable callback. For
+ example, to show the error message to the user:
+ </para>
+
+ <programlisting><![CDATA[<script
type="text/javascript">
+ A4J.AJAX.onError = function(req,status,message) {
+ alert("message");
+ };
+</script>]]></programlisting>
+ </section>
+
+ <section>
+ <title>RichFaces Ajax</title>
+
+ <para>
+ RichFaces Ajax is the AJAX library most commonly used with Seam, and
+ provides all the controls discussed above:
+ </para>
+
+ <itemizedlist>
+ <listitem>
+ <para>
+ <literal>eventsQueue</literal> — provide a queue in
which
+ events are placed. All events are queued and requests are sent to
+ the server serially. This is useful if the request can to the
+ server can take some time to execute (e.g. heavy computation,
+ retrieving information from a slow source) as the server isn't
+ flooded.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>ignoreDupResponses</literal> — ignore the
response
+ produced by the request if a more recent 'similar' request is
+ already in the queue. ignoreDupResponses="true" does
<emphasis>not
+ cancel</emphasis> the the processing of the request on the server
+ side — just prevents unnecessary updates on the client side.
+ </para>
+ <para>
+ This option should be used with care with Seam's conversations as
+ it allows multiple concurrent requests to be made.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal>requestDelay</literal> — defines the time
(in ms.)
+ that the request will be remain on the queue. If the request has
+ not been processed by after this time the request will be sent
+ (regardless of whether a response has been received) or discarded
+ (if there is a more recent similar event on the queue).
+ </para>
+ <para>
+ This option should be used with care with Seam's conversations as
+ it allows multiple concurrent requests to be made. You need to be
+ sure that the delay you set (in combination with the concurrent
+ request timeout) is longer than the action will take to execute.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <literal><a:poll reRender="total"
interval="1000" /></literal> —
+ Polls the server, and rerenders an area as needed
+ </para>
+ </listitem>
+ </itemizedlist>
+ </section>
</section>
</chapter>
\ No newline at end of file
Modified: branches/enterprise/JBPAPP_4_3_FP01/seam-gen/resources/WEB-INF/pages.xml
===================================================================
--- branches/enterprise/JBPAPP_4_3_FP01/seam-gen/resources/WEB-INF/pages.xml 2008-11-21
20:06:57 UTC (rev 9610)
+++ branches/enterprise/JBPAPP_4_3_FP01/seam-gen/resources/WEB-INF/pages.xml 2008-11-23
23:52:31 UTC (rev 9611)
@@ -50,6 +50,10 @@
<message>Your session has timed out, please try again</message>
</redirect>
</exception>
+
+ <exception class="org.jboss.seam.ConcurrentRequestTimeoutException"
logLevel="trace">
+ <http-error error-code="503" />
+ </exception>
<exception>
<redirect view-id="/error.xhtml">
Modified: branches/enterprise/JBPAPP_4_3_FP01/seam-gen/view/layout/template.xhtml
===================================================================
--- branches/enterprise/JBPAPP_4_3_FP01/seam-gen/view/layout/template.xhtml 2008-11-21
20:06:57 UTC (rev 9610)
+++ branches/enterprise/JBPAPP_4_3_FP01/seam-gen/view/layout/template.xhtml 2008-11-23
23:52:31 UTC (rev 9611)
@@ -9,6 +9,12 @@
<meta http-equiv="Content-Type" content="text/html;
charset=UTF-8" />
<title>@projectName@</title>
<link href="stylesheet/theme.css" rel="stylesheet"
type="text/css" />
+ <script type="text/javascript">
+ A4J.AJAX.onError = function(req,status,message) {
+ alert(message);
+
+ };
+ </script>
</head>
<body>
Added:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/ConcurrentRequestTimeoutException.java
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/ConcurrentRequestTimeoutException.java
(rev 0)
+++
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/ConcurrentRequestTimeoutException.java 2008-11-23
23:52:31 UTC (rev 9611)
@@ -0,0 +1,29 @@
+/**
+ *
+ */
+package org.jboss.seam;
+
+public class ConcurrentRequestTimeoutException extends RuntimeException
+{
+
+ public ConcurrentRequestTimeoutException()
+ {
+ super();
+ }
+
+ public ConcurrentRequestTimeoutException(String message, Throwable cause)
+ {
+ super(message, cause);
+ }
+
+ public ConcurrentRequestTimeoutException(String message)
+ {
+ super(message);
+ }
+
+ public ConcurrentRequestTimeoutException(Throwable cause)
+ {
+ super(cause);
+ }
+
+}
Property changes on:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/ConcurrentRequestTimeoutException.java
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Modified:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Conversation.java
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Conversation.java 2008-11-21
20:06:57 UTC (rev 9610)
+++
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Conversation.java 2008-11-23
23:52:31 UTC (rev 9611)
@@ -31,6 +31,7 @@
{
private static final long serialVersionUID = -6131304128727444876L;
private Integer timeout;
+ private Integer concurrentRequestTimeout;
String description;
String viewId;
@@ -54,6 +55,16 @@
this.timeout = timeout;
}
+ public Integer getConcurrentRequestTimeout()
+ {
+ return concurrentRequestTimeout == null ?
Manager.instance().getCurrentConversationConcurrentRequestTimeout() :
concurrentRequestTimeout;
+ }
+
+ public void setConcurrentRequestTimeout(Integer concurrentRequestTimeout)
+ {
+ this.concurrentRequestTimeout = concurrentRequestTimeout;
+ }
+
/**
* Get the conversation id.
*/
@@ -136,6 +147,11 @@
entry.setTimeout(timeout);
}
+ if (concurrentRequestTimeout != null)
+ {
+ entry.setConcurrentRequestTimeout(concurrentRequestTimeout);
+ }
+
description = null;
viewId = null;
timeout = null;
Modified:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/ConversationEntry.java
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/ConversationEntry.java 2008-11-21
20:06:57 UTC (rev 9610)
+++
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/ConversationEntry.java 2008-11-23
23:52:31 UTC (rev 9611)
@@ -25,6 +25,7 @@
private String viewId;
private List<String> conversationIdStack;
private Integer timeout;
+ private Integer concurrentRequestTimeout;
private boolean removeAfterRedirect;
private boolean ended;
@@ -176,6 +177,17 @@
this.timeout = conversationTimeout;
}
+ public Integer getConcurrentRequestTimeout()
+ {
+ return concurrentRequestTimeout == null ?
Manager.instance().getConcurrentRequestTimeout() : concurrentRequestTimeout;
+ }
+
+ void setConcurrentRequestTimeout(Integer concurrentRequestTimeout)
+ {
+ entries.setDirty(this.concurrentRequestTimeout, concurrentRequestTimeout);
+ this.concurrentRequestTimeout = concurrentRequestTimeout;
+ }
+
public boolean isRemoveAfterRedirect()
{
return removeAfterRedirect;
@@ -201,7 +213,7 @@
{
try
{
- return lock.tryLock( Manager.instance().getConcurrentRequestTimeout(),
TimeUnit.MILLISECONDS );
+ return lock.tryLock( getConcurrentRequestTimeout(), TimeUnit.MILLISECONDS );
}
catch (InterruptedException ie)
{
Modified: branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Manager.java
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Manager.java 2008-11-21
20:06:57 UTC (rev 9610)
+++
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/core/Manager.java 2008-11-23
23:52:31 UTC (rev 9611)
@@ -16,6 +16,7 @@
import java.util.Map;
import org.jboss.seam.Component;
+import org.jboss.seam.ConcurrentRequestTimeoutException;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Name;
@@ -196,6 +197,13 @@
return ce.getViewId();
}
+ public Integer getCurrentConversationConcurrentRequestTimeout()
+ {
+ ConversationEntry ce = getCurrentConversationEntry();
+ if (ce == null) return null;
+ return ce.getConcurrentRequestTimeout();
+ }
+
public String getParentConversationViewId()
{
ConversationEntry conversationEntry =
ConversationEntries.instance().getConversationEntry(getParentConversationId());
@@ -487,7 +495,15 @@
private boolean restoreAndLockConversation(ConversationEntry ce)
{
- if ( ce!=null && ce.lock() )
+ if ( ce == null )
+ {
+ //there was no id in either place, so there is no
+ //long-running conversation to restore
+ log.debug("No stored conversation");
+ initializeTemporaryConversation();
+ return false;
+ }
+ else if ( ce.lock() )
{
// do this ASAP, since there is a window where conversationTimeout() might
// try to destroy the conversation, even if he cannot obtain the lock!
@@ -511,11 +527,8 @@
}
else
{
- //there was no id in either place, so there is no
- //long-running conversation to restore
- log.debug("No stored conversation, or concurrent call to the stored
conversation");
- initializeTemporaryConversation();
- return false;
+ log.debug("Concurrent call to conversation");
+ throw new ConcurrentRequestTimeoutException("Concurrent call to
conversation");
}
}
Modified:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/faces/FacesManager.java
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/faces/FacesManager.java 2008-11-21
20:06:57 UTC (rev 9610)
+++
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/faces/FacesManager.java 2008-11-23
23:52:31 UTC (rev 9611)
@@ -271,7 +271,7 @@
{
conversation.setDescription( pageEntry.renderDescription() );
}
- conversation.setTimeout( pages.getTimeout(viewId) );
+ conversation.setConcurrentRequestTimeout(
pages.getConcurrentRequestTimeout(viewId) );
}
}
else
Modified:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Page.java
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Page.java 2008-11-21
20:06:57 UTC (rev 9610)
+++
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Page.java 2008-11-23
23:52:31 UTC (rev 9611)
@@ -29,6 +29,7 @@
private List<Input> inputs = new ArrayList<Input>();
private List<Action> actions = new ArrayList<Action>();
private Map<String, Navigation> navigations = new HashMap<String,
Navigation>();
+ private Integer concurrentRequestTimeout;
private Navigation defaultNavigation;
private boolean conversationRequired;
private boolean loginRequired;
@@ -119,6 +120,16 @@
return timeout;
}
+ public void setConcurrentRequestTimeout(Integer concurrentRequestTimeout)
+ {
+ this.concurrentRequestTimeout = concurrentRequestTimeout;
+ }
+
+ public Integer getConcurrentRequestTimeout()
+ {
+ return concurrentRequestTimeout;
+ }
+
public void setNoConversationViewId(String noConversationViewId)
{
this.noConversationViewId = noConversationViewId;
Modified:
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Pages.java
===================================================================
---
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Pages.java 2008-11-21
20:06:57 UTC (rev 9610)
+++
branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/navigation/Pages.java 2008-11-23
23:52:31 UTC (rev 9611)
@@ -914,6 +914,26 @@
return Manager.instance().getConversationTimeout();
}
+ /**
+ * Search for a defined concurrent request timeout, beginning with
+ * the most specific view id, then wildcarded view ids, and
+ * finally the global setting from Manager
+ */
+ public Integer getConcurrentRequestTimeout(String viewId)
+ {
+ List<Page> stack = getPageStack(viewId);
+ for (int i=stack.size()-1; i>=0; i--)
+ {
+ Page page = stack.get(i);
+ Integer concurrentRequestTimeout = page.getConcurrentRequestTimeout();
+ if (concurrentRequestTimeout!=null)
+ {
+ return concurrentRequestTimeout;
+ }
+ }
+ return Manager.instance().getConcurrentRequestTimeout();
+ }
+
public static String getSuffix()
{
String defaultSuffix = FacesContext.getCurrentInstance().getExternalContext()
@@ -1060,7 +1080,13 @@
page.setTimeout(Integer.parseInt(timeoutString));
}
- page.setNoConversationViewId(
element.attributeValue("no-conversation-view-id") );
+ String concurrentRequestTimeoutString =
element.attributeValue("concurrent-request-timeout");
+ if (concurrentRequestTimeoutString!=null)
+ {
+
page.setConcurrentRequestTimeout(Integer.parseInt(concurrentRequestTimeoutString));
+ }
+
+ page.setNoConversationViewId(
element.attributeValue("no-conversation-view-id") );
page.setConversationRequired( "true".equals(
element.attributeValue("conversation-required") ) );
page.setLoginRequired( "true".equals(
element.attributeValue("login-required") ) );
page.setScheme( element.attributeValue("scheme") );
Modified: branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.dtd
===================================================================
--- branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.dtd 2008-11-21
20:06:57 UTC (rev 9610)
+++ branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.dtd 2008-11-23
23:52:31 UTC (rev 9611)
@@ -27,6 +27,7 @@
<!ATTLIST page login-required (true|false) "false">
<!ATTLIST page scheme CDATA #IMPLIED>
<!ATTLIST page timeout CDATA #IMPLIED>
+<!ATTLIST page concurrent-request-timeout CDATA #IMPLIED>
<!ATTLIST page bundle CDATA #IMPLIED>
<!ATTLIST page conversation CDATA #IMPLIED>
Modified: branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.xsd
===================================================================
--- branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.xsd 2008-11-21
20:06:57 UTC (rev 9610)
+++ branches/enterprise/JBPAPP_4_3_FP01/src/main/org/jboss/seam/pages-2.0.xsd 2008-11-23
23:52:31 UTC (rev 9611)
@@ -95,6 +95,16 @@
</xs:attribute>
<xs:attribute name="scheme"/>
<xs:attribute name="timeout"/>
+ <xs:attribute name="concurrent-request-timeout">
+ <xs:annotation>
+ <xs:documentation>
+ Requests to conversations are serialized by default, and if
+ a lock cannot be acquired in time, the request will be
+ dropped. You can set the timeout on a page-by-page basis
+ here.
+ </xs:documentation>
+ </xs:annotation>
+ </xs:attribute>
<xs:attribute name="bundle"/>
<xs:attribute name="conversation"/>
</xs:attributeGroup>