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