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
28
29
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
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
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
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
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
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));
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
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 }