View Javadoc

1   /*
2    * $Id: InMemoryStoreTestCase.java 20321 2010-11-24 15:21:24Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.util.store;
12  
13  import org.mule.api.lifecycle.InitialisationException;
14  import org.mule.tck.AbstractMuleTestCase;
15  
16  import edu.emory.mathcs.backport.java.util.concurrent.CountDownLatch;
17  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
18  
19  public class InMemoryStoreTestCase extends AbstractMuleTestCase
20  {
21      private InMemoryObjectStore<String> store = null;
22      
23      @Override
24      protected void doTearDown() throws Exception
25      {
26          store.dispose();
27          super.doTearDown();
28      }
29  
30      public void testSimpleTimedExpiry() throws Exception
31      {
32          int entryTTL = 3000;
33          createTimedObjectStore(entryTTL);
34  
35          // store entries in quick succession
36          storeObjects("1", "2", "3");
37  
38          // they should still be alive at this point
39          assertObjectsInStore("1", "2", "3");
40  
41          // wait until the entry TTL has been exceeded
42          Thread.sleep(entryTTL + 1000);
43  
44          // make sure all values are gone
45          assertObjectsExpired("1", "2", "3");
46      }
47      
48      public void testComplexTimedExpiry() throws Exception
49      {
50          int entryTTL = 3000;
51          createTimedObjectStore(entryTTL);
52          
53          // store an entry ...
54          storeObjects("1");
55          
56          // ... wait half of the expiry time ...
57          Thread.sleep(entryTTL / 2);
58          
59          // ... and store another object ...
60          storeObjects("2");
61          
62          // ... now wait until the first one is expired
63          Thread.sleep((entryTTL / 2) + 500);
64          
65          assertObjectsExpired("1");
66          assertObjectsInStore("2");
67      }
68  
69      public void testStoreAndRetrieve() throws Exception
70      {
71          String key = "key";
72          String value = "hello";
73          
74          createBoundedObjectStore(1);
75          
76          store.store(key, value);
77          assertObjectsInStore(key);
78          
79          String retrieved = store.retrieve(key);
80          assertEquals(value, retrieved);
81          
82          store.remove(key);        
83          assertObjectsExpired(key);
84      }
85      
86      public void testExpiringUnboundedStore() throws Exception
87      {
88          createUnboundedObjectStore();
89          
90          // put some items into the store
91          storeObjects("1", "2", "3");
92          
93          // expire ... this should keep all objects in the store
94          store.expire();
95          
96          assertObjectsInStore("1", "2", "3");
97      }
98          
99      public void testMaxSize() throws Exception
100     {
101         int maxEntries = 3;
102         createBoundedObjectStore(maxEntries);
103 
104         storeObjects("1", "2", "3");
105         assertObjectsInStore("1", "2", "3");
106 
107         // exceed threshold
108         store.store("4", "4");
109 
110         // the oldest entry should still be there, not yet expired
111         assertTrue(store.contains("1"));
112 
113         // expire manually
114         store.expire();
115         assertObjectsExpired("1");
116         assertObjectsInStore("2", "3", "4");
117 
118         // exceed some more
119         storeObjects("5");
120         store.expire();
121         assertObjectsExpired("2");
122         assertObjectsInStore("3", "4", "5");
123 
124         // exceed multiple times
125         storeObjects("6", "7", "8", "9");
126         store.expire();
127         assertObjectsInStore("7", "8", "9");
128         assertObjectsExpired("3", "4", "5", "6");
129     }
130 
131     private void storeObjects(String... objects) throws Exception
132     {
133         for (String entry : objects)
134         {
135             store.store(entry, entry);
136         }
137     }
138     
139     private void assertObjectsInStore(String... identifiers) throws Exception
140     {
141         for (String id : identifiers)
142         {
143             String message = "id " + id + " not in store " + store;
144             assertTrue(message, store.contains(id));
145         }
146     }
147     
148     private void assertObjectsExpired(String... identifiers) throws Exception
149     {
150         for (String id : identifiers)
151         {
152             assertFalse(store.contains(id));
153         }
154     }
155 
156     private void createTimedObjectStore(int timeToLive) throws InitialisationException
157     {
158         int expireInterval = 1000;
159         assertTrue("objects' time to live must be greater than the expire interval", 
160             timeToLive > expireInterval);
161         
162         store = new InMemoryObjectStore<String>();
163         store.setName("timed");
164         store.setMaxEntries(3);
165         store.setEntryTTL(timeToLive);
166         store.setExpirationInterval(expireInterval);
167         store.initialise();
168     }
169 
170     private void createBoundedObjectStore(int numberOfEntries) throws InitialisationException
171     {
172         createNonexpiringObjectStore();
173         store.setName("bounded");
174         store.setMaxEntries(numberOfEntries);
175         store.initialise();
176     }
177     
178     private void createUnboundedObjectStore() throws InitialisationException
179     {
180         createNonexpiringObjectStore();
181         store.setMaxEntries(-1);
182         store.initialise();
183     }
184 
185     private void createNonexpiringObjectStore()
186     {
187         store = new NonExpiringInMemoryObjectStore();
188     }
189     
190     /**
191      * Special subclass that coordinates with the expire thread. Upon calling <code>initialize</code>
192      * the scheduler in {@link AbstractMonitoredObjectStore} runs once. The tests in this test case
193      * rely on the fact that no expiry happens during their execution. This implementation waits for
194      * the first run of the expire method in initialize and only then continues with the execution 
195      * of the current thread.
196      */
197     private static class NonExpiringInMemoryObjectStore extends InMemoryObjectStore<String>
198     {
199         private CountDownLatch expireLatch;
200 
201         public NonExpiringInMemoryObjectStore()
202         {
203             super();
204             // entryTTL=-1 means we will have to expire manually
205             setEntryTTL(-1);
206             // run the expire thread in very, very large intervals (irreleavent to this test)
207             setExpirationInterval(Integer.MAX_VALUE);
208             
209             expireLatch = new CountDownLatch(1);
210         }
211         
212         @Override
213         public void initialise() throws InitialisationException
214         {
215             super.initialise();
216             
217             // now wait for the first expire to happen
218             try
219             {
220                 expireLatch.await(30, TimeUnit.SECONDS);
221             }
222             catch (InterruptedException ie)
223             {
224                 throw new RuntimeException("Interrupted while waiting for the first expire", ie);
225             }
226         }
227 
228         @Override
229         public void expire()
230         {
231             super.expire();
232             // expire successful ... signal initialize that it can continue
233             expireLatch.countDown();
234         }
235     }
236 }