You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by jc...@apache.org on 2006/11/10 22:46:19 UTC
svn commit: r473514 - in
/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket:
AccessStackPageMap.java Component.java ISessionFactory.java Page.java
PageMap.java RequestCycle.java Session.java
Author: jcompagner
Date: Fri Nov 10 13:46:18 2006
New Revision: 473514
URL: http://svn.apache.org/viewvc?view=rev&rev=473514
Log:
stateless support
Added:
incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/AccessStackPageMap.java
Modified:
incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Component.java
incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/ISessionFactory.java
incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Page.java
incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/PageMap.java
incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/RequestCycle.java
incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Session.java
Added: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/AccessStackPageMap.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/AccessStackPageMap.java?view=auto&rev=473514
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/AccessStackPageMap.java (added)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/AccessStackPageMap.java Fri Nov 10 13:46:18 2006
@@ -0,0 +1,447 @@
+/*
+ * $Id: AccessStackPageMap.java 5791 2006-05-20 00:32:57 +0000 (Sat, 20 May
+ * 2006) joco01 $ $Revision: 461715 $ $Date: 2006-05-20 00:32:57 +0000 (Sat, 20
+ * May 2006) $
+ *
+ * ==============================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package wicket;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import wicket.session.pagemap.IPageMapEntry;
+import wicket.util.collections.ArrayListStack;
+
+/**
+ * A container for pages held in the session. PageMap is a parameter to several
+ * methods in the Wicket API. You can get a PageMap by name from a Session with
+ * Session.getPageMap(String pageMapName) or more conveniently with
+ * PageMap.forName(String pageMapName). But you should not hold onto a reference
+ * to the pagemap (just as you should not hold onto a reference to your Session
+ * but should get it each time you need it instead). Instead, create a strongly
+ * typed accessor method like this:
+ *
+ * <pre>
+ * public PageMap getMyPageMap()
+ * {
+ * return PageMap.forName("myPageMapName");
+ * }
+ * </pre>
+ *
+ * If the page map with the given name is not found, one will be automatically
+ * created.
+ *
+ * @author Jonathan Locke
+ */
+public final class AccessStackPageMap extends PageMap implements Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /** Log. */
+ private static final Log log = LogFactory.getLog(AccessStackPageMap.class);
+
+
+ /** Stack of entry accesses by id */
+ private final ArrayListStack accessStack = new ArrayListStack(8);
+
+
+ /**
+ * Holds information about a pagemap access
+ *
+ * @author Jonathan
+ */
+ public static class Access implements Serializable
+ {
+ private static final long serialVersionUID = 1L;
+
+ int id;
+ int version;
+
+ /**
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof Access)
+ {
+ Access tmp = (Access)obj;
+ return tmp.id == id && tmp.version == version;
+ }
+ return false;
+ }
+
+ /**
+ * Gets id.
+ *
+ * @return id
+ */
+ public final int getId()
+ {
+ return id;
+ }
+
+ /**
+ * Gets version.
+ *
+ * @return version
+ */
+ public final int getVersion()
+ {
+ return version;
+ }
+
+ /**
+ * @see java.lang.Object#hashCode()
+ */
+ public int hashCode()
+ {
+ return id + (version << 16);
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "[Access id=" + id + ", version=" + version + "]";
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param name
+ * The name of this page map
+ * @param session
+ * The session holding this page map
+ */
+ public AccessStackPageMap(final String name, final Session session)
+ {
+ super(name, session);
+ }
+
+ /**
+ * Removes all pages from this map
+ */
+ public final void clear()
+ {
+ super.clear();
+ // Clear access stack
+ accessStack.clear();
+ dirty();
+ }
+
+ // TODO Post 1.2: We should encode the page id of the current page into the
+ // URL for truly stateless pages so we can adjust the stack correctly
+
+ /**
+ * Returns a stack of PageMap.Access entries pushed in the order that the
+ * pages and versions were accessed.
+ *
+ * @return Stack containing ids of entries in access order.
+ */
+ public final ArrayListStack getAccessStack()
+ {
+ return accessStack;
+ }
+
+ /**
+ * @return Number of page versions stored in this page map
+ */
+ public final int getVersions()
+ {
+ return accessStack.size();
+ }
+
+ /**
+ * Gets the most recently accessed page map entry off the top of the entry
+ * access stack. This is guaranteed to be the most recently accessed entry
+ * IF AND ONLY IF the user just came from a stateful page. If the user could
+ * get to the current page from a stateless page, this method may not work
+ * if the user uses the back button. For a detailed explanation of this
+ * issue, see getAccessStack().
+ *
+ * @see PageMap#getAccessStack()
+ *
+ * @return Previous pagemap entry in terms of access
+ */
+ public final IPageMapEntry lastAccessedEntry()
+ {
+ return getEntry(peekAccess().getId());
+ }
+
+
+ /**
+ * @param entry
+ * The entry to remove
+ */
+ public final void removeEntry(final IPageMapEntry entry)
+ {
+ if(entry == null)
+ {
+ // TODO this shouldn't happen but to many people are still getting this now and then/
+ // so first this "fix"
+ log.warn("PageMap.removeEntry called with an null entry");
+ return;
+ }
+ // Remove entry from session
+ Session session = getSession();
+ synchronized (session)
+ {
+ session.removeAttribute(attributeForId(entry.getNumericId()));
+
+ // Remove page from acccess stack
+ final Iterator stack = accessStack.iterator();
+ while (stack.hasNext())
+ {
+ final Access access = (Access)stack.next();
+ if (access.id == entry.getNumericId())
+ {
+ stack.remove();
+ }
+ }
+
+ // Let the session know we changed the pagemap
+ dirty();
+ }
+ }
+
+ /**
+ * Retrieves page with given id.
+ *
+ * @param id
+ * The page identifier
+ * @param versionNumber
+ * The version to get
+ * @return Any page having the given id
+ */
+ protected final Page get(final int id, int versionNumber)
+ {
+ final IPageMapEntry entry = (IPageMapEntry)getSession().getAttribute(attributeForId(id));
+ if (entry != null)
+ {
+ // Get page as dirty
+ Page page = entry.getPage();
+
+ // TODO Performance: Is this really the case is a page always dirty
+ // even if we just render it again? POSSIBLE ANSWER: The page could
+ // mark itself as clean to prevent replication, but the reverse is
+ // probably not desirable (pages marking themselves dirty manually)
+ // We ought to think about this a bit and consider whether this
+ // could be tied in with version management. It's only when a page's
+ // version changes that it should be considered dirty, because then
+ // some kind of state changed. Right? - Jonathan
+ page.dirty();
+
+ // Get the version of the page requested from the page
+ final Page version = page.getVersion(versionNumber);
+
+ // Entry has been accessed
+ // pushAccess(entry);
+ // Entry has been accessed
+ access(entry, versionOf(entry));
+
+
+ // Is the requested version available?
+ if (version != null)
+ {
+ // Need to update session with new page?
+ if (version != page)
+ {
+ // This is our new page
+ page = version;
+
+ // Replaces old page entry
+ page.getPageMap().put(page);
+ }
+ }
+ else
+ {
+ if (log.isInfoEnabled())
+ {
+ log.info("Unable to get version " + versionNumber + " of page " + page);
+ }
+ return null;
+ }
+ return page;
+ }
+ return null;
+ }
+
+ /**
+ * @param page
+ * The page to put into this map
+ */
+ protected final void put(final Page page)
+ {
+ // Page only goes into session if it is stateless
+ if (!page.isPageStateless())
+ {
+ Session session = getSession();
+ // Get page map entry from page
+ final IPageMapEntry entry = page.getPageMapEntry();
+
+ // Entry has been accessed
+ pushAccess(entry);
+
+ // Store entry in session
+ final String attribute = attributeForId(entry.getNumericId());
+
+ if (session.getAttribute(attribute) == null)
+ {
+ // Set attribute if it is a new page, so that it will exists
+ // already for other threads that can come on the same time.
+ session.setAttribute(attribute, entry);
+ }
+ else
+ {
+ // Else don't set it directly but add to the dirty map
+ session.dirtyPage(page);
+ }
+
+ // Evict any page(s) as need be
+ session.getApplication().getSessionSettings().getPageMapEvictionStrategy().evict(this);
+ }
+ }
+
+ /**
+ * @param entry
+ * Add entry to access list
+ * @param version
+ * Version number being accessed
+ */
+ private final void access(final IPageMapEntry entry, final int version)
+ {
+ // See if the version being accessed is already in the stack
+ boolean add = true;
+ int id = entry.getNumericId();
+ for (int i = accessStack.size() - 1; i >= 0; i--)
+ {
+ final Access access = (Access)accessStack.get(i);
+
+ // If we found id and version in access stack
+ if (access.id == id && access.version == version)
+ {
+ // No need to add since id and version are already in stack
+ add = false;
+
+ // Pop entries to reveal that version at top of stack
+ // because the user used the back button
+ while (i < accessStack.size() - 1)
+ {
+ // Pop unreachable access off top of stack
+ final Access topAccess = popAccess();
+
+ // Get entry for access
+ final IPageMapEntry top = getEntry(topAccess.getId());
+
+ // If it's a page we can remove version info
+ if (top instanceof Page)
+ {
+ // If there's more than one version
+ Page topPage = (Page)top;
+ if (topPage.getVersions() > 1)
+ {
+ // Remove version the top access version (-1)
+ topPage.getVersion(topAccess.getVersion() - 1);
+ }
+ else
+ {
+ // Remove whole page
+ remove(topPage);
+ }
+ }
+ else if(top != null)
+ {
+ // Remove entry
+ removeEntry(top);
+ }
+ }
+ break;
+ }
+ }
+
+ // If the user did not use the back button
+ if (add)
+ {
+ pushAccess(entry);
+ }
+ }
+
+ /**
+ * @return Access entry on top of the access stack
+ */
+ private final Access peekAccess()
+ {
+ return (Access)accessStack.peek();
+ }
+
+ /**
+ * Removes access entry on top of stack
+ *
+ * @return Access entry on top of the access stack
+ */
+ private final Access popAccess()
+ {
+ dirty();
+ return (Access)accessStack.pop();
+ }
+
+ /**
+ * @param entry
+ * Entry that was accessed
+ */
+ private final void pushAccess(IPageMapEntry entry)
+ {
+ // Create new access entry
+ final Access access = new Access();
+ access.id = entry.getNumericId();
+ access.version = versionOf(entry);
+ if (accessStack.size() > 0)
+ {
+ if (peekAccess().equals(access))
+ {
+ return;
+ }
+ int index = accessStack.indexOf(access);
+ if (index >= 0)
+ {
+ accessStack.remove(index);
+ }
+ }
+ accessStack.push(access);
+ dirty();
+ }
+
+ /**
+ * @param entry
+ * Page map entry
+ * @return Version of entry
+ */
+ private final int versionOf(final IPageMapEntry entry)
+ {
+ if (entry instanceof Page)
+ {
+ return ((Page)entry).getCurrentVersionNumber();
+ }
+
+ // If entry is not a page, it cannot have versions because the Page
+ // is constructed on the fly.
+ return 0;
+ }
+}
Modified: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Component.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Component.java?view=diff&rev=473514&r1=473513&r2=473514
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Component.java (original)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Component.java Fri Nov 10 13:46:18 2006
@@ -2649,6 +2649,45 @@
{
return this.getFlag(FLAG_IGNORE_ATTRIBUTE_MODIFIER);
}
+
+ /**
+ * Returns whether the component can be stateless. Being able to be
+ * stateless doesn't necessary mean, that the component should be stateless.
+ * Whether the component should be stateless depends on
+ *
+ * @return whether the component can be stateless
+ */
+ protected boolean getStatelessHint()
+ {
+ return true;
+ }
+
+ /**
+ * Returns if the component is stateless or not. It checks the stateless
+ * hint if that is false it returns directly false. If that is still true it
+ * checks all its behaviours if they can be stateless.
+ *
+ * @return whether the component is stateless.
+ */
+ public final boolean isStateless()
+ {
+ if (!getStatelessHint())
+ {
+ return false;
+ }
+
+ final Iterator behaviors = getBehaviors().iterator();
+
+ while (behaviors.hasNext())
+ {
+ IBehavior behavior = (IBehavior)behaviors.next();
+ if (!behavior.getStatelessHint())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
/**
* Called just after a component is rendered.
Modified: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/ISessionFactory.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/ISessionFactory.java?view=diff&rev=473514&r1=473513&r2=473514
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/ISessionFactory.java (original)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/ISessionFactory.java Fri Nov 10 13:46:18 2006
@@ -25,10 +25,16 @@
*/
public interface ISessionFactory
{
+
/**
* Creates a new session
*
+ * @param request
+ * The request that will create this session.
+ *
* @return The session
+ *
+ * @since 1.3
*/
- Session newSession();
+ Session newSession(Request request);
}
Modified: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Page.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Page.java?view=diff&rev=473514&r1=473513&r2=473514
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Page.java (original)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Page.java Fri Nov 10 13:46:18 2006
@@ -153,6 +153,9 @@
/** True if component changes are being tracked. */
private static final short FLAG_TRACK_CHANGES = FLAG_RESERVED4;
+ /** True if the page should try to be stateless */
+ private static final int FLAG_STATELESS_HINT = FLAG_RESERVED5;
+
/** Log. */
private static final Log log = LogFactory.getLog(Page.class);
@@ -178,7 +181,7 @@
* Boolean if the page is stateless, so it doesn't have to be in the page
* map, will be set in urlFor
*/
- private transient boolean stateless = true;
+ private transient Boolean stateless = null;
/** Version manager for this page */
private IPageVersionManager versionManager;
@@ -357,7 +360,7 @@
renderedComponents = null;
// Reset it to stateless so that it can be tested again
- this.stateless = true;
+ this.stateless = null;
// Set form component values from cookies
setFormComponentValuesFromCookies();
@@ -415,8 +418,13 @@
// Check rendering if it happened fully
checkRendering(this);
- // Add/touch the response page in the session (its pagemap).
- getSession().touch(this);
+ if (!isPageStateless())
+ {
+ // trigger creation of the actual session in case it was deferred
+ Session.get().getSessionStore().getSessionId(RequestCycle.get().getRequest(), true);
+ // Add/touch the response page in the session (its pagemap).
+ getSession().touch(this);
+ }
}
/**
@@ -545,6 +553,18 @@
}
/**
+ * Returns whether the page should try to be stateless. To be stateless,
+ * getStatelessHint() of every component on page (and it's behavior) must
+ * return true and the page must be bookmarkable.
+ *
+ * @see wicket.Component#getStatelessHint()
+ */
+ public final boolean getStatelessHint()
+ {
+ return getFlag(FLAG_STATELESS_HINT);
+ }
+
+ /**
* Override this method to implement a custom way of producing a version of
* a Page when it cannot be found in the Session.
*
@@ -641,6 +661,37 @@
});
return buffer.toString();
}
+
+ /**
+ * Bookmarkable page can be instantiated using a bookmarkable URL.
+ *
+ * @return Returns true if the page is bookmarkable.
+ */
+ public boolean isBookmarkable()
+ {
+ try
+ {
+ if (getClass().getConstructor(new Class[] { PageParameters.class }) != null)
+ {
+ return true;
+ }
+
+ }
+ catch (Exception ignore)
+ {
+ try
+ {
+ if (getClass().getConstructor(new Class[] {}) != null)
+ {
+ return true;
+ }
+ }
+ catch (Exception ignore2)
+ {
+ }
+ }
+ return false;
+ }
/**
* Override this method and return true if your page is used to display
@@ -656,6 +707,71 @@
}
/**
+ * Set page stateless
+ *
+ * @param stateless
+ */
+ void setPageStateless(Boolean stateless)
+ {
+ this.stateless = stateless;
+ }
+
+ /**
+ * Gets whether the page is stateless. Components on stateless page must not
+ * render any statefull urls, and components on statefull page must not
+ * render any stateless urls. Statefull urls are urls, which refer to a
+ * certain (current) page instance.
+ *
+ * @return Whether to page is stateless
+ */
+ public final boolean isPageStateless()
+ {
+ if (isBookmarkable() == false)
+ {
+ stateless = Boolean.FALSE;
+ if (getStatelessHint())
+ {
+ log.warn("Page '" + this + "' is not stateless because it is not bookmarkable, "
+ + "but the stateless hint is set to true!");
+ }
+ }
+
+ if (stateless == null)
+ {
+ final Object[] returnArray = new Object[1];
+ Object returnValue = visitChildren(Component.class, new IVisitor()
+ {
+ public Object component(Component component)
+ {
+ if (!component.isStateless())
+ {
+ returnArray[0] = component;
+ return Boolean.FALSE;
+ }
+
+ return CONTINUE_TRAVERSAL;
+ }
+ });
+ if (returnValue == null)
+ {
+ stateless = Boolean.TRUE;
+ }
+ else if (returnValue instanceof Boolean)
+ {
+ stateless = (Boolean)returnValue;
+ }
+
+ if (!stateless.booleanValue() && getStatelessHint())
+ {
+ log.warn("Page '" + this + "' is not stateless because of '" + returnArray[0]
+ + "' but the stateless hint is set to true!");
+ }
+ }
+
+ return stateless.booleanValue();
+ }
+
+ /**
* Redirect to this page.
*
* @see wicket.IRedirectListener#onRedirect()
@@ -961,20 +1077,6 @@
}
/**
- * @return Return true from this method if you want to keep a page out of
- * the session.
- */
- final boolean isStateless()
- {
- return stateless;
- }
-
- final void setStateless(boolean stateless)
- {
- this.stateless = stateless;
- }
-
- /**
* Sets values for form components based on cookie values in the request.
*
*/
@@ -1006,6 +1108,24 @@
this.pageMapName = pageMap.getName();
}
+ /**
+ * Sets whether the page should try to be stateless. To be stateless,
+ * getStatelessHint() of every component on page (and it's behavior) must
+ * return true and the page must be bookmarkable.
+ *
+ * @param value
+ * whether the page should try to be stateless
+ */
+ public final void setStatelessHint(boolean value)
+ {
+ if (value && !isBookmarkable())
+ {
+ throw new WicketRuntimeException(
+ "Can't set stateless hint to true on a page when the page is not bookmarkable, page: "
+ + this);
+ }
+ setFlag(FLAG_STATELESS_HINT, value);
+ }
/**
* THIS METHOD IS NOT PART OF THE WICKET PUBLIC API. DO NOT CALL OR
Modified: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/PageMap.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/PageMap.java?view=diff&rev=473514&r1=473513&r2=473514
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/PageMap.java (original)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/PageMap.java Fri Nov 10 13:46:18 2006
@@ -1,6 +1,6 @@
/*
- * $Id$ $Revision:
- * 1.67 $ $Date$
+ * $Id:PageMap.java 5583 2006-04-30 22:23:23 +0000 (zo, 30 apr 2006) joco01 $
+ * $Revision:5583 $ $Date:2006-04-30 22:23:23 +0000 (zo, 30 apr 2006) $
*
* ==============================================================================
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
@@ -22,46 +22,19 @@
import java.util.Iterator;
import java.util.List;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-
import wicket.session.pagemap.IPageMapEntry;
-import wicket.util.collections.ArrayListStack;
import wicket.util.lang.Objects;
/**
- * A container for pages held in the session. PageMap is a parameter to several
- * methods in the Wicket API. You can get a PageMap by name from a Session with
- * Session.getPageMap(String pageMapName) or more conveniently with
- * PageMap.forName(String pageMapName). But you should not hold onto a reference
- * to the pagemap (just as you should not hold onto a reference to your Session
- * but should get it each time you need it instead). Instead, create a strongly
- * typed accessor method like this:
- *
- * <pre>
- * public PageMap getMyPageMap()
- * {
- * return PageMap.forName("myPageMapName");
- * }
- * </pre>
- *
- * If the page map with the given name is not found, one will be automatically
- * created.
- *
- * @author Jonathan Locke
+ * @author jcompagner
*/
-public final class PageMap implements Serializable
+public abstract class PageMap implements Serializable
{
- /** Name of default pagemap */
- public static final String DEFAULT_NAME = null;
-
- /** Log. */
- private static final Log log = LogFactory.getLog(PageMap.class);
-
private static final long serialVersionUID = 1L;
- /** Stack of entry accesses by id */
- private final ArrayListStack/* <Access> */accessStack = new ArrayListStack(8);
+
+ /** Name of default pagemap */
+ public static final String DEFAULT_NAME = null;
/** URL to continue to after a given page. */
private String interceptContinuationURL;
@@ -76,65 +49,18 @@
private transient Session session;
/**
- * Holds information about a pagemap access
+ * Gets a page map for a page map name, automatically creating the page map
+ * if it does not exist. If you do not want the pagemap to be automatically
+ * created, you can call Session.pageMapForName(pageMapName, false).
*
- * @author Jonathan
+ * @param pageMapName
+ * The name of the page map to get
+ * @return The PageMap with the given name from the current session
*/
- public static class Access implements Serializable
+ public static PageMap forName(final String pageMapName)
{
- private static final long serialVersionUID = 1L;
-
- int id;
- int version;
-
- /**
- * @see java.lang.Object#equals(java.lang.Object)
- */
- public boolean equals(Object obj)
- {
- if (obj instanceof Access)
- {
- Access tmp = (Access)obj;
- return tmp.id == id && tmp.version == version;
- }
- return false;
- }
-
- /**
- * Gets id.
- *
- * @return id
- */
- public final int getId()
- {
- return id;
- }
-
- /**
- * Gets version.
- *
- * @return version
- */
- public final int getVersion()
- {
- return version;
- }
-
- /**
- * @see java.lang.Object#hashCode()
- */
- public int hashCode()
- {
- return id + (version << 16);
- }
-
- /**
- * @see java.lang.Object#toString()
- */
- public String toString()
- {
- return "[Access id=" + id + ", version=" + version + "]";
- }
+ Session session = Session.get();
+ return (session != null) ? session.pageMapForName(pageMapName, true) : null;
}
/**
@@ -151,20 +77,6 @@
public void entry(final IPageMapEntry entry);
}
- /**
- * Gets a page map for a page map name, automatically creating the page map
- * if it does not exist. If you do not want the pagemap to be automatically
- * created, you can call Session.pageMapForName(pageMapName, false).
- *
- * @param pageMapName
- * The name of the page map to get
- * @return The PageMap with the given name from the current session
- */
- public static PageMap forName(final String pageMapName)
- {
- Session session = Session.get();
- return (session != null) ? session.pageMapForName(pageMapName, true) : null;
- }
/**
* Constructor
@@ -174,7 +86,7 @@
* @param session
* The session holding this page map
*/
- PageMap(final String name, final Session session)
+ public PageMap(String name, Session session)
{
this.name = name;
if (session == null)
@@ -184,37 +96,6 @@
this.session = session;
}
- /**
- * Removes all pages from this map
- */
- public final void clear()
- {
- // Remove all entries
- visitEntries(new IVisitor()
- {
- public void entry(IPageMapEntry entry)
- {
- removeEntry(entry);
- }
- });
-
- // Clear access stack
- accessStack.clear();
- }
-
- // TODO Post 1.2: We should encode the page id of the current page into the
- // URL for truly stateless pages so we can adjust the stack correctly
-
- /**
- * Returns a stack of PageMap.Access entries pushed in the order that the
- * pages and versions were accessed.
- *
- * @return Stack containing ids of entries in access order.
- */
- public final ArrayListStack getAccessStack()
- {
- return accessStack;
- }
/**
* Retrieves entry with given id.
@@ -225,7 +106,7 @@
*/
public final IPageMapEntry getEntry(final int id)
{
- return(IPageMapEntry)session.getAttribute(attributeForId(id));
+ return (IPageMapEntry)session.getAttribute(attributeForId(id));
}
/**
@@ -245,36 +126,6 @@
}
/**
- * @return Size of this page map in bytes, including a sum of the sizes of
- * all the pages it contains.
- */
- public final long getSizeInBytes()
- {
- long size = Objects.sizeof(this);
- for (Iterator iterator = getEntries().iterator(); iterator.hasNext();)
- {
- IPageMapEntry entry = (IPageMapEntry)iterator.next();
- if (entry instanceof Page)
- {
- size += ((Page)entry).getSizeInBytes();
- }
- else
- {
- size += Objects.sizeof(entry);
- }
- }
- return size;
- }
-
- /**
- * @return Number of page versions stored in this page map
- */
- public final int getVersions()
- {
- return accessStack.size();
- }
-
- /**
* @return True if this is the default page map
*/
public final boolean isDefault()
@@ -283,86 +134,17 @@
}
/**
- * Gets the most recently accessed page map entry off the top of the entry
- * access stack. This is guaranteed to be the most recently accessed entry
- * IF AND ONLY IF the user just came from a stateful page. If the user could
- * get to the current page from a stateless page, this method may not work
- * if the user uses the back button. For a detailed explanation of this
- * issue, see getAccessStack().
- *
- * @see PageMap#getAccessStack()
- *
- * @return Previous pagemap entry in terms of access
- */
- public final IPageMapEntry lastAccessedEntry()
- {
- return getEntry(peekAccess().getId());
- }
-
- /**
- * Removes this PageMap from the Session.
- */
- public final void remove()
- {
- // First clear all pages from the session for this pagemap
- clear();
-
- // Then remove the pagemap itself
- session.removePageMap(this);
- }
-
- /**
- * Removes the page from the pagemap
- *
- * @param page
- * page to be removed from the pagemap
- */
- public final void remove(final Page page)
- {
- // Remove the pagemap entry from session
- removeEntry(page.getPageMapEntry());
- }
-
- /**
- * @param entry
- * The entry to remove
+ * @return The next id for this pagemap
*/
- public final void removeEntry(final IPageMapEntry entry)
+ final int nextId()
{
- if(entry == null)
- {
- // TODO this shouldn't happen but to many people are still getting this now and then/
- // so first this "fix"
- log.warn("PageMap.removeEntry called with an null entry");
- return;
- }
- // Remove entry from session
- synchronized (session)
- {
- session.removeAttribute(attributeForId(entry.getNumericId()));
-
- // Remove page from acccess stack
- final Iterator stack = accessStack.iterator();
- while (stack.hasNext())
- {
- final Access access = (Access)stack.next();
- if (access.id == entry.getNumericId())
- {
- stack.remove();
- }
- }
-
- // Let the session know we changed the pagemap
- session.dirtyPageMap(this);
- }
+ dirty();
+ return this.pageId++;
}
- /**
- * @see java.lang.Object#toString()
- */
- public String toString()
+ protected final void dirty()
{
- return "[PageMap name=" + name + ", access=" + accessStack + "]";
+ session.dirtyPageMap(this);
}
/**
@@ -371,7 +153,7 @@
* @return The session attribute for the given page (for replication of
* state)
*/
- final String attributeForId(final int id)
+ public final String attributeForId(final int id)
{
return attributePrefix() + id;
}
@@ -424,119 +206,13 @@
interceptContinuationURL = null;
// Force session to replicate page maps
- session.dirtyPageMap(this);
+ dirty();
return true;
}
return false;
}
/**
- * Retrieves page with given id.
- *
- * @param id
- * The page identifier
- * @param versionNumber
- * The version to get
- * @return Any page having the given id
- */
- final Page get(final int id, int versionNumber)
- {
- final IPageMapEntry entry = (IPageMapEntry)session.getAttribute(attributeForId(id));
- if (entry != null)
- {
- // Get page as dirty
- Page page = entry.getPage();
-
- // TODO Performance: Is this really the case is a page always dirty
- // even if we just render it again? POSSIBLE ANSWER: The page could
- // mark itself as clean to prevent replication, but the reverse is
- // probably not desirable (pages marking themselves dirty manually)
- // We ought to think about this a bit and consider whether this
- // could be tied in with version management. It's only when a page's
- // version changes that it should be considered dirty, because then
- // some kind of state changed. Right? - Jonathan
- page.dirty();
-
- // Get the version of the page requested from the page
- final Page version = page.getVersion(versionNumber);
-
- // Entry has been accessed
- //pushAccess(entry);
- // Entry has been accessed
- access(entry, versionOf(entry));
-
-
- // Is the requested version available?
- if (version != null)
- {
- // Need to update session with new page?
- if (version != page)
- {
- // This is our new page
- page = version;
-
- // Replaces old page entry
- page.getPageMap().put(page);
- }
- }
- else
- {
- if (log.isInfoEnabled())
- {
- log.info("Unable to get version " + versionNumber + " of page " + page);
- }
- return null;
- }
- return page;
- }
- return null;
- }
-
- /**
- * @return The next id for this pagemap
- */
- final int nextId()
- {
- session.dirtyPageMap(this);
- return this.pageId++;
- }
-
- /**
- * @param page
- * The page to put into this map
- */
- final void put(final Page page)
- {
- // Page only goes into session if it is stateless
- if (!page.isStateless())
- {
- // Get page map entry from page
- final IPageMapEntry entry = page.getPageMapEntry();
-
- // Entry has been accessed
- pushAccess(entry);
-
- // Store entry in session
- final String attribute = attributeForId(entry.getNumericId());
-
- if(session.getAttribute(attribute) == null)
- {
- // Set attribute if it is a new page, so that it will exists
- // already for other threads that can come on the same time.
- session.setAttribute(attribute, entry);
- }
- else
- {
- // Else don't set it directly but add to the dirty map
- session.dirtyPage(page);
- }
-
- // Evict any page(s) as need be
- session.getApplication().getSessionSettings().getPageMapEvictionStrategy().evict(this);
- }
- }
-
- /**
* Redirects browser to an intermediate page such as a sign-in page. The
* current request's URL is saved exactly as it was requested for future use
* by continueToOriginalDestination(); Only use this method when you plan to
@@ -548,6 +224,7 @@
*/
final void redirectToInterceptPage(final Page page)
{
+ Session.get().bind();
// Get the request cycle
final RequestCycle cycle = RequestCycle.get();
@@ -556,14 +233,14 @@
interceptContinuationURL = cycle.getRequest().getURL();
// Page map is dirty
- session.dirtyPageMap(this);
+ dirty();
// Redirect to the page
cycle.setRedirect(true);
cycle.setResponsePage(page);
}
-
- /**
+
+ /**
* Redirects browser to an intermediate page such as a sign-in page. The
* current request's URL is saved exactly as it was requested for future use
* by continueToOriginalDestination(); Only use this method when you plan to
@@ -575,6 +252,7 @@
*/
final void redirectToInterceptPage(final Class pageClazz)
{
+ Session.get().bind();
// Get the request cycle
final RequestCycle cycle = RequestCycle.get();
@@ -588,8 +266,23 @@
// Redirect to the page
cycle.setRedirect(true);
cycle.setResponsePage(pageClazz);
+ }
+
+ /**
+ * Removes all pages from this map
+ */
+ public void clear()
+ {
+ // Remove all entries
+ visitEntries(new IVisitor()
+ {
+ public void entry(IPageMapEntry entry)
+ {
+ removeEntry(entry);
+ }
+ });
}
-
+
/**
* @param session
* Session to set
@@ -617,146 +310,100 @@
}
/**
- * @param entry
- * Add entry to access list
- * @param version
- * Version number being accessed
- */
- private final void access(final IPageMapEntry entry, final int version)
- {
- // See if the version being accessed is already in the stack
- boolean add = true;
- int id = entry.getNumericId();
- for (int i = accessStack.size()-1; i >=0 ; i--)
- {
- final Access access = (Access)accessStack.get(i);
-
- // If we found id and version in access stack
- if (access.id == id && access.version == version)
- {
- // No need to add since id and version are already in stack
- add = false;
-
- // Pop entries to reveal that version at top of stack
- // because the user used the back button
- while (i < accessStack.size() - 1)
- {
- // Pop unreachable access off top of stack
- final Access topAccess = popAccess();
-
- // Get entry for access
- final IPageMapEntry top = getEntry(topAccess.getId());
-
- // If it's a page we can remove version info
- if (top instanceof Page)
- {
- // If there's more than one version
- Page topPage = (Page)top;
- if (topPage.getVersions() > 1)
- {
- // Remove version the top access version (-1)
- topPage.getVersion(topAccess.getVersion()-1);
- }
- else
- {
- // Remove whole page
- remove(topPage);
- }
- }
- else
- {
- // Remove entry
- removeEntry(top);
- }
- }
- break;
- }
- }
+ * Removes this PageMap from the Session.
+ */
+ public final void remove()
+ {
+ // First clear all pages from the session for this pagemap
+ clear();
- // If the user did not use the back button
- if (add)
- {
- pushAccess(entry);
- }
+ // Then remove the pagemap itself
+ session.removePageMap(this);
}
/**
- * @return List of entries in this page map
+ * Removes the page from the pagemap
+ *
+ * @param page
+ * page to be removed from the pagemap
*/
- private final List getEntries()
+ public final void remove(final Page page)
{
- final List attributes = session.getAttributeNames();
- final List list = new ArrayList();
- for (final Iterator iterator = attributes.iterator(); iterator.hasNext();)
- {
- final String attribute = (String)iterator.next();
- if (attribute.startsWith(attributePrefix()))
- {
- list.add(session.getAttribute(attribute));
- }
- }
- return list;
+ // Remove the pagemap entry from session
+ removeEntry(page.getPageMapEntry());
}
/**
- * @return Access entry on top of the access stack
+ * @param entry
+ * The entry to remove
*/
- private final Access peekAccess()
- {
- return (Access)accessStack.peek();
- }
+ protected abstract void removeEntry(final IPageMapEntry entry);
+
+ /**
+ * @param page
+ * The page to put into this map
+ */
+ protected abstract void put(final Page page);
+
/**
- * Removes access entry on top of stack
+ * Retrieves page with given id.
*
- * @return Access entry on top of the access stack
+ * @param id
+ * The page identifier
+ * @param versionNumber
+ * The version to get
+ * @return Any page having the given id
*/
- private final Access popAccess()
- {
- session.dirtyPageMap(this);
- return (Access)accessStack.pop();
- }
+ protected abstract Page get(final int id, int versionNumber);
/**
- * @param entry
- * Entry that was accessed
+ * @return Size of this page map in bytes, including a sum of the sizes of
+ * all the pages it contains.
*/
- private final void pushAccess(IPageMapEntry entry)
+ public final long getSizeInBytes()
{
- // Create new access entry
- final Access access = new Access();
- access.id = entry.getNumericId();
- access.version = versionOf(entry);
- if(accessStack.size() > 0)
+ long size = Objects.sizeof(this);
+ Iterator it = getEntries().iterator();
+ while(it.hasNext())
{
- if(peekAccess().equals(access))
+ IPageMapEntry entry = (IPageMapEntry)it.next();
+ if (entry instanceof Page)
{
- return;
+ size += ((Page)entry).getSizeInBytes();
}
- int index = accessStack.indexOf(access);
- if (index >= 0)
+ else
{
- accessStack.remove(index);
+ size += Objects.sizeof(entry);
}
}
- accessStack.push(access);
- session.dirtyPageMap(this);
+ return size;
}
/**
- * @param entry
- * Page map entry
- * @return Version of entry
+ * @return List of entries in this page map
*/
- private final int versionOf(final IPageMapEntry entry)
+ private final List getEntries()
{
- if (entry instanceof Page)
+ final List attributes = session.getAttributeNames();
+ final List list = new ArrayList();
+ for (final Iterator iterator = attributes.iterator(); iterator.hasNext();)
{
- return ((Page)entry).getCurrentVersionNumber();
+ final String attribute = (String)iterator.next();
+ if (attribute.startsWith(attributePrefix()))
+ {
+ list.add(session.getAttribute(attribute));
+ }
}
+ return list;
+ }
+
- // If entry is not a page, it cannot have versions because the Page
- // is constructed on the fly.
- return 0;
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString()
+ {
+ return "[PageMap name=" + name + "]";
}
}
Modified: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/RequestCycle.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/RequestCycle.java?view=diff&rev=473514&r1=473513&r2=473514
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/RequestCycle.java (original)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/RequestCycle.java Fri Nov 10 13:46:18 2006
@@ -24,10 +24,12 @@
import org.apache.commons.logging.LogFactory;
import wicket.protocol.http.BufferedWebResponse;
+import wicket.protocol.http.IRequestLogger;
import wicket.request.ClientInfo;
import wicket.request.IRequestCodingStrategy;
import wicket.request.IRequestCycleProcessor;
import wicket.request.RequestParameters;
+import wicket.request.target.component.BookmarkableListenerInterfaceRequestTarget;
import wicket.request.target.component.BookmarkablePageRequestTarget;
import wicket.request.target.component.ComponentRequestTarget;
import wicket.request.target.component.IBookmarkablePageRequestTarget;
@@ -653,10 +655,26 @@
{
// Get Page holding component and mark it as stateful.
final Page page = component.getPage();
- page.setStateless(false);
+ final IRequestTarget target;
+ if (listener != IRedirectListener.INTERFACE && component.isStateless()
+ && page.isBookmarkable())
+ {
+ target = new BookmarkableListenerInterfaceRequestTarget(page.getPageMap().getName(),
+ page.getClass(), new PageParameters(), component, listener);
+ }
+ else
+ {
+ if (listener == IRedirectListener.INTERFACE)
+ {
+ page.setPageStateless(Boolean.FALSE);
+ }
- // Get the listener interface name
- final IRequestTarget target = new ListenerInterfaceRequestTarget(page, component, listener);
+ // trigger creation of the actual session in case it was deferred
+ session.getSessionStore().getSessionId(request, true);
+
+ // Get the listener interface name
+ target = new ListenerInterfaceRequestTarget(page, component, listener);
+ }
final IRequestCodingStrategy requestCodingStrategy = getProcessor()
.getRequestCodingStrategy();
return requestCodingStrategy.encode(this, target);
@@ -860,11 +878,11 @@
}
}
-// IRequestLogger requestLogger = getApplication().getRequestLogger();
-// if (requestLogger != null)
-// {
-// requestLogger.requestTime((System.currentTimeMillis() - startTime));
-// }
+ IRequestLogger requestLogger = getApplication().getRequestLogger();
+ if (requestLogger != null)
+ {
+ requestLogger.requestTime((System.currentTimeMillis() - startTime));
+ }
try
{
Modified: incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Session.java
URL: http://svn.apache.org/viewvc/incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Session.java?view=diff&rev=473514&r1=473513&r2=473514
==============================================================================
--- incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Session.java (original)
+++ incubator/wicket/branches/wicket-1.x/wicket/src/main/java/wicket/Session.java Fri Nov 10 13:46:18 2006
@@ -265,6 +265,46 @@
}
/**
+ * Force binding this session to the application's
+ * {@link ISessionStore session store}. A Wicket application can operate in
+ * a session-less mode as long as stateless pages are used. Session objects
+ * will be then created for each request, but they will only live for that
+ * request. You can recognize temporary sessions by calling
+ * {@link #isTemporary()} which basically checks whether the session's id is
+ * null. Hence, temporary sessions have no session id.
+ * <p>
+ * By calling this method, the session will be bound (made not-temporary) if
+ * it was not bound yet. It is useful for cases where you want to be
+ * absolutely sure this session object will be available in next requests.
+ * </p>
+ */
+ public final void bind()
+ {
+ ISessionStore store = getSessionStore();
+ Request request = RequestCycle.get().getRequest();
+ if (store.getSessionId(request, false) == null)
+ {
+ // explicitly create a session
+ this.id = store.getSessionId(request, true);
+ // bind it
+ store.bind(request, this);
+ }
+ }
+
+ /**
+ * Whether this session is temporary. A Wicket application can operate in a
+ * session-less mode as long as stateless pages are used. If this session
+ * object is temporary, it will not be available on a next request.
+ *
+ * @return Whether this session is temporary (which is the same as it's id
+ * being null)
+ */
+ public final boolean isTemporary()
+ {
+ return getId() == null;
+ }
+
+ /**
* Removes all pages from the session. Although this method should rarely be
* needed, it is available (possibly for security reasons).
*/
@@ -334,15 +374,17 @@
}
/**
- * Gets the unique id for this session from the underlying SessionStore
+ * Gets the unique id for this session from the underlying SessionStore. May
+ * be null if a concrete session is not yet created.
*
- * @return The unique id for this session
+ * @return The unique id for this session or null if it is a temporary
+ * session
*/
public final String getId()
{
if (id == null)
{
- id = getSessionStore().getSessionId(RequestCycle.get().getRequest());
+ id = getSessionStore().getSessionId(RequestCycle.get().getRequest(), false);
// we have one?
if (id != null)
@@ -617,7 +659,7 @@
}
// Create new page map
- final PageMap pageMap = new PageMap(name, this);
+ final PageMap pageMap = getSessionStore().createPageMap(name, this);
setAttribute(attributeForPageMapName(name), pageMap);
dirty();
return pageMap;
@@ -914,6 +956,8 @@
if (cycle != null)
{
getSessionStore().removeAttribute(cycle.getRequest(), name);
+ System.err.println("removing " + name + ", the current list " + getSessionStore().getAttributeNames(cycle.getRequest()));
+
}
}
@@ -942,9 +986,13 @@
Object current = store.getAttribute(request, name);
if (current == null)
{
- // this is a new instance. wherever it came from, bind the
- // session now
- store.bind(request, (Session)value);
+ String id = store.getSessionId(request, false);
+ if (id != null)
+ {
+ // this is a new instance. wherever it came from, bind the
+ // session now
+ store.bind(request, (Session)value);
+ }
}
}