Author: ivaynberg
Date: Sun Nov 14 07:19:10 2010
New Revision: 1034948

URL: http://svn.apache.org/viewvc?rev=1034948&view=rev
Log:

Issue: WICKET-3108

Modified:
    
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/PageMap.java
    
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/Session.java
    
wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/PageMapTest.java

Modified: 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/PageMap.java
URL: 
http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/PageMap.java?rev=1034948&r1=1034947&r2=1034948&view=diff
==============================================================================
--- 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/PageMap.java
 (original)
+++ 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/PageMap.java
 Sun Nov 14 07:19:10 2010
@@ -346,11 +346,14 @@ public abstract class PageMap implements
        }
 
        /**
-        * 
+        * Marking this PageMap as the most recently used only if it isn't 
already removed from session.
         */
        protected final void dirty()
        {
-               Session.get().dirtyPageMap(this);
+               if (Session.get().getPageMaps().contains(this))
+               {
+                       Session.get().dirtyPageMap(this);
+               }
        }
 
        /**

Modified: 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/Session.java
URL: 
http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/Session.java?rev=1034948&r1=1034947&r2=1034948&view=diff
==============================================================================
--- 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/Session.java
 (original)
+++ 
wicket/branches/wicket-1.4.x/wicket/src/main/java/org/apache/wicket/Session.java
 Sun Nov 14 07:19:10 2010
@@ -19,6 +19,7 @@ package org.apache.wicket;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -351,8 +352,8 @@ public abstract class Session implements
         */
        private transient Map<String, Object> temporarySessionAttributes;
 
-       /** A linked list for last used pagemap queue */
-       private final LinkedList/* <IPageMap> */<IPageMap> usedPageMaps = new 
LinkedList<IPageMap>();
+       /** A linked list for last used pagemap names queue */
+       private final LinkedList<String> usedPageMapNames = new 
LinkedList<String>();
 
        /**
         * Constructor. Note that {...@link RequestCycle} is not available 
until this constructor returns.
@@ -705,7 +706,7 @@ public abstract class Session implements
                IPageMap pageMap = pageMapForName(pageMapName, pageMapName == 
PageMap.DEFAULT_NAME);
                if (pageMap != null)
                {
-                       synchronized (usedPageMaps) // get a lock so be sure 
that only one
+                       synchronized (usedPageMapNames) // get a lock so be 
sure that only one
                        // is made
                        {
                                if (pageMapsUsedInRequest == null)
@@ -809,10 +810,24 @@ public abstract class Session implements
                                list.add((IPageMap)getAttribute(attribute));
                        }
                }
+               Collections.sort(list, new LruComparator());
                return list;
        }
 
        /**
+        * Sorting page maps respecting the least recently used sequence.
+        */
+       private class LruComparator implements Comparator<IPageMap>
+       {
+               public int compare(IPageMap pg1, IPageMap pg2)
+               {
+                       Integer pg1Index = 
usedPageMapNames.indexOf(pg1.getName());
+                       Integer pg2Index = 
usedPageMapNames.indexOf(pg2.getName());
+                       return pg1Index.compareTo(pg2Index);
+               }
+       }
+
+       /**
         * @return Size of this session, including all the pagemaps it contains
         */
        public final long getSizeInBytes()
@@ -919,18 +934,26 @@ public abstract class Session implements
        {
                // Check that session doesn't have too many page maps already, 
if so, evict
                final int maxPageMaps = 
getApplication().getSessionSettings().getMaxPageMaps();
-               synchronized (usedPageMaps)
+               synchronized (usedPageMapNames)
                {
-                       while (usedPageMaps.size() >= maxPageMaps)
+                       List<IPageMap> usedPageMaps = getPageMaps();
+                       int excessPagemaps = (usedPageMaps.size() + 1) - 
maxPageMaps;/*
+                                                                               
                                                                 * plus 1 
meaning the new
+                                                                               
                                                                 * one we are 
about to add
+                                                                               
                                                                 */
+                       if (excessPagemaps > 0)
                        {
-                               IPageMap pm = usedPageMaps.getFirst();
-                               pm.remove();
+                               for (int i = 0; i < excessPagemaps; i++)
+                               {
+                                       usedPageMaps.get(i).remove();
+                               }
                        }
                }
 
                // Create new page map
                final IPageMap pageMap = getSessionStore().createPageMap(name);
                setAttribute(attributeForPageMapName(name), pageMap);
+               // marking it as the most recently used
                dirtyPageMap(pageMap);
                dirty();
                return pageMap;
@@ -967,9 +990,9 @@ public abstract class Session implements
                        pagemapMetaData.pageMapNames.remove(pageMap.getName());
                }
 
-               synchronized (usedPageMaps)
+               synchronized (usedPageMapNames)
                {
-                       usedPageMaps.remove(pageMap);
+                       usedPageMapNames.remove(pageMap.getName());
                }
 
                // the page map also needs to be removed from the dirty objects 
list or
@@ -1147,7 +1170,7 @@ public abstract class Session implements
         *            Name of page map
         * @return Session attribute holding page map
         */
-       private final String attributeForPageMapName(final String pageMapName)
+       private static final String attributeForPageMapName(final String 
pageMapName)
        {
                return pageMapAttributePrefix + pageMapName;
        }
