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