/* * Copyright (C) 2000 - 2008 TagServlet Ltd * * This file is part of the BlueDragon Java Open Source Project. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package com.naryx.tagfusion.cfm.cache; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.aw20.security.MD5; import com.naryx.tagfusion.cfm.cache.impl.MemcachedCacheImpl; import com.naryx.tagfusion.cfm.cache.impl.MemoryDiskCacheImpl; import com.naryx.tagfusion.cfm.cache.impl.MongoCacheImpl; import com.naryx.tagfusion.cfm.cache.impl.NullCacheImpl; import com.naryx.tagfusion.cfm.engine.cfBooleanData; import com.naryx.tagfusion.cfm.engine.cfEngine; import com.naryx.tagfusion.cfm.engine.cfStructData; public class CacheFactory extends Object { public static String CFFUNCTION = "function"; public static CacheFactory thisInst; static{ new CacheFactory(); } private HashMap<String,CacheInterface> cacheEngines; private Map<String, lockSemaphore> cacheLocks; private CacheFactory(){ thisInst = this; cacheEngines = new HashMap<String,CacheInterface>(); cacheLocks = new HashMap<String, lockSemaphore>(); cfStructData props = new cfStructData(); props.setData("type", "null"); try { createCacheEngine( "nullcache", props ); } catch (Exception e) {} setMemoryDiskCache( CFFUNCTION, 100, false, 0 ); setMemoryDiskCache( "default", 25, false, 0 ); } public static void shutdown(){ Iterator<CacheInterface> it = thisInst.cacheEngines.values().iterator(); while ( it.hasNext() ){ it.next().shutdown(); it.remove(); } cfEngine.log("CacheFactory: All Cache Regions shutdown"); } public static String[] getAllRegionsNames(){ return thisInst.cacheEngines.keySet().toArray( new String[0] ); } public static boolean isCacheEnabled( String type ){ return thisInst.cacheEngines.containsKey( type.toLowerCase() ); } public static CacheInterface getCacheEngine( String type ){ CacheInterface e = thisInst.cacheEngines.get( type.toLowerCase() ); return ( e != null ) ? e : thisInst.cacheEngines.get( "nullcache" ); } public static void removeCacheEngine(String type) { synchronized( thisInst ){ CacheInterface ci = thisInst.cacheEngines.remove( type.toLowerCase() ); if ( ci!= null ){ ci.shutdown(); cfEngine.log( ci.getName() + "." + type + ": removed" ); } } } public static void createCacheEngine(String region, cfStructData props) throws Exception { if ( !props.containsKey("type") ) throw new Exception("missing 'type' parameter"); // Pull out the type CacheInterface cacheinterface = null; String type = props.getData("type").getString().toLowerCase(); if ( type.equals("null") ) cacheinterface = new NullCacheImpl(); else if ( type.equals("memorydisk") ) cacheinterface = new MemoryDiskCacheImpl(); else if ( type.equals("memcached") ) cacheinterface = new MemcachedCacheImpl(); else if ( type.equals("mongo") ) cacheinterface = new MongoCacheImpl(); // Make sure we have one if ( cacheinterface == null ) throw new Exception("unknown 'type' parameter [" + type + "]" ); synchronized( thisInst ){ cacheinterface.setProperties( region.toLowerCase(), props); thisInst.cacheEngines.put( region.toLowerCase(), cacheinterface ); } } /* * Creates a normalized cache name from a potentially long string */ public static String createCacheKey( String data ){ return MD5.getDigest( data + "crc" ); } /* * ------------------------------------------------------------------ * We use a simple semaphore to track the number of waiting threads * waiting on a lock. We only remove from the list of locks when * there are no more thread waitings. Since the getLock/removeLock * functions are synchronized there is no need to synchronize the * lockSemaphore classes. * ------------------------------------------------------------------ */ public static Object getLock( String key ) { synchronized ( thisInst.cacheLocks ){ lockSemaphore lock = thisInst.cacheLocks.get( key ); if ( lock == null ){ lock = thisInst.new lockSemaphore(); thisInst.cacheLocks.put( key, lock ); } lock.lock(); return lock; } } public static void removeLock( String key ){ synchronized ( thisInst.cacheLocks ){ lockSemaphore lock = thisInst.cacheLocks.get( key ); if ( lock == null ){ return; } lock.unlock(); if ( lock.isFree() ){ thisInst.cacheLocks.remove( key ); } } } class lockSemaphore { int inUse = 0; public void lock(){ inUse = inUse + 1; } public void unlock(){ inUse = inUse - 1; } public boolean isFree(){ return (inUse == 0); } } /** * Helper method for creating the default engines for the core engine * * @param region * @param cacheCount * @param diskPersit * @param diskMaxMB */ public static void setMemoryDiskCache(String region, int cacheCount, boolean diskPersit, int diskMaxMB ) { cfStructData props = new cfStructData(); props.setData("type", "memorydisk"); props.setData("diskpersistent", cfBooleanData.TRUE ); props.setData("diskcleanonstart", cfBooleanData.TRUE ); props.setData("diskmaxsizemb", diskMaxMB); props.setData("size", cacheCount); try { createCacheEngine( region, props ); } catch (Exception e) {} } }