View Javadoc
1   package com.atlassian.cache;
2   
3   import java.util.concurrent.CountDownLatch;
4   import java.util.concurrent.TimeUnit;
5   import java.util.concurrent.atomic.AtomicInteger;
6   
7   import javax.annotation.Nonnull;
8   import javax.annotation.Nullable;
9   
10  import com.atlassian.utt.concurrency.Barrier;
11  import com.atlassian.utt.concurrency.TestThread;
12  
13  import com.google.common.base.Throwables;
14  import org.junit.Test;
15  
16  import static com.atlassian.utt.concurrency.TestThread.runTest;
17  import static java.util.concurrent.TimeUnit.SECONDS;
18  import static org.hamcrest.Matchers.is;
19  import static org.hamcrest.Matchers.isOneOf;
20  import static org.hamcrest.core.IsEqual.equalTo;
21  import static org.hamcrest.core.IsInstanceOf.instanceOf;
22  import static org.junit.Assert.assertNotSame;
23  import static org.junit.Assert.assertThat;
24  import static org.junit.Assert.fail;
25  
26  /**
27   * Test the Lazy Reference
28   *
29   * @since 2.0
30   */
31  public abstract class AbstractLazyReferenceTest extends AbstractCacheTest
32  {
33      public AbstractLazyReferenceTest()
34      {
35      }
36  
37      public AbstractLazyReferenceTest(CacheType cacheType)
38      {
39          super(cacheType);
40      }
41  
42      @Test
43      public void testGetName() throws Exception
44      {
45          CachedReference<Long> ref = makeReference();
46  
47          assertThat(((ManagedCache) ref).getName(), equalTo("mycache"));
48      }
49  
50      @Test
51      public void testFactoryGeneratedName() throws Exception
52      {
53          CachedReference<Long> ref = factory.getCachedReference(
54                  Object.class, "mycache", nullSupplier(), settingsBuilder().build());
55  
56          assertThat(((ManagedCache)ref).getName(), equalTo("java.lang.Object.mycache"));
57      }
58  
59      @Test
60      public void testGet() throws Exception
61      {
62          CachedReference<Long> ref = makeReference();
63  
64          assertThat(ref.get(), equalTo(100L));
65          assertThat(ref.get(), equalTo(100L));
66          assertThat(ref.get(), equalTo(100L));
67      }
68  
69  
70      @Test
71      public void testReset() throws Exception
72      {
73          CachedReference<Long> ref = makeReference();
74  
75          assertThat(ref.get(), equalTo(100L));
76  
77          ref.reset();
78  
79          // This should have been recalculated
80          assertThat(ref.get(), equalTo(101L));
81      }
82  
83      @Test
84      public void testRemoveTwice() throws Exception
85      {
86          CachedReference<Long> ref = makeReference();
87  
88          assertThat(ref.get(), equalTo(100L));
89          assertThat(ref.get(), equalTo(100L));
90  
91          ref.reset();
92          ref.reset();
93  
94          assertThat(ref.get(), equalTo(101L));
95          assertThat(ref.get(), equalTo(101L));
96  
97          ref.reset();
98  
99          // This should have been recalculated
100         assertThat(ref.get(), equalTo(102L));
101         assertThat(ref.get(), equalTo(102L));
102     }
103 
104     @Test
105     public void testClear() throws Exception
106     {
107         CachedReference<Long> ref = makeReference();
108 
109         assertThat(ref.get(), equalTo(100L));
110 
111         ((ManagedCache) ref).clear();
112 
113         // This should have been recalculated
114         assertThat(ref.get(), equalTo(101L));
115     }
116 
117     @Test(expected = CacheException.class)
118     public void testNullHandling() throws Exception
119     {
120         makeNullReference().get();
121     }
122 
123     @Test
124     public void testExceptionHandling() throws Exception
125     {
126         final CachedReference<Long> ref = makeExceptionalReference();
127         try
128         {
129             ref.get();
130             fail("Should throw CacheException when the loader throws an IllegalArgumentException");
131         }
132         catch (CacheException ex)
133         {
134             assertThat(
135                     "This exception should wrap the original Exception",
136                     Throwables.getRootCause(ex),
137                     instanceOf(IllegalArgumentException.class));
138         }
139         assertThat("The exception should not be sticky", ref.get(), equalTo(42L));
140     }
141 
142     @Test
143     public void testNewInstanceForEveryGet()
144     {
145         CachedReference<Long> ref1 = makeReference();
146         CachedReference<Long> ref2 = makeReference();
147 
148         assertNotSame("The cache manager should not return the same cache twice", ref1, ref2);
149 
150     }
151 
152     @Test
153     public void testWhenResettingAReferenceWhereTheSupplierReturnsNullNoExceptionIsThrown()
154     {
155         try
156         {
157             CachedReference<Long> reference = makeExceptionalReference();
158             reference.reset();
159         }
160         catch (Exception e)
161         {
162             fail("Exception should not be thrown when a loader return null during a remove: " + e.getMessage());
163         }
164     }
165 
166     @Test
167     public void testResetConcurrentWithLoadLocal()
168     {
169         resetConcurrentWithLoad(factory, null);
170     }
171 
172     protected void resetConcurrentWithLoad(final CacheFactory factory1, @Nullable final CacheFactory factory2)
173     {
174         final Barrier doUpdate = new Barrier();
175         final CountDownLatch resetDone = new CountDownLatch(1);
176         final Barrier afterGet = new Barrier();
177         final AtomicInteger dbValue = new AtomicInteger(1);
178 
179         final Supplier<Integer> supplier = new Supplier<Integer>()
180         {
181             @Nonnull
182             @Override
183             public Integer get()
184             {
185                 final int value = dbValue.get();
186                 doUpdate.trySignal();
187                 try
188                 {
189                     // Whether or not reset gets blocked by the load operation is implementation-dependent.
190                     resetDone.await(100L, TimeUnit.MILLISECONDS);
191                 }
192                 catch (InterruptedException ie)
193                 {
194                     throw new AssertionError(ie);
195                 }
196                 afterGet.trySignal();
197                 return value;
198             }
199         };
200 
201         final CachedReference<Integer> ref1 = factory1.getCachedReference("resetConcurrentWithLoad", supplier,
202                 settingsBuilder().build());
203         final CachedReference<Integer> ref2 = (factory2 != null)
204                 ? factory2.getCachedReference("resetConcurrentWithLoad", supplier, settingsBuilder().build())
205                 : ref1;
206 
207         // Make sure everything is live.  Hazelcast takes >5s to initialize sometimes.
208         ref1.reset();
209         ref2.reset();
210 
211         final TestThread blockingGet = new TestThread("blockingGet")
212         {
213             @Override
214             protected void go() throws Exception
215             {
216                 Integer value = ref1.get();
217                 assertThat("before reset", value, isOneOf(1, 2));  // Either the stale or updated value is acceptable
218 
219                 assertThat("resetDone", resetDone.await(5L, SECONDS), is(true));
220 
221                 value = ref1.get();
222                 assertThat("after reset", value, is(2));
223             }
224         };
225 
226         final TestThread modifyResetGet = new TestThread("modifyResetGet")
227         {
228             @Override
229             protected void go() throws Exception
230             {
231                 doUpdate.await();
232 
233                 dbValue.set(2);
234                 ref2.reset();
235                 resetDone.countDown();
236 
237                 Integer value = ref2.get();
238                 assertThat("after reset", value, is(2));
239             }
240         };
241 
242         runTest(blockingGet, modifyResetGet);
243     }
244 
245     protected CachedReference<Long> makeReference()
246     {
247         // Build a Cache using the builder
248         Supplier<Long> supplier = new Supplier<Long>()
249         {
250             private long value = 100;
251             @Override
252             public Long get()
253             {
254                 return value++;
255             }
256         };
257         return factory.getCachedReference("mycache", supplier, settingsBuilder().build());
258     }
259 
260     private CachedReference<Long> makeNullReference()
261     {
262         return factory.getCachedReference("mycache", nullSupplier(), settingsBuilder().build());
263     }
264 
265     private CachedReference<Long> makeExceptionalReference()
266     {
267         Supplier<Long> supplier = new Supplier<Long>()
268         {
269             private boolean called = false;
270 
271             @Override
272             public Long get()
273             {
274                 if (called)
275                 {
276                     return 42L;
277                 }
278                 called = true;
279                 throw new IllegalArgumentException();
280             }
281         };
282         return factory.getCachedReference("mycache", supplier, settingsBuilder().build());
283     }
284 
285     private static Supplier<Long> nullSupplier()
286     {
287         return new Supplier<Long>()
288         {
289             @Override
290             public Long get()
291             {
292                 return null;
293             }
294         };
295     }
296 }