Author: mgrigorov
Date: Tue Dec 14 13:26:27 2010
New Revision: 1049077

URL: http://svn.apache.org/viewvc?rev=1049077&view=rev
Log:
WICKET-3209 WebApplication MostRecentlyUsedMap based upon Key age not Value age

Introduce a map with maximum capacity and lifetime for its entries.
This map is used to store the responses when redirect to buffer strategy is 
used.

Added:
    wicket/trunk/wicket-util/src/test/java/org/apache/wicket/util/collections/
    
wicket/trunk/wicket-util/src/test/java/org/apache/wicket/util/collections/MostRecentlyUsedMapTest.java
   (with props)
    
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/StoredResponsesMap.java
   (with props)
    
wicket/trunk/wicket/src/test/java/org/apache/wicket/protocol/http/StoredResponsesMapTest.java
   (with props)
Modified:
    
wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/MostRecentlyUsedMap.java
    
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java

Modified: 
wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/MostRecentlyUsedMap.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/MostRecentlyUsedMap.java?rev=1049077&r1=1049076&r2=1049077&view=diff
==============================================================================
--- 
wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/MostRecentlyUsedMap.java
 (original)
+++ 
wicket/trunk/wicket-util/src/main/java/org/apache/wicket/util/collections/MostRecentlyUsedMap.java
 Tue Dec 14 13:26:27 2010
@@ -34,7 +34,7 @@ public class MostRecentlyUsedMap<K, V> e
        private static final long serialVersionUID = 1L;
 
        /** Value most recently removed from map */
-       V removedValue;
+       protected V removedValue;
 
        /** Maximum number of entries allowed in this map */
        private final int maxEntries;

Added: 
wicket/trunk/wicket-util/src/test/java/org/apache/wicket/util/collections/MostRecentlyUsedMapTest.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket-util/src/test/java/org/apache/wicket/util/collections/MostRecentlyUsedMapTest.java?rev=1049077&view=auto
==============================================================================
--- 
wicket/trunk/wicket-util/src/test/java/org/apache/wicket/util/collections/MostRecentlyUsedMapTest.java
 (added)
+++ 
wicket/trunk/wicket-util/src/test/java/org/apache/wicket/util/collections/MostRecentlyUsedMapTest.java
 Tue Dec 14 13:26:27 2010
@@ -0,0 +1,48 @@
+/*
+ * 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.util.collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+/**
+ * 
+ */
+public class MostRecentlyUsedMapTest
+{
+       /**
+        * Tests that {...@link MostRecentlyUsedMap} contains at most 2 entries
+        * 
+        * @see <a 
href="https://issues.apache.org/jira/browse/WICKET-3209";>WICKET-3209</a>
+        */
+       @Test
+       public void max2Entries()
+       {
+               MostRecentlyUsedMap<String, String> map = new 
MostRecentlyUsedMap<String, String>(2);
+               assertEquals(0, map.size());
+               map.put("1", "one");
+               assertEquals(1, map.size());
+               map.put("2", "two");
+               assertEquals(2, map.size());
+               map.put("3", "three");
+               assertEquals(2, map.size());
+               assertTrue(map.containsKey("2"));
+               assertTrue(map.containsKey("3"));
+       }
+}

Propchange: 
wicket/trunk/wicket-util/src/test/java/org/apache/wicket/util/collections/MostRecentlyUsedMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/StoredResponsesMap.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/StoredResponsesMap.java?rev=1049077&view=auto
==============================================================================
--- 
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/StoredResponsesMap.java
 (added)
+++ 
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/StoredResponsesMap.java
 Tue Dec 14 13:26:27 2010
