public ErrorTranslatorImpl(String... errorFileNames) { log.info("Start initializing {}", getClass().getSimpleName()); messageSources = asList(errorFileNames); standardLocale = Locale.getDefault(); standardMessages = retrieveByLocale(standardLocale); messagesByLocale = new ReapedMap<>(ONE_HOUR, true); log.info("Finished initializing {}", getClass().getSimpleName()); }
private synchronized Map<String, String> getMessages(Locale locale) { Map<String, String> messages = null; if (standardLocale.equals(locale)) { messages = standardMessages; } else { if ((messages = messagesByLocale.get(locale)) == null) { messages = retrieveByLocale(locale); messagesByLocale.put(locale, messages); messagesByLocale.reapable(locale); } } return messages; }
@Override public synchronized V put(K k, V v) { reap(); return super.put(k, v); }
@Test public void testReapingWithoutGC() throws Exception { map = new ReapedMap<>(1000); populate(1, 2, 3); assertSizes(3, 0); assertExpected(1, 2, 3); map.reapable("one"); map.reapable("three"); assertSizes(1, 2); map.reapable("one"); assertSizes(1, 2); assertExpected(1, 2, 3); assertEquals(Integer.valueOf(3), map.remove("three")); assertSizes(1, 1); assertExpected(1, 2); assertNull(map.get("three")); Thread.sleep(1100); assertExpected(1); assertNull(map.get("one")); assertExpected(2); assertSizes(1, 0); populate(4, 5); map.clear(); }
private void populate(Integer ... values) { for (Integer v: values) { map.put(MAPPER.getKey(v), v); } }
private void assertSizes(int i, int j) { assertEquals(i, map.size(), "unexpected primary map size"); assertEquals(j, map.reapableSize(), "unexpected secondary map size"); }
private void assertExpected(Integer ... values) { for (Integer v: values) { assertEquals(v, map.get(MAPPER.getKey(v))); } }
@Test public void testReapingOnGetWithAccessBasedAging() throws Exception { setUpAccessBaseAgingExpectations(); populate(1, 2, 3); assertSizes(3, 0); map.reapable("one"); map.reapable("three"); assertSizes(1, 2); for (int i = 0; i < 6; i++) { Thread.sleep(250); assertExpected(i == 0 ? 1 : 3); } assertSizes(1, 1); assertNull(map.get("one")); assertExpected(3); Thread.sleep(500); assertExpected(3); assertSizes(1, 1); Thread.sleep(1200); assertExpected(2); assertSizes(1, 0); assertNull(map.get("three")); }
@SuppressWarnings("unchecked") private void setUpAccessBaseAgingExpectations() { ReferenceQueue<Integer> queue = mock(ReferenceQueue.class); map = new ReapedMap<>(1000, true, queue); }
/** * Mark a key as being reapable, caching corresponding soft reference to corresponding value in the secondary map. */ public synchronized void reapable(K k) { V v = super.remove(k); if (v != null) { reapableMap.put(k, new IdAwareReference<>(k, v, queue)); } reap(); }
@SuppressWarnings({ "unchecked", "rawtypes" }) private void setUpGCExpectations(final int gcAfter) { ReferenceQueue<Integer> queue = mock(ReferenceQueue.class); map = new ReapedMap<>(10000, false, queue); final IdAwareReference ref = mock(IdAwareReference.class); when(ref.getKey()).thenReturn("three").thenReturn(null); // the gcAfter queue poll simulates a GC event and triggers deletion // on the reapable map when(queue.poll()).thenAnswer(new Answer<Reference<Integer>>() { private int times = 0; @Override public Reference<Integer> answer(InvocationOnMock invocation) { return times++ == gcAfter ? ref : null; } }); }
@Override public synchronized V get(Object key) { V ret = super.get(key); if (ret == null) { IdAwareReference<K, V> ref = accessBasedAging ? reapableMap.remove(key) : reapableMap.get(key); if (ref != null) { if (ref.isEnqueued()) { ref.clear(); reapableMap.remove(key); } else { ret = ref.get(); if (ret == null) { reapableMap.remove(key); } else if (accessBasedAging) { // re-insert on timestamp reset so // as to maintain insertion order reapableMap.put(ref.key, ref.reset()); } } } } reap(); return ret; }
@Override public synchronized V remove(Object key) { V ret = super.remove(key); if (ret == null) { IdAwareReference<K, V> ref = reapableMap.remove(key); if (ref != null) { if (ref.isEnqueued()) { ref.clear(); } else { ret = ref.get(); } } } reap(); return ret; }