View Javadoc
1   package org.apache.commons.jcs3.engine.memory.shrinking;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import junit.framework.TestCase;
23  
24  import org.apache.commons.jcs3.engine.ElementAttributesUtils;
25  import org.apache.commons.jcs3.engine.control.event.ElementEventHandlerMockImpl;
26  import org.apache.commons.jcs3.engine.memory.MockMemoryCache;
27  import org.apache.commons.jcs3.engine.CacheElement;
28  import org.apache.commons.jcs3.engine.CompositeCacheAttributes;
29  import org.apache.commons.jcs3.engine.ElementAttributes;
30  import org.apache.commons.jcs3.engine.behavior.ICacheElement;
31  import org.apache.commons.jcs3.engine.control.CompositeCache;
32  import org.apache.commons.jcs3.engine.control.event.behavior.ElementEventType;
33  import java.io.IOException;
34  
35  /**
36   * This tests the functionality of the shrinker thread.
37   */
38  public class ShrinkerThreadUnitTest
39      extends TestCase
40  {
41      /** verify the check for removal
42       * <p>
43       * @throws IOException */
44      public void testCheckForRemoval_Expired() throws IOException
45      {
46          // SETUP
47          final CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
48          cacheAttr.setCacheName("testRegion");
49          cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
50          cacheAttr.setMaxSpoolPerRun( 10 );
51  
52          final CompositeCache<String, String> cache = new CompositeCache<>(cacheAttr, new ElementAttributes());
53  
54          final String key = "key";
55          final String value = "value";
56  
57          final ICacheElement<String, String> element = new CacheElement<>( "testRegion", key, value );
58          final ElementAttributes elementAttr = new ElementAttributes();
59          elementAttr.setIsEternal( false );
60          element.setElementAttributes( elementAttr );
61          element.getElementAttributes().setMaxLife(1);
62  
63          long now = System.currentTimeMillis();
64          // add two seconds
65          now += 2000;
66  
67          // DO WORK
68          final boolean result = cache.isExpired( element, now,
69                  ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
70                  ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
71  
72          // VERIFY
73          assertTrue( "Item should have expired.", result );
74      }
75  
76      /** verify the check for removal
77       * <p>
78       * @throws IOException */
79      public void testCheckForRemoval_NotExpired() throws IOException
80      {
81          // SETUP
82          final CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
83          cacheAttr.setCacheName("testRegion");
84          cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
85          cacheAttr.setMaxSpoolPerRun( 10 );
86  
87          final CompositeCache<String, String> cache = new CompositeCache<>(cacheAttr, new ElementAttributes());
88  
89          final String key = "key";
90          final String value = "value";
91  
92          final ICacheElement<String, String> element = new CacheElement<>( "testRegion", key, value );
93          final ElementAttributes elementAttr = new ElementAttributes();
94          elementAttr.setIsEternal( false );
95          element.setElementAttributes( elementAttr );
96          element.getElementAttributes().setMaxLife(1);
97  
98          long now = System.currentTimeMillis();
99          // subtract two seconds
100         now -= 2000;
101 
102         // DO WORK
103         final boolean result = cache.isExpired( element, now,
104                 ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
105                 ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
106 
107         // VERIFY
108         assertFalse( "Item should not have expired.", result );
109     }
110 
111     /** verify the check for removal
112      * <p>
113      * @throws IOException */
114     public void testCheckForRemoval_IdleTooLong() throws IOException
115     {
116         // SETUP
117         final CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
118         cacheAttr.setCacheName("testRegion");
119         cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
120         cacheAttr.setMaxSpoolPerRun( 10 );
121 
122         final CompositeCache<String, String> cache = new CompositeCache<>(cacheAttr, new ElementAttributes());
123 
124         final String key = "key";
125         final String value = "value";
126 
127         final ICacheElement<String, String> element = new CacheElement<>( "testRegion", key, value );
128         final ElementAttributes elementAttr = new ElementAttributes();
129         elementAttr.setIsEternal( false );
130         element.setElementAttributes( elementAttr );
131         element.getElementAttributes().setMaxLife(100);
132         element.getElementAttributes().setIdleTime( 1 );
133 
134         long now = System.currentTimeMillis();
135         // add two seconds
136         now += 2000;
137 
138         // DO WORK
139         final boolean result = cache.isExpired( element, now,
140                 ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
141                 ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
142 
143         // VERIFY
144         assertTrue( "Item should have expired.", result );
145     }
146 
147     /** verify the check for removal
148      * <p>
149      * @throws IOException */
150     public void testCheckForRemoval_NotIdleTooLong() throws IOException
151     {
152         // SETUP
153         final CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
154         cacheAttr.setCacheName("testRegion");
155         cacheAttr.setMaxMemoryIdleTimeSeconds( 10 );
156         cacheAttr.setMaxSpoolPerRun( 10 );
157 
158         final CompositeCache<String, String> cache = new CompositeCache<>(cacheAttr, new ElementAttributes());
159 
160         final String key = "key";
161         final String value = "value";
162 
163         final ICacheElement<String, String> element = new CacheElement<>( "testRegion", key, value );
164         final ElementAttributes elementAttr = new ElementAttributes();
165         elementAttr.setIsEternal( false );
166         element.setElementAttributes( elementAttr );
167         element.getElementAttributes().setMaxLife(100);
168         element.getElementAttributes().setIdleTime( 1 );
169 
170         long now = System.currentTimeMillis();
171         // subtract two seconds
172         now -= 2000;
173 
174         // DO WORK
175         final boolean result = cache.isExpired( element, now,
176                 ElementEventType.EXCEEDED_MAXLIFE_BACKGROUND,
177                 ElementEventType.EXCEEDED_IDLETIME_BACKGROUND );
178 
179         // VERIFY
180         assertFalse( "Item should not have expired.", result );
181     }
182 
183     /**
184      * Setup cache attributes in mock. Create the shrinker with the mock. Add some elements into the
185      * mock memory cache see that they get spooled.
186      * <p>
187      * @throws Exception
188      */
189     public void testSimpleShrink()
190         throws Exception
191     {
192         // SETUP
193         final CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
194         cacheAttr.setCacheName("testRegion");
195         cacheAttr.setMemoryCacheName("org.apache.commons.jcs3.engine.memory.MockMemoryCache");
196         cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
197         cacheAttr.setMaxSpoolPerRun( 10 );
198 
199         final CompositeCache<String, String> cache = new CompositeCache<>(cacheAttr, new ElementAttributes());
200         final MockMemoryCache<String, String> memory = (MockMemoryCache<String, String>)cache.getMemoryCache();
201 
202         final String key = "key";
203         final String value = "value";
204 
205         final ICacheElement<String, String> element = new CacheElement<>( "testRegion", key, value );
206 
207         final ElementAttributes elementAttr = new ElementAttributes();
208         elementAttr.setIsEternal( false );
209         element.setElementAttributes( elementAttr );
210         element.getElementAttributes().setMaxLife(1);
211         memory.update( element );
212 
213         final ICacheElement<String, String> returnedElement1 = memory.get( key );
214         assertNotNull( "We should have received an element", returnedElement1 );
215 
216         // set this to 2 seconds ago.
217         ElementAttributesUtils.setLastAccessTime( elementAttr,  System.currentTimeMillis() - 2000 );
218 
219         // DO WORK
220         final ShrinkerThread<String, String> shrinker = new ShrinkerThread<>( cache );
221         shrinker.run();
222 
223         Thread.sleep( 500 );
224 
225         // VERIFY
226         final ICacheElement<String, String> returnedElement2 = memory.get( key );
227         assertTrue( "Waterfall should have been called.", memory.waterfallCallCount > 0 );
228         assertNull( "We not should have received an element.  It should have been spooled.", returnedElement2 );
229     }
230 
231     /**
232      * Add 10 to the memory cache. Set the spool per run limit to 3.
233      * <p>
234      * @throws Exception
235      */
236     public void testSimpleShrinkMultiple()
237         throws Exception
238     {
239         // SETUP
240         final CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
241         cacheAttr.setCacheName("testRegion");
242         cacheAttr.setMemoryCacheName("org.apache.commons.jcs3.engine.memory.MockMemoryCache");
243         cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
244         cacheAttr.setMaxSpoolPerRun( 3 );
245 
246         final CompositeCache<String, String> cache = new CompositeCache<>(cacheAttr, new ElementAttributes());
247         final MockMemoryCache<String, String> memory = (MockMemoryCache<String, String>)cache.getMemoryCache();
248 
249         for ( int i = 0; i < 10; i++ )
250         {
251             final String key = "key" + i;
252             final String value = "value";
253 
254             final ICacheElement<String, String> element = new CacheElement<>( "testRegion", key, value );
255 
256             final ElementAttributes elementAttr = new ElementAttributes();
257             elementAttr.setIsEternal( false );
258             element.setElementAttributes( elementAttr );
259             element.getElementAttributes().setMaxLife(1);
260             memory.update( element );
261 
262             final ICacheElement<String, String> returnedElement1 = memory.get( key );
263             assertNotNull( "We should have received an element", returnedElement1 );
264 
265             // set this to 2 seconds ago.
266             ElementAttributesUtils.setLastAccessTime( elementAttr,  System.currentTimeMillis() - 2000 );
267         }
268 
269         // DO WORK
270         final ShrinkerThread<String, String> shrinker = new ShrinkerThread<>( cache );
271         shrinker.run();
272 
273         // VERIFY
274         Thread.sleep( 500 );
275         assertEquals( "Waterfall called the wrong number of times.", 3, memory.waterfallCallCount );
276         assertEquals( "Wrong number of elements remain.", 7, memory.getSize() );
277     }
278 
279     /**
280      * Add a mock event handler to the items. Verify that it gets called.
281      * <p>
282      * This is only testing the spooled background event
283      * <p>
284      * @throws Exception
285      */
286     public void testSimpleShrinkMultipleWithEventHandler()
287         throws Exception
288     {
289         // SETUP
290         final CompositeCacheAttributes cacheAttr = new CompositeCacheAttributes();
291         cacheAttr.setCacheName("testRegion");
292         cacheAttr.setMemoryCacheName("org.apache.commons.jcs3.engine.memory.MockMemoryCache");
293         cacheAttr.setMaxMemoryIdleTimeSeconds( 1 );
294         cacheAttr.setMaxSpoolPerRun( 3 );
295 
296         final CompositeCache<String, String> cache = new CompositeCache<>(cacheAttr, new ElementAttributes());
297         final MockMemoryCache<String, String> memory = (MockMemoryCache<String, String>)cache.getMemoryCache();
298 
299         final ElementEventHandlerMockImpl handler = new ElementEventHandlerMockImpl();
300 
301         for ( int i = 0; i < 10; i++ )
302         {
303             final String key = "key" + i;
304             final String value = "value";
305 
306             final ICacheElement<String, String> element = new CacheElement<>( "testRegion", key, value );
307 
308             final ElementAttributes elementAttr = new ElementAttributes();
309             elementAttr.addElementEventHandler( handler );
310             elementAttr.setIsEternal( false );
311             element.setElementAttributes( elementAttr );
312             element.getElementAttributes().setMaxLife(1);
313             memory.update( element );
314 
315             final ICacheElement<String, String> returnedElement1 = memory.get( key );
316             assertNotNull( "We should have received an element", returnedElement1 );
317 
318             // set this to 2 seconds ago.
319             ElementAttributesUtils.setLastAccessTime( elementAttr,  System.currentTimeMillis() - 2000 );
320         }
321 
322         // DO WORK
323         final ShrinkerThread<String, String> shrinker = new ShrinkerThread<>( cache );
324         shrinker.run();
325 
326         // VERIFY
327         Thread.sleep( 500 );
328         assertEquals( "Waterfall called the wrong number of times.", 3, memory.waterfallCallCount );
329         // the shrinker delegates the composite cache on the memory cache to put the
330         // event on the queue.  This make it hard to test.  TODO we need to change this to make it easier to verify.
331         //assertEquals( "Event handler ExceededIdleTimeBackground called the wrong number of times.", 3, handler.getExceededIdleTimeBackgroundCount() );
332         assertEquals( "Wrong number of elements remain.", 7, memory.getSize() );
333     }
334 }