@@ -0,0 +1,133 @@
+/*
+ * 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.protocol.http;
+
+import java.util.Map;
+
+import org.apache.wicket.util.collections.MostRecentlyUsedMap;
+import org.apache.wicket.util.time.Duration;
+import org.apache.wicket.util.time.Time;
+
+/**
+ * A map that contains the buffered responses. It has a constraint on the 
maximum entries that it
+ * can contain, and a constraint on the duration of time an entry is 
considered valid/non-expired
+ */
+class StoredResponsesMap extends MostRecentlyUsedMap<String, Object>
+{
+       private static final long serialVersionUID = 1L;
+
+       /**
+        * The actual object that is stored as a value of the map. It wraps the 
buffered response and
+        * assigns it a creation time.
+        */
+       private static class Value
+       {
+               /** the original response to store */
+               private BufferedWebResponse response;
+
+               /** the time when this response is stored */
+               private Time creationTime;
+       }
+
+       /**
+        * The duration of time before a {...@link Value} is considered as 
expired
+        */
+       private final Duration lifetime;
+
+       /**
+        * Construct.
+        * 
+        * @param maxEntries
+        *            how much entries this map can contain
+        * @param lifetime
+        *            the duration of time to keep an entry in the map before 
considering it expired
+        */
+       public StoredResponsesMap(int maxEntries, Duration lifetime)
+       {
+               super(maxEntries);
+
+               this.lifetime = lifetime;
+       }
+
+       @Override
+       protected boolean removeEldestEntry(java.util.Map.Entry<String, Object> 
eldest)
+       {
+               boolean removed = super.removeEldestEntry(eldest);
+               if (removed == false)
+               {
+                       Value value = (Value)eldest.getValue();
+                       Duration elapsedTime = 
Time.now().subtract(value.creationTime);
+                       if (lifetime.lessThanOrEqual(elapsedTime))
+                       {
+                               removedValue = value.response;
+                               removed = true;
+                       }
+               }
+               return removed;
+       }
+
+       @Override
+       public BufferedWebResponse put(String key, Object bufferedResponse)
+       {
+               if (!(bufferedResponse instanceof BufferedWebResponse))
+               {
+                       throw new 
IllegalArgumentException(StoredResponsesMap.class.getSimpleName() +
+                               " can store only instances of " + 
BufferedWebResponse.class.getSimpleName());
+               }
+
+               Value value = new Value();
+               value.creationTime = Time.now();
+               value.response = (BufferedWebResponse)bufferedResponse;
+               Value oldValue = (Value)super.put(key, value);
+
+               return oldValue != null ? oldValue.response : null;
+       }
+
+       @Override
+       public BufferedWebResponse get(Object key)
+       {
+               BufferedWebResponse result = null;
+               Value value = (Value)super.get(key);
+               if (value != null)
+               {
+                       Duration elapsedTime = 
Time.now().subtract(value.creationTime);
+                       if (lifetime.greaterThan(elapsedTime))
+                       {
+                               result = value.response;
+                       }
+                       else
+                       {
+                               // expired, remove it
+                               remove(key);
+                       }
+               }
+               return result;
+       }
+
+       @Override
+       public BufferedWebResponse remove(Object key)
+       {
+               Value removedValue = (Value)super.remove(key);
+               return removedValue != null ? removedValue.response : null;
+       }
+
+       @Override
+       public void putAll(Map<? extends String, ? extends Object> m)
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Propchange: 
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/StoredResponsesMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java?rev=1049077&r1=1049076&r2=1049077&view=diff
==============================================================================
--- 
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
 (original)
+++ 
wicket/trunk/wicket/src/main/java/org/apache/wicket/protocol/http/WebApplication.java
 Tue Dec 14 13:26:27 2010
@@ -16,10 +16,6 @@
  */
 package org.apache.wicket.protocol.http;
 
-import java.util.Collections;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
 import javax.servlet.ServletContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -54,13 +50,12 @@ import org.apache.wicket.request.resourc
 import org.apache.wicket.session.HttpSessionStore;
 import org.apache.wicket.session.ISessionStore;
 import org.apache.wicket.util.IProvider;
-import org.apache.wicket.util.collections.MostRecentlyUsedMap;
 import org.apache.wicket.util.file.FileUploadCleaner;
 import org.apache.wicket.util.file.IFileUploadCleaner;
 import org.apache.wicket.util.file.IResourceFinder;
 import org.apache.wicket.util.file.WebApplicationPath;
 import org.apache.wicket.util.lang.Args;
-import org.apache.wicket.util.lang.Generics;
+import org.apache.wicket.util.time.Duration;
 import org.apache.wicket.util.watch.IModificationWatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -133,12 +128,6 @@ public abstract class WebApplication ext
        }
 
        /**
-        * Map of buffered responses that are in progress per session. Buffered 
responses are
-        * temporarily stored
-        */
-       private final ConcurrentHashMap<String, Map<String, 
BufferedHttpServletResponse>> bufferedResponses = 
Generics.newConcurrentHashMap();
-
-       /**
         * the prefix for storing variables in the actual session (typically 
{...@link HttpSession} for
         * this application instance.
         */
@@ -391,8 +380,6 @@ public abstract class WebApplication ext
        {
                super.sessionUnbound(sessionId);
 
-               bufferedResponses.remove(sessionId);
-
                IRequestLogger logger = getRequestLogger();
                if (logger != null)
                {
@@ -438,7 +425,6 @@ public abstract class WebApplication ext
                {
                        resourceWatcher.destroy();
                }
-               bufferedResponses.clear();
 
                IFileUploadCleaner fileUploadCleaner = 
getResourceSettings().getFileUploadCleaner();
                if (fileUploadCleaner != null)
@@ -577,35 +563,6 @@ public abstract class WebApplication ext
        }
 
        /**
-        * Add a buffered response to the redirect buffer.
-        * 
-        * @param sessionId
-        *            the session id
-        * @param bufferId
-        *            the id that should be used for storing the buffer
-        * @param renderedResponse
-        *            the response to buffer
-        */
-       final void addBufferedResponse(String sessionId, String bufferId,
-               BufferedHttpServletResponse renderedResponse)
-       {
-               Map<String, BufferedHttpServletResponse> responsesPerSession = 
bufferedResponses.get(sessionId);
-               if (responsesPerSession == null)
-               {
-                       responsesPerSession = Collections.synchronizedMap(new 
MostRecentlyUsedMap<String, BufferedHttpServletResponse>(
-                               4));
-                       Map<String, BufferedHttpServletResponse> previousValue 
= bufferedResponses.putIfAbsent(
-                               sessionId, responsesPerSession);
-                       if (previousValue != null)
-                       {
-                               responsesPerSession = previousValue;
-                       }
-               }
-               String bufferKey = bufferId.startsWith("/") ? 
bufferId.substring(1) : bufferId;
-               responsesPerSession.put(bufferKey, renderedResponse);
-       }
-
-       /**
         * Log that this application is started.
         */
        final void logStarted()
@@ -646,8 +603,12 @@ public abstract class WebApplication ext
                        + 
"********************************************************************\n");
        }
 
