Package org.apache.wicket

Source Code of org.apache.wicket.AccessStackPageMap$Access

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.wicket;

import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.session.pagemap.IPageMapEntry;
import org.apache.wicket.util.collections.ArrayListStack;


/**
* A container for pages held in the session. IPageMap is a parameter to several
* methods in the Wicket API. You can get a IPageMap 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 IPageMap getMyPageMap()
* {
*   return IPageMap.forName(&quot;myPageMapName&quot;);
* }
* </pre>
*
* If the page map with the given name is not found, one will be automatically
* created.
*
* @author Jonathan Locke
*/
public class AccessStackPageMap extends PageMap implements IClusterable
{
  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 IClusterable
  {
    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
   */
  public AccessStackPageMap(final String name)
  {
    super(name);
  }

  /**
   * 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
   */
  public 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();

      // Entry has been accessed
      access(entry, versionNumber);

      // Get the version of the page requested from the page
      final Page version = page.getVersion(versionNumber);



      // 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
   */
  public 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;
  }
}
TOP

Related Classes of org.apache.wicket.AccessStackPageMap$Access

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.