[seam-commits] Seam SVN: r10362 - in trunk: ui/src/main/java/org/jboss/seam/ui and 1 other directories.

seam-commits at lists.jboss.org seam-commits at lists.jboss.org
Wed Apr 8 18:58:39 EDT 2009


Author: dan.j.allen
Date: 2009-04-08 18:58:39 -0400 (Wed, 08 Apr 2009)
New Revision: 10362

Added:
   trunk/ui/src/main/java/org/jboss/seam/ui/RenderStampStore.java
Modified:
   trunk/doc/Seam_Reference_Guide/en-US/Components.xml
   trunk/ui/src/main/java/org/jboss/seam/ui/renderkit/TokenRendererBase.java
Log:
JBSEAM-4076


Modified: trunk/doc/Seam_Reference_Guide/en-US/Components.xml
===================================================================
--- trunk/doc/Seam_Reference_Guide/en-US/Components.xml	2009-04-08 22:51:53 UTC (rev 10361)
+++ trunk/doc/Seam_Reference_Guide/en-US/Components.xml	2009-04-08 22:58:39 UTC (rev 10362)
@@ -192,6 +192,28 @@
                     </para>
                 </listitem>
              </varlistentry>
+            <varlistentry>
+                <term><literal>org.jboss.seam.ui.renderStampStore</literal></term>
+                <listitem>
+                    <para>
+                        A component (session-scoped by default) responsible for maintaining
+                        a collection of render stamps. A render stamp is an indicator as
+                        to whether a form which was rendered has been submitted. This store
+                        is useful when the client-side state saving method of JSF is being
+                        used because it puts the determination of whether a form has been
+                        posted in the control of the server rather than in the component tree
+                        which is maintained on the client.
+                    </para>
+                    <itemizedlist>
+                        <listitem>
+                        <para>
+                            <literal>maxSize</literal> &#8212; The maximum number of stamps
+                            to be kept in the store. Default: 100
+                        </para>
+                        </listitem>
+                    </itemizedlist>
+                </listitem>
+             </varlistentry>
         </variablelist>
 
         <para>

Added: trunk/ui/src/main/java/org/jboss/seam/ui/RenderStampStore.java
===================================================================
--- trunk/ui/src/main/java/org/jboss/seam/ui/RenderStampStore.java	                        (rev 0)
+++ trunk/ui/src/main/java/org/jboss/seam/ui/RenderStampStore.java	2009-04-08 22:58:39 UTC (rev 10362)
@@ -0,0 +1,96 @@
+package org.jboss.seam.ui;
+
+import static org.jboss.seam.ScopeType.SESSION;
+import static org.jboss.seam.annotations.Install.BUILT_IN;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.jboss.seam.Component;
+import org.jboss.seam.annotations.AutoCreate;
+import org.jboss.seam.annotations.Install;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.intercept.BypassInterceptors;
+
+/**
+ * A class that stores render stamps for use with &lt;s:token&gt; when client side
+ * state saving is in use. By default the render stamp store will never remove a
+ * render stamp unless instructed to by a UIToken. If the maxSize property is
+ * larger than zero then it will control the maximum number of tokens stored,
+ * with the oldest token being removed when a token is inserted that will take
+ * the store over the maxSize limit. The default maxSize is 100.
+ * 
+ * @author Stuart Douglas
+ */
+ at Name("org.jboss.seam.ui.renderStampStore")
+ at Scope(SESSION)
+ at Install(precedence = BUILT_IN, value = false)
+ at AutoCreate
+ at BypassInterceptors
+public class RenderStampStore implements Serializable {
+
+    class RenderStamp {
+        String stamp;
+        Date timeStamp;
+    }
+
+    int maxSize = 100;
+
+    Map<String, RenderStamp> store = new ConcurrentHashMap<String, RenderStamp>();
+
+    /**
+     * Stores a stamp in the store, and returns the key it is stored under.
+     */
+    public String storeStamp(String stamp) {
+        if (maxSize > 0) {
+            if (store.size() == maxSize) {
+                Date oldest = null;
+                String oldestSigniture = null;
+                for (String sig : store.keySet()) {
+                    RenderStamp s = store.get(sig);
+                    if (oldest == null || s.timeStamp.before(oldest)) {
+                        oldestSigniture = sig;
+                    }
+                }
+                store.remove(oldestSigniture);
+            }
+        }
+        RenderStamp s = new RenderStamp();
+        s.stamp = stamp;
+        s.timeStamp = new Date();
+        String key;
+        do {
+           key = UUID.randomUUID().toString();
+        } while (!store.containsKey(key));
+        store.put(key, s);
+        return key;
+    }
+
+    public void removeStamp(String viewSigniture) {
+        store.remove(viewSigniture);
+    }
+
+    public String getStamp(String viewSigniture) {
+        RenderStamp s = store.get(viewSigniture);
+        if (s != null) {
+            return store.get(viewSigniture).stamp;
+        }
+        return null;
+    }
+
+    public static RenderStampStore instance() {
+        return (RenderStampStore) Component.getInstance(RenderStampStore.class);
+    }
+
+    public int getMaxSize() {
+        return maxSize;
+    }
+
+    public void setMaxSize(int maxSize) {
+        this.maxSize = maxSize;
+    }
+}

Modified: trunk/ui/src/main/java/org/jboss/seam/ui/renderkit/TokenRendererBase.java
===================================================================
--- trunk/ui/src/main/java/org/jboss/seam/ui/renderkit/TokenRendererBase.java	2009-04-08 22:51:53 UTC (rev 10361)
+++ trunk/ui/src/main/java/org/jboss/seam/ui/renderkit/TokenRendererBase.java	2009-04-08 22:58:39 UTC (rev 10362)
@@ -10,6 +10,7 @@
 import javax.faces.context.ResponseWriter;
 import javax.servlet.http.HttpSession;
 
