/** * Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.util.ehcache; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReference; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheException; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import net.sf.ehcache.config.Configuration; import net.sf.ehcache.config.ConfigurationFactory; import org.apache.commons.io.IOUtils; import com.opengamma.OpenGammaRuntimeException; import com.opengamma.util.ArgumentChecker; /** * Utilities for working with EHCache. */ public final class EHCacheUtils { /** The set of unique names already seen. */ private static final ConcurrentMap<String, String> UNIQUE_TEST_NAMES = new ConcurrentHashMap<>(); /** The default Ehcache configuration file name. */ private static final String DEFAULT_EHCACHE_CONFIG_FILE = "/com/opengamma/util/ehcache/default-ehcache.xml"; /** The default test Ehcache configuration file name. */ private static final String DEFAULT_TEST_EHCACHE_CONFIG_FILE = "/com/opengamma/util/ehcache/test-ehcache.xml"; /** The cached content of the default config file. */ private static final AtomicReference<byte[]> CONFIG_TEST_FILE_BYTES = new AtomicReference<>(); /** Null value to cache */ private static final Object NULL = new Object(); /** * Restrictive constructor. */ private EHCacheUtils() { } /** * Creates a unique cache manager. * * @param uniqueName the unique name, typically a test case class * @return the unique cache manager, not null */ public static CacheManager createTestCacheManager(Class<?> uniqueName) { ArgumentChecker.notNull(uniqueName, "uniqueName"); return createTestCacheManager(uniqueName.getName()); } /** * Creates a unique cache manager. * * @param uniqueName the unique name, typically a test case class name * @return the unique cache manager, not null */ public static CacheManager createTestCacheManager(String uniqueName) { ArgumentChecker.notNull(uniqueName, "uniqueName"); if (UNIQUE_TEST_NAMES.putIfAbsent(uniqueName, uniqueName) != null) { throw new OpenGammaRuntimeException("CacheManager has already been created with unique name: " + uniqueName); } try { InputStream configStream = getTestEhCacheConfig(); Configuration config = ConfigurationFactory.parseConfiguration(configStream); config.setName(uniqueName); config.setUpdateCheck(false); return CacheManager.newInstance(config); } catch (CacheException ex) { throw new OpenGammaRuntimeException("Unable to create CacheManager", ex); } } private static synchronized InputStream getTestEhCacheConfig() { byte[] bytes = CONFIG_TEST_FILE_BYTES.get(); if (bytes == null) { String ehcacheConfigFile = DEFAULT_TEST_EHCACHE_CONFIG_FILE; String overrideEhcacheConfigFile = System.getProperty("ehcache.config"); // passed in by Ant if (overrideEhcacheConfigFile != null) { ehcacheConfigFile = overrideEhcacheConfigFile; System.err.println("Using ehcache.config from system property: " + ehcacheConfigFile); } else { System.err.println("Using default test ehcache.config file name: " + ehcacheConfigFile); } InputStream in = EHCacheUtils.class.getResourceAsStream(ehcacheConfigFile); try { bytes = IOUtils.toByteArray(in); } catch (IOException ex) { throw new OpenGammaRuntimeException("Unable to read cache configuration", ex); } CONFIG_TEST_FILE_BYTES.compareAndSet(null, bytes); bytes = CONFIG_TEST_FILE_BYTES.get(); } return new ByteArrayInputStream(bytes); } //------------------------------------------------------------------------- /** * Creates/returns the singleton cache manager using the default configuration. This should be used only in a test * environment; in other environments a properly configured cache manager should be injected. * <p> * Since Ehcache 2.5 multiple cache managers with the same name are not allowed, so parallel tests have to use the * same default cache manager instance. * * (from Ehcache docs) * With Ehcache 2.5.2 and higher, the behavior of the CacheManager creation methods is as follows: * CacheManager.newInstance(Configuration configuration) – Create a new CacheManager or return the existing one * named in the configuration. * CacheManager.create() – Create a new singleton CacheManager with default configuration, or return the existing * singleton. This is the same as CacheManager.getInstance(). * CacheManager.create(Configuration configuration) – Create a singleton CacheManager with the passed-in * configuration, or return the existing singleton. * new CacheManager(Configuration configuration) – Create a new CacheManager, or throw an exception if the * CacheManager named in the configuration already exists or if the parameter (configuration) is null. * <p> * Tests should use {@link #createTestCacheManager(String)} or {@link #createTestCacheManager(Class)}. * * @return the cache manager, not null */ public static CacheManager createCacheManager() { try { return CacheManager.newInstance(getEhCacheConfig()); } catch (CacheException ex) { throw new OpenGammaRuntimeException("Unable to create CacheManager", ex); } } private static synchronized Configuration getEhCacheConfig() { String ehcacheConfigFile = DEFAULT_EHCACHE_CONFIG_FILE; String overrideEhcacheConfigFile = System.getProperty("ehcache.config"); // passed in by Ant if (overrideEhcacheConfigFile != null) { ehcacheConfigFile = overrideEhcacheConfigFile; System.err.println("Using ehcache.config from system property: " + ehcacheConfigFile); } else { System.err.println("Using default ehcache.config file name: " + ehcacheConfigFile); } try (InputStream resource = EHCacheUtils.class.getResourceAsStream(ehcacheConfigFile)) { Configuration config = ConfigurationFactory.parseConfiguration(resource); config.setUpdateCheck(false); return config; } catch (IOException ex) { throw new OpenGammaRuntimeException("Unable to read ehcache file", ex); } } //------------------------------------------------------------------------- /** * Clears the contents of a cache manager. * * @param cacheManager the cache manager, not null */ public static void clear(CacheManager cacheManager) { ArgumentChecker.notNull(cacheManager, "cacheManager"); cacheManager.clearAll(); } /** * Clears the contents of a named cache, if that cache exists, without deleting the cache itself. * * @param cacheManager the cache manager, not null * @param cacheName the cache name, not null */ public static void clear(CacheManager cacheManager, String cacheName) { ArgumentChecker.notNull(cacheManager, "cacheManager"); ArgumentChecker.notNull(cacheName, "cacheName"); Cache cache = cacheManager.getCache(cacheName); if (cache != null) { cache.removeAll(); } } /** * Adds a cache to the cache manager if necessary. * <p> * The cache configuration is loaded from the manager's configuration, or the default is used. * * @param manager the cache manager, not null * @param cache the cache, not null */ public static void addCache(CacheManager manager, Cache cache) { ArgumentChecker.notNull(manager, "manager"); ArgumentChecker.notNull(cache, "cache"); if (!manager.cacheExists(cache.getName())) { try { manager.addCache(cache); } catch (Exception ex) { throw new OpenGammaRuntimeException("Unable to add cache " + cache.getName(), ex); } } } /** * Adds a cache to the cache manager if necessary. * <p> * The cache configuration is loaded from the manager's configuration, or the default is used. * * @param manager the cache manager, not null * @param name the cache name, not null */ public static void addCache(final CacheManager manager, final String name) { if (!manager.cacheExists(name)) { try { manager.addCache(name); } catch (Exception ex) { throw new OpenGammaRuntimeException("Unable to create cache " + name, ex); } } } /** * Gets a cache from the manager. * @param manager the manager, not null * @param name the cache name, not null * @return the cache, not null */ public static Cache getCacheFromManager(CacheManager manager, String name) { try { return manager.getCache(name); } catch (Exception ex) { throw new OpenGammaRuntimeException( "Unable to retrieve from CacheManager, cache: " + name, ex); } } @SuppressWarnings("unchecked") public static <T> T get(final Element e) { final Object o = e.getObjectValue(); if (o == NULL) { return null; } if (o instanceof RuntimeException) { throw (RuntimeException) o; } return (T) o; } public static <T> Collection<T> putValues(final Object key, final Collection<T> values, final Cache cache) { final Element e; if (values == null) { e = new Element(key, NULL); } else { e = new Element(key, values); } cache.put(e); return values; } public static <T> T putValue(final Object key, final T value, final Cache cache) { final Element e; if (value == null) { e = new Element(key, NULL); } else { e = new Element(key, value); } cache.put(e); return value; } public static <T> T putException(final Object key, final RuntimeException e, final Cache cache) { cache.put(new Element(key, e)); throw e; } //------------------------------------------------------------------------- /** * Shuts down the cache manager, only really useful in tests. * <p> * The cache manager that is shutdown should be started with * {@code new CacheManager()} or similar. * * @param cacheManager the cache manager, null ignored * @return null */ public static CacheManager shutdownQuiet(CacheManager cacheManager) { if (cacheManager != null) { try { cacheManager.shutdown(); } catch (RuntimeException ex) { // ignore } } return null; } }