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