001 package org.apache.jcs.engine.memory.shrinking;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.io.IOException;
023
024 import junit.framework.TestCase;
025
026 import org.apache.jcs.engine.CacheElement;
027 import org.apache.jcs.engine.CompositeCacheAttributes;
028 import org.apache.jcs.engine.ElementAttributes;
029 import org.apache.jcs.engine.behavior.ICacheElement;
030 import org.apache.jcs.engine.control.event.ElementEventHandlerMockImpl;
031 import org.apache.jcs.engine.memory.MockMemoryCache;
032
033 /**
034 * This tests the functionality of the shrinker thread.
035 * <p>
036 * @author Aaron Smuts
037 */
038 public class ShrinkerThreadUnitTest
039 extends TestCase
040 {
041 /** verify the check for removal
042 * <p>
043 * @throws IOException */
044 public void testCheckForRemoval_Expired() throws IOException
045 {
046 // SETUP
047 MockMemoryCache<String, String> memory = new MockMemoryCache<String, String>();
048 CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
049 cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
050 cacheAttr.setMaxSpoolPerRun( 10 );
051 memory.setCacheAttributes( cacheAttr );
052
053 ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( memory );
054
055 String key = "key";
056 String value = "value";
057
058 ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
059 ElementAttributes elementAttr = new ElementAttributes();
060 elementAttr.setIsEternal( false );
061 element.setElementAttributes( elementAttr );
062 element.getElementAttributes().setMaxLifeSeconds( 1 );
063
064 long now = System.currentTimeMillis();
065 // add two seconds
066 now += 2000;
067
068 // DO WORK
069 boolean result = shrinker.checkForRemoval( element, now );
070
071 // VERIFY
072 assertTrue( "Item should have expired.", result );
073 }
074
075 /** verify the check for removal
076 * <p>
077 * @throws IOException */
078 public void testCheckForRemoval_NotExpired() throws IOException
079 {
080 // SETUP
081 MockMemoryCache<String, String> memory = new MockMemoryCache<String, String>();
082 CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
083 cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
084 cacheAttr.setMaxSpoolPerRun( 10 );
085 memory.setCacheAttributes( cacheAttr );
086
087 ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( memory );
088
089 String key = "key";
090 String value = "value";
091
092 ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
093 ElementAttributes elementAttr = new ElementAttributes();
094 elementAttr.setIsEternal( false );
095 element.setElementAttributes( elementAttr );
096 element.getElementAttributes().setMaxLifeSeconds( 1 );
097
098 long now = System.currentTimeMillis();
099 // subtract two seconds
100 now -= 2000;
101
102 // DO WORK
103 boolean result = shrinker.checkForRemoval( element, now );
104
105 // VERIFY
106 assertFalse( "Item should not have expired.", result );
107 }
108
109 /** verify the check for removal
110 * <p>
111 * @throws IOException */
112 public void testCheckForRemoval_IdleTooLong() throws IOException
113 {
114 // SETUP
115 MockMemoryCache<String, String> memory = new MockMemoryCache<String, String>();
116 CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
117 cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
118 cacheAttr.setMaxSpoolPerRun( 10 );
119 memory.setCacheAttributes( cacheAttr );
120
121 ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( memory );
122
123 String key = "key";
124 String value = "value";
125
126 ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
127 ElementAttributes elementAttr = new ElementAttributes();
128 elementAttr.setIsEternal( false );
129 element.setElementAttributes( elementAttr );
130 element.getElementAttributes().setMaxLifeSeconds( 100 );
131 element.getElementAttributes().setIdleTime( 1 );
132
133 long now = System.currentTimeMillis();
134 // add two seconds
135 now += 2000;
136
137 // DO WORK
138 boolean result = shrinker.checkForRemoval( element, now );
139
140 // VERIFY
141 assertTrue( "Item should have expired.", result );
142 }
143
144 /** verify the check for removal
145 * <p>
146 * @throws IOException */
147 public void testCheckForRemoval_NotIdleTooLong() throws IOException
148 {
149 // SETUP
150 MockMemoryCache<String, String> memory = new MockMemoryCache<String, String>();
151 CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
152 cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
153 cacheAttr.setMaxSpoolPerRun( 10 );
154 memory.setCacheAttributes( cacheAttr );
155
156 ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( memory );
157
158 String key = "key";
159 String value = "value";
160
161 ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
162 ElementAttributes elementAttr = new ElementAttributes();
163 elementAttr.setIsEternal( false );
164 element.setElementAttributes( elementAttr );
165 element.getElementAttributes().setMaxLifeSeconds( 100 );
166 element.getElementAttributes().setIdleTime( 1 );
167
168 long now = System.currentTimeMillis();
169 // subtract two seconds
170 now -= 2000;
171
172 // DO WORK
173 boolean result = shrinker.checkForRemoval( element, now );
174
175 // VERIFY
176 assertFalse( "Item should not have expired.", result );
177 }
178
179 /**
180 * Setup cache attributes in mock. Create the shrinker with the mock. Add some elements into the
181 * mock memory cache see that they get spooled.
182 * <p>
183 * @throws Exception
184 */
185 public void testSimpleShrink()
186 throws Exception
187 {
188 // SETUP
189 MockMemoryCache<String, String> memory = new MockMemoryCache<String, String>();
190
191 CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
192 cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
193 cacheAttr.setMaxSpoolPerRun( 10 );
194
195 memory.setCacheAttributes( cacheAttr );
196
197 String key = "key";
198 String value = "value";
199
200 ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
201
202 ElementAttributes elementAttr = new ElementAttributes();
203 elementAttr.setIsEternal( false );
204 element.setElementAttributes( elementAttr );
205 element.getElementAttributes().setMaxLifeSeconds( 1 );
206 memory.update( element );
207
208 ICacheElement<String, String> returnedElement1 = memory.get( key );
209 assertNotNull( "We should have received an element", returnedElement1 );
210
211 // set this to 2 seconds ago.
212 elementAttr.lastAccessTime = System.currentTimeMillis() - 2000;
213
214 // DO WORK
215 ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( memory );
216 Thread runner = new Thread( shrinker );
217 runner.run();
218
219 Thread.sleep( 500 );
220
221 // VERIFY
222 ICacheElement<String, String> returnedElement2 = memory.get( key );
223 assertTrue( "Waterfall should have been called.", memory.waterfallCallCount > 0 );
224 assertNull( "We not should have received an element. It should have been spooled.", returnedElement2 );
225 }
226
227 /**
228 * Add 10 to the memory cache. Set the spool per run limit to 3.
229 * <p>
230 * @throws Exception
231 */
232 public void testSimpleShrinkMultiple()
233 throws Exception
234 {
235 // SETUP
236 MockMemoryCache<String, String> memory = new MockMemoryCache<String, String>();
237
238 CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
239 cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
240 cacheAttr.setMaxSpoolPerRun( 3 );
241
242 memory.setCacheAttributes( cacheAttr );
243
244 for ( int i = 0; i < 10; i++ )
245 {
246 String key = "key" + i;
247 String value = "value";
248
249 ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
250
251 ElementAttributes elementAttr = new ElementAttributes();
252 elementAttr.setIsEternal( false );
253 element.setElementAttributes( elementAttr );
254 element.getElementAttributes().setMaxLifeSeconds( 1 );
255 memory.update( element );
256
257 ICacheElement<String, String> returnedElement1 = memory.get( key );
258 assertNotNull( "We should have received an element", returnedElement1 );
259
260 // set this to 2 seconds ago.
261 elementAttr.lastAccessTime = System.currentTimeMillis() - 2000;
262 }
263
264 // DO WORK
265 ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( memory );
266 Thread runner = new Thread( shrinker );
267 runner.run();
268
269 // VERIFY
270 Thread.sleep( 500 );
271 assertEquals( "Waterfall called the wrong number of times.", 3, memory.waterfallCallCount );
272 assertEquals( "Wrong number of elements remain.", 7, memory.getSize() );
273 }
274
275 /**
276 * Add a mock event handler to the items. Verify that it gets called.
277 * <p>
278 * This is only testing the spooled background event
279 * <p>
280 * @throws Exception
281 */
282 public void testSimpleShrinkMultipleWithEventHandler()
283 throws Exception
284 {
285 // SETUP
286 MockMemoryCache<String, String> memory = new MockMemoryCache<String, String>();
287
288 CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
289 cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
290 cacheAttr.setMaxSpoolPerRun( 3 );
291
292 memory.setCacheAttributes( cacheAttr );
293
294 ElementEventHandlerMockImpl handler = new ElementEventHandlerMockImpl();
295
296 for ( int i = 0; i < 10; i++ )
297 {
298 String key = "key" + i;
299 String value = "value";
300
301 ICacheElement<String, String> element = new CacheElement<String, String>( "testRegion", key, value );
302
303 ElementAttributes elementAttr = new ElementAttributes();
304 elementAttr.addElementEventHandler( handler );
305 elementAttr.setIsEternal( false );
306 element.setElementAttributes( elementAttr );
307 element.getElementAttributes().setMaxLifeSeconds( 1 );
308 memory.update( element );
309
310 ICacheElement<String, String> returnedElement1 = memory.get( key );
311 assertNotNull( "We should have received an element", returnedElement1 );
312
313 // set this to 2 seconds ago.
314 elementAttr.lastAccessTime = System.currentTimeMillis() - 2000;
315 }
316
317 // DO WORK
318 ShrinkerThread<String, String> shrinker = new ShrinkerThread<String, String>( memory );
319 Thread runner = new Thread( shrinker );
320 runner.run();
321
322 // VERIFY
323 Thread.sleep( 500 );
324 assertEquals( "Waterfall called the wrong number of times.", 3, memory.waterfallCallCount );
325 // the shrinker delegates the the composite cache on the memory cache to put the
326 // event on the queue. This make it hard to test. TODO we need to change this to make it easier to verify.
327 //assertEquals( "Event handler ExceededIdleTimeBackground called the wrong number of times.", 3, handler.getExceededIdleTimeBackgroundCount() );
328 assertEquals( "Wrong number of elements remain.", 7, memory.getSize() );
329 }
330 }