+import org.jboss.seam.ui.RenderStampStore;
 import org.jboss.seam.ui.UnauthorizedCommandException;
 import org.jboss.seam.ui.component.UIToken;
 import org.jboss.seam.ui.util.HTML;
@@ -20,7 +21,10 @@
 /**
  * <p>
  * The <strong>TokenRendererBase</strong> renders the form's signature as a
- * hidden form field for the UIToken component.
+ * hidden form field for the UIToken component. If the renderStampStore
+ * component is enabled, the actually signature will be stored in the session
+ * and the key to this token store in the hidden form field, providing the same
+ * guarantee for client-side state saving as with server-side state saving.
  * </p>
  * 
  * <p>
@@ -41,18 +45,30 @@
  * sha1(signature = contextPath + viewId + &quot;,&quot; + formClientId + &quot;,&quot; + random alphanum + sessionId, salt = clientUid)
  * </pre>
  * 
- * <p>The decode method performs the following steps:</p>
+ * <p>
+ * The decode method performs the following steps:
+ * </p>
  * <ol>
- * <li>check if this is a postback, otherwise skip the check</li>
- * <li>check that this form was the one that was submitted, otherwise skip the check</li>
- * <li>get the unique client identifier (from cookie), otherwise throw an exception that the browser must have unique identifier</li>
- * <li>get the javax.faces.FormSignature request parameter, otherwise throw an exception that the form signature is missing</li>
- * <li>generate the hash as before and verify that it equals the value of the javax.faces.FormSignature request parameter, otherwise throw an exception</li>
+ * <li>Check if this is a postback, otherwise skip the check</li>
+ * <li>Check that this form was the one that was submitted, otherwise skip the
+ * check</li>
+ * <li>Get the unique client identifier (from cookie), otherwise throw an
+ * exception that the browser must have unique identifier</li>
+ * <li>Get the javax.faces.FormSignature request parameter, otherwise throw an
+ * exception that the form signature is missing</li>
+ * <li>If the renderStampStore component is enabled, retrieve the render stamp
+ * from the store using the key stored in the render stamp attribute of the form.</li>
+ * <li>Generate the hash as before and verify that it equals the value of the
+ * javax.faces.FormSignature request parameter, otherwise throw an exception</li>
  * </ol>
  * 
- * <p>If all of that passes, we are okay to process the form (advance to validate phase as decode() is called in apply request values).</p>
+ * <p>
+ * If all of that passes, we are okay to process the form (advance to validate
+ * phase as decode() is called in apply request values).
+ * </p>
  * 
  * @author Dan Allen
+ * @author Stuart Douglas
  * @see UnauthorizedCommandException
  */
 public class TokenRendererBase extends RendererBase
@@ -60,7 +76,7 @@
    public static final String FORM_SIGNATURE_PARAM = "javax.faces.FormSignature";
 
    public static final String RENDER_STAMP_ATTR = "javax.faces.RenderStamp";
-   
+
    private static final String COOKIE_CHECK_SCRIPT_KEY = "org.jboss.seam.ui.COOKIE_CHECK_SCRIPT";
 
    @Override
@@ -93,7 +109,12 @@
          {
             throw new UnauthorizedCommandException(viewId, "Form signature invalid");
          }
-
+         RenderStampStore store = RenderStampStore.instance();
+         if (store != null)
+         {
+            // remove the key from the store if we are using it
+            store.removeStamp(String.valueOf(form.getAttributes().get(RENDER_STAMP_ATTR)));
+         }
          form.getAttributes().remove(RENDER_STAMP_ATTR);
       }
    }
@@ -107,11 +128,23 @@
       {
          throw new IllegalStateException("UIToken must be inside a UIForm.");
       }
-      
+
+      String renderStamp = RandomStringUtils.randomAlphanumeric(50);
+      RenderStampStore store = RenderStampStore.instance();
+      if (store != null)
+      {
+         // if the store is not null we store the key
+         // instead of the actual stamp; this puts the
+         // server in control of this value rather than
+         // the component tree, which is owned by the client
+         // when using client-side state saving
+         renderStamp = store.storeStamp(renderStamp);
+      }
+
       writeCookieCheckScript(context, writer, token);
 
       token.getClientUidSelector().seed();
-      form.getAttributes().put(RENDER_STAMP_ATTR, RandomStringUtils.randomAlphanumeric(50));
+      form.getAttributes().put(RENDER_STAMP_ATTR, renderStamp);
       writer.startElement(HTML.INPUT_ELEM, component);
       writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, HTML.TYPE_ATTR);
       writer.writeAttribute(HTML.NAME_ATTR, FORM_SIGNATURE_PARAM, HTML.NAME_ATTR);
@@ -141,7 +174,16 @@
       String rawViewSignature = context.getExternalContext().getRequestContextPath() + "," + context.getViewRoot().getViewId() + "," + form.getClientId(context);
       if (useRenderStamp)
       {
-         rawViewSignature += "," + form.getAttributes().get(RENDER_STAMP_ATTR);
+         String renderStamp = form.getAttributes().get(RENDER_STAMP_ATTR).toString();
+         RenderStampStore store = RenderStampStore.instance();
+         if (store != null)
+         {
+            // if we are using the RenderStampStore the key to access the render
+            // stamp
+            // is stored in the view root instead of the actual render stamp
+            renderStamp = store.getStamp(renderStamp);
+         }
+         rawViewSignature += "," + renderStamp;
       }
       if (useSessionId)
       {
@@ -164,5 +206,5 @@
          return null;
       }
    }
-   
+
 }




More information about the seam-commits mailing list