/* * JBoss, Home of Professional Open Source. * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.cache.loader; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.cache.CacheException; import org.jboss.cache.CacheSPI; import org.jboss.cache.Fqn; import org.jboss.cache.RegionManager; import org.jboss.cache.config.CacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig; import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig.SingletonStoreConfig; import org.jboss.cache.config.Configuration; import org.jboss.cache.config.ConfigurationException; import org.jboss.cache.factories.ComponentRegistry; import org.jboss.cache.factories.annotations.Inject; import org.jboss.cache.factories.annotations.Start; import org.jboss.cache.factories.annotations.Stop; import org.jboss.cache.util.reflect.ReflectionUtil; import java.util.ArrayList; import java.util.Iterator; import java.util.Set; import java.util.StringTokenizer; /** * Manages all cache loader functionality. This class is typically initialised with an XML DOM Element, * represeting a cache loader configuration, or a {@link org.jboss.cache.config.CacheLoaderConfig} object. *

* Usage: *

* * CacheLoaderManager manager = new CacheLoaderManager(); * manager.setConfig(myXmlSnippet, myTreeCache); * CacheLoader loader = manager.getCacheLoader(); * *

* The XML configuration passed in would typically look like: *

* * * false * / *

* * org.jboss.cache.loader.FileCacheLoader * true * false * false * * location=/tmp/file * * * * ]]> * * * @author Manik Surtani (manik AT jboss DOT org) * @author Galder Zamarreno */ public class CacheLoaderManager { private static final Log log = LogFactory.getLog(CacheLoaderManager.class); private CacheLoaderConfig config; private CacheSPI cache; private CacheLoader loader; private boolean fetchPersistentState; private Configuration configuration; private RegionManager regionManager; private ComponentRegistry registry; @Inject public void injectDependencies(CacheSPI cache, Configuration configuration, RegionManager regionManager, ComponentRegistry registry) { this.regionManager = regionManager; this.config = configuration.getCacheLoaderConfig(); this.cache = cache; this.configuration = configuration; this.registry = registry; if (config != null) { try { loader = createCacheLoader(); } catch (Exception e) { throw new CacheException("Unable to create cache loaders", e); } } } /** * Sets a configuration object and creates a cacheloader accordingly. Primarily used for testing. * * @param config * @param cache * @throws CacheException */ public void setConfig(CacheLoaderConfig config, CacheSPI cache, Configuration configuration) throws CacheException { this.config = config == null ? configuration.getCacheLoaderConfig() : config; this.cache = cache; this.configuration = configuration; if (config != null) { try { loader = createCacheLoader(); } catch (Exception e) { throw new CacheException("Unable to create cache loaders", e); } } } /** * Creates the cache loader based on a cache loader config passed in. * * @return a configured cacheloader * @throws IllegalAccessException * @throws InstantiationException * @throws ClassNotFoundException */ private CacheLoader createCacheLoader() throws Exception { CacheLoader tmpLoader; // if we only have a single cache loader configured in the chaining cacheloader then // don't use a chaining cache loader at all. ArrayList finalConfigs = new ArrayList(); // also if we are using passivation then just directly use the first cache loader. if (config.useChainingCacheLoader()) { // create chaining cache loader. tmpLoader = new ChainingCacheLoader(); ChainingCacheLoader ccl = (ChainingCacheLoader) tmpLoader; Iterator it = config.getIndividualCacheLoaderConfigs().iterator(); // only one cache loader may have fetchPersistentState to true. int numLoadersWithFetchPersistentState = 0; while (it.hasNext()) { CacheLoaderConfig.IndividualCacheLoaderConfig cfg = (CacheLoaderConfig.IndividualCacheLoaderConfig) it.next(); if (cfg.isFetchPersistentState()) { numLoadersWithFetchPersistentState++; fetchPersistentState = true; } if (numLoadersWithFetchPersistentState > 1) { throw new Exception("Invalid cache loader configuration!! Only ONE cache loader may have fetchPersistentState set to true. Cache will not start!"); } assertNotSingletonAndShared(cfg); CacheLoader l = createCacheLoader(cfg, cache); cfg = l.getConfig(); finalConfigs.add(cfg); // Only loaders that deal w/ state transfer factor into // whether the overall chain supports ExtendedCacheLoader ccl.addCacheLoader(l, cfg); } } else { CacheLoaderConfig.IndividualCacheLoaderConfig cfg = config.getIndividualCacheLoaderConfigs().get(0); tmpLoader = createCacheLoader(cfg, cache); finalConfigs.add(tmpLoader.getConfig() == null ? cfg : tmpLoader.getConfig()); fetchPersistentState = cfg.isFetchPersistentState(); assertNotSingletonAndShared(cfg); } // Update the config with those actually used by the loaders ReflectionUtil.setValue(config, "accessible", true); config.setIndividualCacheLoaderConfigs(finalConfigs); return tmpLoader; } private void assertNotSingletonAndShared(IndividualCacheLoaderConfig cfg) { SingletonStoreConfig ssc = cfg.getSingletonStoreConfig(); if (ssc != null && ssc.isSingletonStoreEnabled() && config.isShared()) throw new ConfigurationException("Invalid cache loader configuration!! If a cache loader is configured as a singleton, the cache loader cannot be shared in a cluster!"); } /** * Creates the cache loader based on the configuration. * * @param cfg * @param cache * @return a cache loader * @throws Exception */ @SuppressWarnings("deprecation") private CacheLoader createCacheLoader(CacheLoaderConfig.IndividualCacheLoaderConfig cfg, CacheSPI cache) throws Exception { // create loader CacheLoader tmpLoader = cfg.getCacheLoader() == null ? createInstance(cfg.getClassName()) : cfg.getCacheLoader(); if (tmpLoader != null) { // async? if (cfg.isAsync()) { CacheLoader asyncDecorator; asyncDecorator = new AsyncCacheLoader(tmpLoader); tmpLoader = asyncDecorator; } if (cfg.isIgnoreModifications()) { AbstractDelegatingCacheLoader readOnlyDecorator; readOnlyDecorator = new ReadOnlyDelegatingCacheLoader(tmpLoader); tmpLoader = readOnlyDecorator; } // singleton? SingletonStoreConfig ssc = cfg.getSingletonStoreConfig(); if (ssc != null && ssc.isSingletonStoreEnabled()) { Object decorator = createInstance(ssc.getSingletonStoreClass()); /* class providing singleton store functionality must extend AbstractDelegatingCacheLoader so that * underlying cacheloader can be set. */ if (decorator instanceof AbstractDelegatingCacheLoader) { AbstractDelegatingCacheLoader singletonDecorator = (AbstractDelegatingCacheLoader) decorator; /* set the cache loader to where calls will be delegated by the class providing the singleton * store functionality. */ singletonDecorator.setCacheLoader(tmpLoader); tmpLoader = singletonDecorator; } else { throw new Exception("Invalid cache loader configuration!! Singleton store implementation class must extend org.jboss.cache.loader.AbstractDelegatingCacheLoader"); } } // load props tmpLoader.setConfig(cfg); setCacheInLoader(cache, tmpLoader); // we should not be creating/starting the cache loader here - this should be done in the separate // startCacheLoader() method. // tmpLoader.create(); // tmpLoader.start(); if (configuration != null && configuration.isUseRegionBasedMarshalling()) { tmpLoader.setRegionManager(regionManager); } } return tmpLoader; } /** * Sets the cache instance associated with the given cache loader. This method was created for testing purpouses * so that it can be overriden in the mock version of the CacheLoaderManager. * * @param c instance of cache to be set in cache loader * @param loader cache loader to which assign the cache instance */ protected void setCacheInLoader(CacheSPI c, CacheLoader loader) { loader.setCache(c); } private CacheLoader createInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException { if (log.isTraceEnabled()) log.trace("instantiating class " + className); Class cl = Thread.currentThread().getContextClassLoader().loadClass(className); return (CacheLoader) cl.newInstance(); } /** * Performs a preload on the cache based on the cache loader preload configs used when configuring the cache. * * @throws Exception */ @Start(priority = 50) public void preloadCache() throws CacheException { if (loader != null) { if (config.getPreload() == null || config.getPreload().equals("")) return; if (log.isDebugEnabled()) log.debug("preloading transient state from cache loader " + loader); StringTokenizer st = new StringTokenizer(config.getPreload(), ","); String tok; Fqn fqn; long start, stop, total; start = System.currentTimeMillis(); while (st.hasMoreTokens()) { tok = st.nextToken(); fqn = Fqn.fromString(tok.trim()); if (log.isTraceEnabled()) log.trace("preloading " + fqn); preload(fqn, true, true); } stop = System.currentTimeMillis(); total = stop - start; if (log.isDebugEnabled()) { log.debug("preloading transient state from cache loader was successful (in " + total + " milliseconds)"); } } } /** * Preloads a specific Fqn into the cache from the configured cacheloader * * @param fqn fqn to preload * @param preloadParents whether we preload parents * @param preloadChildren whether we preload children * @throws CacheException if we are unable to preload */ public void preload(Fqn fqn, boolean preloadParents, boolean preloadChildren) throws CacheException { cache.getInvocationContext().getOptionOverrides().setSkipDataGravitation(true); cache.getInvocationContext().getOptionOverrides().setSkipCacheStatusCheck(true); // 1. Load the attributes first // but this will go down the entire damn chain!! :S cache.get(fqn, "bla"); // 2. Then load the parents if (preloadParents) { Fqn tmp_fqn = Fqn.ROOT; for (int i = 0; i < fqn.size() - 1; i++) { tmp_fqn = Fqn.fromRelativeElements(tmp_fqn, fqn.get(i)); cache.get(tmp_fqn, "bla"); } } if (preloadChildren) { // 3. Then recursively for all child nodes, preload them as well Set children; try { children = loader.getChildrenNames(fqn); } catch (Exception e) { throw new CacheException("Unable to preload from cache loader", e); } if (children != null) { for (Object aChildren : children) { String child_name = (String) aChildren; Fqn child_fqn = Fqn.fromRelativeElements(fqn, child_name); preload(child_fqn, false, true); } } } } /** * Returns the configuration element of the cache loaders */ public CacheLoaderConfig getCacheLoaderConfig() { return config; } /** * Returns the cache loader */ public CacheLoader getCacheLoader() { return loader; } /** * Tests if we're using passivation */ public boolean isPassivation() { return config.isPassivation(); } /** * Returns true if at least one of the configured cache loaders has set fetchPersistentState to true. */ public boolean isFetchPersistentState() { return fetchPersistentState; } @Stop public void stopCacheLoader() { if (loader != null) { // stop the cache loader loader.stop(); // destroy the cache loader loader.destroy(); } } @Start public void startCacheLoader() throws CacheException { if (config == null) config = configuration.getCacheLoaderConfig(); if (config != null && loader == null) { try { loader = createCacheLoader(); } catch (Exception e) { throw new CacheException("Unable to create cache loaders", e); } } if (loader != null) { try { // wire any deps. registry.wireDependencies(loader); // create the cache loader loader.create(); // start the cache loader loader.start(); purgeLoaders(false); } catch (Exception e) { throw new CacheException("Unable to start cache loaders", e); } fetchPersistentState = fetchPersistentState || (loader.getConfig() != null && loader.getConfig().isFetchPersistentState()); fetchPersistentState = fetchPersistentState || (config != null && config.isFetchPersistentState()); } } public void purgeLoaders(boolean force) throws Exception { if ((loader instanceof ChainingCacheLoader) && !force) { ((ChainingCacheLoader) loader).purgeIfNecessary(); } else { CacheLoaderConfig.IndividualCacheLoaderConfig first = getCacheLoaderConfig().getFirstCacheLoaderConfig(); if (force || (first != null && first.isPurgeOnStartup())) { loader.remove(Fqn.ROOT); } } } }