@@ -1362,10 +1385,10 @@ public abstract class Session implements
                        return;
                }
 
-               synchronized (usedPageMaps)
+               synchronized (usedPageMapNames)
                {
-                       usedPageMaps.remove(map);
-                       usedPageMaps.addLast(map);
+                       usedPageMapNames.remove(map.getName());
+                       usedPageMapNames.addLast(map.getName());
                }
 
                List<IClusterable> dirtyObjects = getDirtyObjectsList();

Modified: 
wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/PageMapTest.java
URL: 
http://svn.apache.org/viewvc/wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/PageMapTest.java?rev=1034948&r1=1034947&r2=1034948&view=diff
==============================================================================
--- 
wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/PageMapTest.java
 (original)
+++ 
wicket/branches/wicket-1.4.x/wicket/src/test/java/org/apache/wicket/PageMapTest.java
 Sun Nov 14 07:19:10 2010
@@ -16,23 +16,43 @@
  */
 package org.apache.wicket;
 
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
 import org.apache.wicket.markup.IMarkupResourceStreamProvider;
 import org.apache.wicket.markup.html.WebPage;
 import org.apache.wicket.markup.html.link.Link;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.protocol.http.SecondLevelCacheSessionStore;
 import org.apache.wicket.protocol.http.WebRequestCycle;
+import org.apache.wicket.protocol.http.pagestore.DiskPageStore;
 import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy;
+import org.apache.wicket.session.ISessionStore;
+import org.apache.wicket.util.lang.Objects;
 import org.apache.wicket.util.resource.IResourceStream;
 import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.tester.WicketTester;
 
 /**
  * https://issues.apache.org/jira/browse/WICKET-3108
  */
-public class PageMapTest extends WicketTestCase
+public class PageMapTest extends TestCase
 {
        private static final String TO_REMOVE_PAGE_MAP = "test-0";
        private static final String WORKING_PAGE_MAP = "test-1";
        // just to have an test limit, can be any value
        private static final int MAX_PAGE_MAPS = 2;
+       private static final long BIG_OBJECT_SIZE = 10000;
+
+       private WicketTester tester;
+
+       @Override
+       protected void setUp() throws Exception
+       {
+               tester = new WicketTester();
+       }
 
        /**
         * Making sure that the TO_REMOVE_PAGEMAP doesn't get back to session 
after a call to
@@ -58,7 +78,6 @@ public class PageMapTest extends WicketT
                }
        }
 
-
        /**
         * Requesting (maxPageMaps + 1) pages on distinct pagemaps
         */
@@ -87,6 +106,72 @@ public class PageMapTest extends WicketT
                assertTrue(MAX_PAGE_MAPS >= 
tester.getWicketSession().getPageMaps().size());
        }
 
+       /**
+        * Making sure that the PageMap don't get serialized with the session. 
If this test is failing,
+        * please look for any possible memory leak and than increase the 
BIG_OBJECT_SIZE.
+        * 
+        * @see https://issues.apache.org/jira/browse/WICKET-3160
+        */
+       public void testPagemapIsNotReferencedBySession()
+       {
+               tester = new WicketTester(new WicketTester.DummyWebApplication()
+               {
+                       @Override
+                       protected ISessionStore newSessionStore()
+                       {
+                               return new SecondLevelCacheSessionStore(this, 
new DiskPageStore());
+                       }
+               });
+               BigObject bigObject = new BigObject();
+               long bigObjectSize = Objects.sizeof(bigObject);
+               TestPage testPage = new TestPage();
+               testPage.setDefaultModel(new Model(bigObject));
+               tester.startPage(testPage);
+               long sessionSize = Objects.sizeof(tester.getWicketSession());
+               assertTrue(sessionSize < bigObjectSize);
+       }
+
+       public static class BigObject implements Serializable
+       {
+               private final ArrayList list = new ArrayList();
+               {
+                       for (int i = 0; i < BIG_OBJECT_SIZE; i++)
+                       {
+                               list.add(Byte.MIN_VALUE);
+                       }
+               }
+       }
+
+       /**
+        * Making sure that the getPageMaps return the correctly LRU page maps 
sequence
+        */
+       public void testLruCache()
+       {
+               for (int i = 0; i < MAX_PAGE_MAPS; i++)
+               {
+                       
tester.getWicketSession().newPageMap(Integer.toString(i));
+               }
+               Integer cacheIndex = 0;
+               for (IPageMap pageMap : tester.getWicketSession().getPageMaps())
+               {
+                       assertEquals(cacheIndex++, 
Integer.valueOf(pageMap.getName()));
+               }
+       }
+
+       /**
+        * Making sure that the dirty page map logic is marking the the page 
map as the last used.
+        */
+       public void testLruCacheOnDirtyPageMap()
+       {
+               goToPage(TestPage.class, "0");
+               goToPage(TestPage.class, "1");
+               goToPage(TestPage.class, "2");
+               goToPage(TestPage.class, "1");
+               assertEquals(3, tester.getWicketSession().getPageMaps().size());
+               assertEquals("0", 
tester.getWicketSession().getPageMaps().get(0).getName());
+               assertEquals("1", 
tester.getWicketSession().getPageMaps().get(2).getName());
+       }
+
        private void goToPage(Class pageClass, String pageMap)
        {
                WebRequestCycle cycle = tester.setupRequestAndResponse();


Reply via email to