-
-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Created ConfigCache implementation, that works as a Singleton.
See issue #64
- Loading branch information
Luigi R. Viggiano
committed
Apr 8, 2014
1 parent
08d041a
commit 9b521d4
Showing
2 changed files
with
268 additions
and
0 deletions.
There are no files selected for viewing
137 changes: 137 additions & 0 deletions
137
owner/src/main/java/org/aeonbits/owner/ConfigCache.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
* Copyright (c) 2013-2014, Luigi R. Viggiano | ||
* All rights reserved. | ||
* | ||
* This software is distributable under the BSD license. | ||
* See the terms of the BSD license in the documentation provided with this software. | ||
*/ | ||
|
||
package org.aeonbits.owner; | ||
|
||
import java.util.Map; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
import java.util.concurrent.ConcurrentMap; | ||
|
||
/** | ||
* Utility class to have a cache singleton for Config instances. | ||
* | ||
* @author Luigi R. Viggiano | ||
* @since 1.0.6 | ||
*/ | ||
public final class ConfigCache { | ||
private static ConcurrentMap<Object, Config> CACHE = new ConcurrentHashMap<Object, Config>(); | ||
|
||
/** Don't let anyone instantiate this class */ | ||
private ConfigCache() {} | ||
|
||
/** | ||
* Gets from the cache or create, an instance of the given class using the given imports. | ||
* The factory used to create new instances is the static {@link ConfigFactory#INSTANCE}. | ||
* | ||
* @param clazz the interface extending from {@link Config} that you want to instantiate. | ||
* @param imports additional variables to be used to resolve the properties. | ||
* @param <T> type of the interface. | ||
* @return an object implementing the given interface, that can be taken from the cache, which maps methods | ||
* to property values. | ||
*/ | ||
public static <T extends Config> T getOrCreate(Class<? extends T> clazz, Map<?, ?>... imports) { | ||
return getOrCreate(ConfigFactory.INSTANCE, clazz, clazz, imports); | ||
} | ||
|
||
/** | ||
* Gets from the cache or create, an instance of the given class using the given imports. | ||
* | ||
* @param factory the factory to use to eventually create the instance. | ||
* @param clazz the interface extending from {@link Config} that you want to instantiate. | ||
* @param imports additional variables to be used to resolve the properties. | ||
* @param <T> type of the interface. | ||
* @return an object implementing the given interface, that can be taken from the cache, which maps methods | ||
* to property values. | ||
*/ | ||
public static <T extends Config> T getOrCreate(Factory factory, Class<? extends T> clazz, Map<?, ?>... imports) { | ||
return getOrCreate(factory, clazz, clazz, imports); | ||
} | ||
|
||
/** | ||
* Gets from the cache or create, an instance of the given class using the given imports. | ||
* | ||
* @param key the key object to be used to identify the instance in the cache. | ||
* @param clazz the interface extending from {@link Config} that you want to instantiate. | ||
* @param imports additional variables to be used to resolve the properties. | ||
* @param <T> type of the interface. | ||
* @return an object implementing the given interface, that can be taken from the cache, which maps methods | ||
* to property values. | ||
*/ | ||
public static <T extends Config> T getOrCreate(Object key, Class<? extends T> clazz, Map<?, ?>... imports) { | ||
return getOrCreate(ConfigFactory.INSTANCE, key, clazz, imports); | ||
} | ||
|
||
/** | ||
* Gets from the cache or create, an instance of the given class using the given imports. | ||
* | ||
* @param factory the factory to use to eventually create the instance. | ||
* @param key the key object to be used to identify the instance in the cache. | ||
* @param clazz the interface extending from {@link Config} that you want to instantiate. | ||
* @param imports additional variables to be used to resolve the properties. | ||
* @param <T> type of the interface. | ||
* @return an object implementing the given interface, that can be taken from the cache, which maps methods | ||
* to property values. | ||
*/ | ||
public static <T extends Config> T getOrCreate(Factory factory, Object key, | ||
Class<? extends T> clazz, Map<?, ?>[] imports) { | ||
T existing = get(key); | ||
if (existing != null) return existing; | ||
T created = factory.create(clazz, imports); | ||
T raced = add(key, created); | ||
return raced != null ? raced : created; | ||
} | ||
|
||
/** | ||
* Gets from the cache the {@link Config} instance identified by the given key. | ||
* | ||
* @param key the key object to be used to identify the instance in the cache. | ||
* @param <T> type of the interface. | ||
* @return the {@link Config} object from the cache if exists, or <tt>null</tt> if it doesn't. | ||
*/ | ||
public static <T extends Config> T get(Object key) { | ||
return (T) CACHE.get(key); | ||
} | ||
|
||
/** | ||
* Adds a {@link Config} object into the cache. | ||
* | ||
* @param key the key object to be used to identify the instance in the cache. | ||
* @param instance the instance of the {@link Config} object to be stored into the cache. | ||
* @param <T> type of the interface. | ||
* @return the previous value associated with the specified key, or | ||
* <tt>null</tt> if there was no mapping for the key. | ||
*/ | ||
public static <T extends Config> T add(Object key, T instance) { | ||
return (T) CACHE.putIfAbsent(key, instance); | ||
} | ||
|
||
/** | ||
* Removes all of the cached instances. | ||
* The cache will be empty after this call returns. | ||
*/ | ||
public static void clear() { | ||
CACHE.clear(); | ||
} | ||
|
||
/** | ||
* Removes the cached instance for the given key if it is present. | ||
* | ||
* <p>Returns previous instance associated to the given key in the cache, | ||
* or <tt>null</tt> if the cache contained no instance for the given key. | ||
* | ||
* <p>The cache will not contain the instance for the specified key once the | ||
* call returns. | ||
* | ||
* @param key key whose instance is to be removed from the cache. | ||
* @return the previous instance associated with <tt>key</tt>, or | ||
* <tt>null</tt> if there was no instance for <tt>key</tt>. | ||
*/ | ||
public static <T extends Config> T remove(Object key) { | ||
return (T) CACHE.remove(key); | ||
} | ||
} |
131 changes: 131 additions & 0 deletions
131
owner/src/test/java/org/aeonbits/owner/cache/CacheConfigTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
/* | ||
* Copyright (c) 2013-2014, Luigi R. Viggiano | ||
* All rights reserved. | ||
* | ||
* This software is distributable under the BSD license. | ||
* See the terms of the BSD license in the documentation provided with this software. | ||
*/ | ||
|
||
package org.aeonbits.owner.cache; | ||
|
||
import org.aeonbits.owner.Config; | ||
import org.aeonbits.owner.ConfigCache; | ||
import org.aeonbits.owner.ConfigFactory; | ||
import org.aeonbits.owner.Factory; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
import org.mockito.Matchers; | ||
|
||
import java.util.Map; | ||
|
||
import static junit.framework.Assert.assertNull; | ||
import static org.junit.Assert.*; | ||
import static org.mockito.Matchers.eq; | ||
import static org.mockito.Mockito.*; | ||
|
||
/** | ||
* @author Luigi R. Viggiano | ||
*/ | ||
public class CacheConfigTest { | ||
|
||
static interface MyConfig extends Config {} | ||
|
||
@Before | ||
public void before() { | ||
ConfigCache.clear(); | ||
} | ||
|
||
@Test | ||
public void testGetOrCreateFromCache() { | ||
MyConfig first = ConfigCache.getOrCreate(MyConfig.class); | ||
MyConfig second = ConfigCache.getOrCreate(MyConfig.class); | ||
assertSame(first, second); | ||
} | ||
|
||
@Test | ||
public void testGetOrCreateWithFactory() { | ||
Factory factory = ConfigFactory.newInstance(); | ||
Factory spy = spy(factory); | ||
MyConfig first = ConfigCache.getOrCreate(spy, MyConfig.class); | ||
MyConfig second = ConfigCache.getOrCreate(spy, MyConfig.class); | ||
MyConfig third = ConfigCache.getOrCreate(spy, MyConfig.class); | ||
assertSame(first, second); | ||
assertSame(second, third); | ||
verify(spy, times(1)).create(eq(MyConfig.class), Matchers.<Map<?, ?>[]>anyVararg()); | ||
} | ||
|
||
@Test | ||
public void testGetOrCreateUsingNameKey() { | ||
MyConfig first = ConfigCache.getOrCreate("MyConfig", MyConfig.class); | ||
MyConfig second = ConfigCache.getOrCreate("MyConfig", MyConfig.class); | ||
assertSame(first, second); | ||
} | ||
|
||
@Test | ||
public void testRemoveUsingClassAsKey() { | ||
MyConfig first = ConfigCache.getOrCreate(MyConfig.class); | ||
Config removed = ConfigCache.remove(MyConfig.class); | ||
assertNotNull(removed); | ||
|
||
MyConfig second = ConfigCache.getOrCreate(MyConfig.class); | ||
MyConfig third = ConfigCache.getOrCreate(MyConfig.class); | ||
|
||
assertNotSame(first, second); | ||
assertSame(second, third); | ||
} | ||
|
||
@Test | ||
public void testRemoveUsingNameKey() { | ||
MyConfig first = ConfigCache.getOrCreate("foo", MyConfig.class); | ||
|
||
Config removed = ConfigCache.remove("foo"); | ||
assertNotNull(removed); | ||
|
||
MyConfig second = ConfigCache.getOrCreate("foo", MyConfig.class); | ||
MyConfig third = ConfigCache.getOrCreate("foo", MyConfig.class); | ||
|
||
assertNotSame(first, second); | ||
assertSame(second, third); | ||
} | ||
|
||
@Test | ||
public void testRemove() { | ||
MyConfig first = ConfigCache.getOrCreate("MyConfig", MyConfig.class); | ||
Config removed = ConfigCache.remove("MyConfig"); | ||
MyConfig second = ConfigCache.getOrCreate("MyConfig", MyConfig.class); | ||
assertNotSame(first, second); | ||
assertSame(first, removed); | ||
} | ||
|
||
@Test | ||
public void testClear() { | ||
MyConfig first = ConfigCache.getOrCreate("MyConfig", MyConfig.class); | ||
ConfigCache.clear(); | ||
MyConfig second = ConfigCache.getOrCreate("MyConfig", MyConfig.class); | ||
assertNotSame(first, second); | ||
} | ||
|
||
@Test | ||
public void testGetWhenInstanceDoesNotExists() { | ||
MyConfig instance = ConfigCache.get(MyConfig.class); | ||
assertNull(instance); | ||
} | ||
|
||
@Test | ||
public void testGetWhenInstanceExists() { | ||
MyConfig created = ConfigCache.getOrCreate(MyConfig.class); | ||
MyConfig got = ConfigCache.get(MyConfig.class); | ||
assertSame(created, got); | ||
} | ||
|
||
@Test | ||
public void testAdd() { | ||
MyConfig dummy = ConfigFactory.create(MyConfig.class); | ||
MyConfig previous = ConfigCache.add("foo", dummy); | ||
MyConfig cached = ConfigCache.getOrCreate("foo", MyConfig.class); | ||
|
||
assertNull(previous); | ||
assertSame(dummy, cached); | ||
} | ||
|
||
} |