View Javadoc
1   package org.apache.commons.jcs.engine;
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 java.io.IOException;
23  
24  import junit.extensions.ActiveTestSuite;
25  import junit.framework.Test;
26  import junit.framework.TestCase;
27  
28  import org.apache.commons.jcs.engine.behavior.ICacheElement;
29  import org.apache.commons.jcs.engine.behavior.ICacheListener;
30  
31  /**
32   * This test case is designed to makes sure there are no deadlocks in the event queue. The time to
33   * live should be set to a very short interval to make a deadlock more likely.
34   * <p>
35   * @author Aaron Smuts
36   */
37  public class EventQueueConcurrentLoadTest
38      extends TestCase
39  {
40      /** The queue implementation */
41      private static CacheEventQueue<String, String> queue = null;
42  
43      /** The mock listener */
44      private static CacheListenerImpl<String, String> listen = null;
45  
46      /** max failure setting */
47      private final int maxFailure = 3;
48  
49      /** time to wait before retrying on failure. */
50      private final int waitBeforeRetry = 100;
51  
52      /** very small idle time */
53      private final int idleTime = 2;
54  
55      /**
56       * Constructor for the TestDiskCache object.
57       * @param testName
58       */
59      public EventQueueConcurrentLoadTest( String testName )
60      {
61          super( testName );
62      }
63  
64      /**
65       * Main method passes this test to the text test runner.
66       * @param args
67       */
68      public static void main( String args[] )
69      {
70          String[] testCaseName = { EventQueueConcurrentLoadTest.class.getName() };
71          junit.textui.TestRunner.main( testCaseName );
72      }
73  
74      /**
75       * A unit test suite for JUnit
76       * @return The test suite
77       */
78      public static Test suite()
79      {
80          ActiveTestSuite suite = new ActiveTestSuite();
81  
82          suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest1" )
83          {
84              @Override
85              public void runTest()
86                  throws Exception
87              {
88                  this.runPutTest( 200, 200 );
89              }
90          } );
91  
92          suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest2" )
93          {
94              @Override
95              public void runTest()
96                  throws Exception
97              {
98                  this.runPutTest( 1200, 1400 );
99              }
100         } );
101 
102         suite.addTest( new EventQueueConcurrentLoadTest( "testRunRemoveTest1" )
103         {
104             @Override
105             public void runTest()
106                 throws Exception
107             {
108                 this.runRemoveTest( 2200 );
109             }
110         } );
111 
112         suite.addTest( new EventQueueConcurrentLoadTest( "testStopProcessing1" )
113         {
114             @Override
115             public void runTest()
116                 throws Exception
117             {
118                 this.runStopProcessingTest();
119             }
120         } );
121 
122         suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutTest4" )
123         {
124             @Override
125             public void runTest()
126                 throws Exception
127             {
128                 this.runPutTest( 5200, 6600 );
129             }
130         } );
131 
132         suite.addTest( new EventQueueConcurrentLoadTest( "testRunRemoveTest2" )
133         {
134             @Override
135             public void runTest()
136                 throws Exception
137             {
138                 this.runRemoveTest( 5200 );
139             }
140         } );
141 
142         suite.addTest( new EventQueueConcurrentLoadTest( "testStopProcessing2" )
143         {
144             @Override
145             public void runTest()
146                 throws Exception
147             {
148                 this.runStopProcessingTest();
149             }
150         } );
151 
152         suite.addTest( new EventQueueConcurrentLoadTest( "testRunPutDelayTest" )
153         {
154             @Override
155             public void runTest()
156                 throws Exception
157             {
158                 this.runPutDelayTest( 100, 6700 );
159             }
160         } );
161 
162         return suite;
163     }
164 
165     /**
166      * Test setup. Create the static queue to be used by all tests
167      */
168     @Override
169     public void setUp()
170     {
171         listen = new CacheListenerImpl<String, String>();
172         queue = new CacheEventQueue<String, String>( listen, 1L, "testCache1", maxFailure, waitBeforeRetry );
173 
174         queue.setWaitToDieMillis( idleTime );
175     }
176 
177     /**
178      * Adds put events to the queue.
179      * @param end
180      * @param expectedPutCount
181      * @throws Exception
182      */
183     public void runPutTest( int end, int expectedPutCount )
184         throws Exception
185     {
186         for ( int i = 0; i <= end; i++ )
187         {
188             CacheElement<String, String> elem = new CacheElement<String, String>( "testCache1", i + ":key", i + "data" );
189             queue.addPutEvent( elem );
190         }
191 
192         while ( !queue.isEmpty() )
193         {
194             synchronized ( this )
195             {
196                 System.out.println( "queue is still busy, waiting 250 millis" );
197                 this.wait( 250 );
198             }
199         }
200         System.out.println( "queue is empty, comparing putCount" );
201 
202         // this becomes less accurate with each test. It should never fail. If
203         // it does things are very off.
204         assertTrue( "The put count [" + listen.putCount + "] is below the expected minimum threshold ["
205             + expectedPutCount + "]", listen.putCount >= ( expectedPutCount - 1 ) );
206 
207     }
208 
209     /**
210      * Add remove events to the event queue.
211      * @param end
212      * @throws Exception
213      */
214     public void runRemoveTest( int end )
215         throws Exception
216     {
217         for ( int i = 0; i <= end; i++ )
218         {
219             queue.addRemoveEvent( i + ":key" );
220         }
221 
222     }
223 
224     /**
225      * Add remove events to the event queue.
226      * @throws Exception
227      */
228     public void runStopProcessingTest()
229         throws Exception
230     {
231         queue.stopProcessing();
232     }
233 
234     /**
235      * Test putting and a delay. Waits until queue is empty to start.
236      * @param end
237      * @param expectedPutCount
238      * @throws Exception
239      */
240     public void runPutDelayTest( int end, int expectedPutCount )
241         throws Exception
242     {
243         while ( !queue.isEmpty() )
244         {
245             synchronized ( this )
246             {
247                 System.out.println( "queue is busy, waiting 250 millis to begin" );
248                 this.wait( 250 );
249             }
250         }
251         System.out.println( "queue is empty, begin" );
252 
253         // get it going
254         CacheElement<String, String> elem = new CacheElement<String, String>( "testCache1", "a:key", "adata" );
255         queue.addPutEvent( elem );
256 
257         for ( int i = 0; i <= end; i++ )
258         {
259             synchronized ( this )
260             {
261                 if ( i % 2 == 0 )
262                 {
263                     this.wait( idleTime );
264                 }
265                 else
266                 {
267                     this.wait( idleTime / 2 );
268                 }
269             }
270             CacheElement<String, String> elem2 = new CacheElement<String, String>( "testCache1", i + ":key", i + "data" );
271             queue.addPutEvent( elem2 );
272         }
273 
274         while ( !queue.isEmpty() )
275         {
276             synchronized ( this )
277             {
278                 System.out.println( "queue is still busy, waiting 250 millis" );
279                 this.wait( 250 );
280             }
281         }
282         System.out.println( "queue is empty, comparing putCount" );
283 
284         Thread.sleep( 1000 );
285 
286         // this becomes less accurate with each test. It should never fail. If
287         // it does things are very off.
288         assertTrue( "The put count [" + listen.putCount + "] is below the expected minimum threshold ["
289             + expectedPutCount + "]", listen.putCount >= ( expectedPutCount - 1 ) );
290 
291     }
292 
293     /**
294      * This is a dummy cache listener to use when testing the event queue.
295      */
296     protected static class CacheListenerImpl<K, V>
297         implements ICacheListener<K, V>
298     {
299         /**
300          * <code>putCount</code>
301          */
302         protected int putCount = 0;
303 
304         /**
305          * <code>removeCount</code>
306          */
307         protected int removeCount = 0;
308 
309         /**
310          * @param item
311          * @throws IOException
312          */
313         @Override
314         public void handlePut( ICacheElement<K, V> item )
315             throws IOException
316         {
317             synchronized ( this )
318             {
319                 putCount++;
320             }
321         }
322 
323         /**
324          * @param cacheName
325          * @param key
326          * @throws IOException
327          */
328         @Override
329         public void handleRemove( String cacheName, K key )
330             throws IOException
331         {
332             synchronized ( this )
333             {
334                 removeCount++;
335             }
336 
337         }
338 
339         /**
340          * @param cacheName
341          * @throws IOException
342          */
343         @Override
344         public void handleRemoveAll( String cacheName )
345             throws IOException
346         {
347             // TODO Auto-generated method stub
348 
349         }
350 
351         /**
352          * @param cacheName
353          * @throws IOException
354          */
355         @Override
356         public void handleDispose( String cacheName )
357             throws IOException
358         {
359             // TODO Auto-generated method stub
360 
361         }
362 
363         /**
364          * @param id
365          * @throws IOException
366          */
367         @Override
368         public void setListenerId( long id )
369             throws IOException
370         {
371             // TODO Auto-generated method stub
372 
373         }
374 
375         /**
376          * @return 0
377          * @throws IOException
378          */
379         @Override
380         public long getListenerId()
381             throws IOException
382         {
383             // TODO Auto-generated method stub
384             return 0;
385         }
386 
387     }
388 }