View Javadoc

1   package net.sf.ehcache.terracotta;
2   
3   import net.sf.ehcache.Cache;
4   import net.sf.ehcache.CacheManager;
5   import net.sf.ehcache.config.CacheConfiguration;
6   import net.sf.ehcache.store.TerracottaStore;
7   import org.junit.Test;
8   import org.mockito.invocation.InvocationOnMock;
9   import org.mockito.stubbing.Answer;
10  
11  import java.io.File;
12  import java.io.IOException;
13  import java.lang.reflect.Field;
14  import java.text.ParseException;
15  import java.util.HashSet;
16  import java.util.Iterator;
17  import java.util.Set;
18  import java.util.concurrent.BrokenBarrierException;
19  import java.util.concurrent.CyclicBarrier;
20  import java.util.concurrent.atomic.AtomicLong;
21  
22  import static org.hamcrest.CoreMatchers.is;
23  import static org.junit.Assert.assertThat;
24  import static org.mockito.Mockito.mock;
25  import static org.mockito.Mockito.when;
26  
27  /***
28   * @author Alex Snaps
29   */
30  public class KeySnapshotterTest {
31  
32      private static final String KEY = "Key";
33      private static final long MAX_KEY = 10000;
34      private static final String DUMPS_DIRECTORY = System.getProperty("java.io.tmpdir") + File.separator + "dumps";
35  
36      final CacheManager cacheManager = new CacheManager();
37  
38      private static void deleteFolder(File root) {
39          if (root.isDirectory()) {
40              File[] files = root.listFiles();
41              for (File file : files) {
42                  if (file.isDirectory()) {
43                      deleteFolder(file);
44                  } else {
45                      file.delete();
46                  }
47              }
48          }
49  
50          if (root.exists()) {
51              root.delete();
52          }
53      }
54  
55      @Test(expected = IllegalArgumentException.class)
56      public void testThrowsIllegalArgumentExceptionOnZeroInterval() {
57          new KeySnapshotter(createFakeTcClusteredCache(mock(TerracottaStore.class)), 0, false, null);
58      }
59  
60      @Test(expected = IllegalArgumentException.class)
61      public void testThrowsIllegalArgumentExceptionOnNegativeInterval() {
62          new KeySnapshotter(createFakeTcClusteredCache(mock(TerracottaStore.class)), -100, false, null);
63      }
64  
65      @Test(expected = NullPointerException.class)
66      public void testThrowsNullPointerExceptionOnNegativeInterval() {
67          new KeySnapshotter(createFakeTcClusteredCache(mock(TerracottaStore.class)), 10, false, null);
68      }
69  
70      @Test
71      public void testOperatesOnDedicatedThread() throws ParseException, IOException {
72          final RotatingSnapshotFile rotatingSnapshotFile = mock(RotatingSnapshotFile.class);
73          final TerracottaStore mock = mock(TerracottaStore.class);
74          KeySnapshotter snapshotter = new KeySnapshotter(createFakeTcClusteredCache(mock), 10, true, rotatingSnapshotFile);
75  
76          boolean found = false;
77          for (Thread thread : getAllThreads()) {
78              if (thread != null) {
79                  if(thread.getName().equals("KeySnapshotter for cache test")) {
80                      found = true;
81                  }
82              }
83          }
84          assertThat(found, is(true));
85          snapshotter.dispose(true);
86      }
87  
88      @Test
89      public void testDisposesProperlyImmediately() throws BrokenBarrierException, InterruptedException, IOException {
90          deleteFolder(new File(DUMPS_DIRECTORY));
91          final RotatingSnapshotFile rotatingSnapshotFile = new RotatingSnapshotFile(DUMPS_DIRECTORY, "testingInterruptImmediate");
92          final TerracottaStore mockedTcStore = mock(TerracottaStore.class);
93          KeySnapshotter snapshotter = new KeySnapshotter(createFakeTcClusteredCache(mockedTcStore), 1, true, rotatingSnapshotFile);
94          final CyclicBarrier barrier = new CyclicBarrier(2);
95          final Set mockedSet = mock(Set.class);
96          final AtomicLong counter = new AtomicLong(0);
97          when(mockedSet.iterator()).thenAnswer(new Answer<Iterator>() {
98              public Iterator answer(final InvocationOnMock invocationOnMock) throws Throwable {
99                  return new Iterator<Object>() {
100 
101                     public boolean hasNext() {
102                         return counter.get() < 100000;
103                     }
104 
105                     public Object next() {
106                         return "Key" + counter.getAndIncrement();
107                     }
108 
109                     public void remove() {
110                         //
111                     }
112                 };
113             }
114         });
115         when(mockedTcStore.getLocalKeys()).thenAnswer(new Answer<Set>() {
116             public Set answer(final InvocationOnMock invocationOnMock) throws Throwable {
117                 barrier.await();
118                 return mockedSet;
119             }
120         });
121         barrier.await();
122         snapshotter.dispose(true);
123         assertThat("We managed to get to a " + counter.get() + " keys written out", counter.get() < 1000, is(true));
124         final int elementsRead = rotatingSnapshotFile.readAll().size();
125         assertThat("Should be only a couple: " + elementsRead, elementsRead < 1000, is(true));
126     }
127 
128     @Test
129     public void testDisposesProperlyButFinishes() throws BrokenBarrierException, InterruptedException, IOException {
130         deleteFolder(new File(DUMPS_DIRECTORY));
131         final RotatingSnapshotFile rotatingSnapshotFile = new RotatingSnapshotFile(DUMPS_DIRECTORY, "testingInterruptFinishes");
132         final TerracottaStore mockedTcStore = mock(TerracottaStore.class);
133         final CyclicBarrier barrier = new CyclicBarrier(2);
134         final Set mockedSet = mock(Set.class);
135         final AtomicLong counter = new AtomicLong(0);
136         when(mockedSet.iterator()).thenAnswer(new Answer<Iterator>() {
137             public Iterator answer(final InvocationOnMock invocationOnMock) throws Throwable {
138                 return new Iterator<Object>() {
139 
140                     public boolean hasNext() {
141                         return counter.get() < MAX_KEY;
142                     }
143 
144                     public Object next() {
145                         return KEY + counter.getAndIncrement();
146                     }
147 
148                     public void remove() {
149                         //
150                     }
151                 };
152             }
153         });
154         when(mockedTcStore.getLocalKeys()).thenAnswer(new Answer<Set>() {
155             public Set answer(final InvocationOnMock invocationOnMock) throws Throwable {
156                 barrier.await();
157                 return mockedSet;
158             }
159         });
160         KeySnapshotter snapshotter = new KeySnapshotter(createFakeTcClusteredCache(mockedTcStore), 1, true, rotatingSnapshotFile);
161         barrier.await();
162         snapshotter.dispose(false);
163         assertThat(counter.get(), is(MAX_KEY));
164         final Set<Object> objects = new HashSet<Object>(rotatingSnapshotFile.readAll());
165         assertThat(objects.size(), is((int) MAX_KEY));
166         for(int i = 0; i < MAX_KEY; i++) {
167             objects.remove(KEY + i);
168         }
169         assertThat(objects.isEmpty(), is(true));
170     }
171 
172     private Thread[] getAllThreads() {
173         ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
174         ThreadGroup parentGroup;
175         while ((parentGroup = rootGroup.getParent()) != null) {
176             rootGroup = parentGroup;
177         }
178 
179         Thread[] threads = new Thread[rootGroup.activeCount()];
180         rootGroup.enumerate(threads, true);
181         return threads;
182     }
183 
184     private Cache createFakeTcClusteredCache(TerracottaStore store) {
185         final Cache cache = new Cache(new CacheConfiguration("test", 10));
186         cacheManager.addCache(cache);
187         try {
188             final Field compoundStore = cache.getClass().getDeclaredField("compoundStore");
189             compoundStore.setAccessible(true);
190             compoundStore.set(cache, store);
191         } catch (Exception e) {
192             throw new RuntimeException(e);
193         }
194         return cache;
195     }
196 }