-       // TODO: Do this properly
-       private final Map<String, BufferedWebResponse> storedResponses = new 
ConcurrentHashMap<String, BufferedWebResponse>();
+       /*
+        * Can contain at most 1000 responses and each entry can live at most 
one minute for now there
+        * is no need to configure these parameters externally
+        */
+       private final StoredResponsesMap storedResponses = new 
StoredResponsesMap(1000,
+               Duration.seconds(60));
 
        /**
         * 
@@ -685,35 +646,6 @@ public abstract class WebApplication ext
                storedResponses.put(key, response);
        }
 
-       // TODO remove after deprecation release
-
-       /**
-        * Returns the redirect map where the buffered render pages are stored 
in and removes it
-        * immediately.
-        * 
-        * @param sessionId
-        *            the session id
-        * 
-        * @param bufferId
-        *            the id of the buffer as passed in as a request parameter
-        * @return the buffered response or null if not found (when this 
request is on a different box
-        *         than the original request came in
-        */
-       final BufferedHttpServletResponse popBufferedResponse(String sessionId, 
String bufferId)
-       {
-               Map<String, BufferedHttpServletResponse> responsesPerSession = 
bufferedResponses.get(sessionId);
-               if (responsesPerSession != null)
-               {
-                       BufferedHttpServletResponse buffered = 
responsesPerSession.remove(bufferId);
-                       if (responsesPerSession.size() == 0)
-                       {
-                               bufferedResponses.remove(sessionId);
-                       }
-                       return buffered;
-               }
-               return null;
-       }
-
        @Override
        public String getMimeType(String fileName)
        {

Added: 
wicket/trunk/wicket/src/test/java/org/apache/wicket/protocol/http/StoredResponsesMapTest.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/protocol/http/StoredResponsesMapTest.java?rev=1049077&view=auto
==============================================================================
--- 
wicket/trunk/wicket/src/test/java/org/apache/wicket/protocol/http/StoredResponsesMapTest.java
 (added)
+++ 
wicket/trunk/wicket/src/test/java/org/apache/wicket/protocol/http/StoredResponsesMapTest.java
 Tue Dec 14 13:26:27 2010
@@ -0,0 +1,78 @@
+/*
+ * 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.protocol.http;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.wicket.util.time.Duration;
+import org.junit.Test;
+
+/**
+ * @see <a 
href="https://issues.apache.org/jira/browse/WICKET-3209";>WICKET-3209</a>
+ */
+public class StoredResponsesMapTest
+{
+       /**
+        * Verifies that {...@link StoredResponsesMap} will expire the oldest 
entry if it is older than 2
+        * seconds
+        * 
+        * @throws Exception
+        */
+       @Test
+       public void entriesLife2Seconds() throws Exception
+       {
+               StoredResponsesMap map = new StoredResponsesMap(1000, 
Duration.seconds(2));
+               assertEquals(0, map.size());
+               map.put("1", new BufferedWebResponse(null));
+               assertEquals(1, map.size());
+               TimeUnit.SECONDS.sleep(3);
+               map.put("2", new BufferedWebResponse(null));
+               assertEquals(1, map.size());
+               assertTrue(map.containsKey("2"));
+       }
+
+       /**
+        * Verifies that getting a value which is expired will return 
<code>null</code>.
+        * 
+        * @throws Exception
+        */
+       @Test
+       public void getExpiredValue() throws Exception
+       {
+               StoredResponsesMap map = new StoredResponsesMap(1000, 
Duration.milliseconds(50));
+               assertEquals(0, map.size());
+               map.put("1", new BufferedWebResponse(null));
+               assertEquals(1, map.size());
+               TimeUnit.MILLISECONDS.sleep(51);
+               Object value = map.get("1");
+               assertNull(value);
+       }
+
+       /**
+        * Verifies that {...@link StoredResponsesMap} can have only {...@link 
BufferedWebResponse} values
+        */
+       @Test(expected = IllegalArgumentException.class)
+       public void cannotPutArbitraryValue()
+       {
+               StoredResponsesMap map = new StoredResponsesMap(1000, 
Duration.days(1));
+               map.put("1", new Object());
+       }
+}

Propchange: 
wicket/trunk/wicket/src/test/java/org/apache/wicket/protocol/http/StoredResponsesMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to