001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.shiro.cache.ehcache; 020 021import org.apache.shiro.cache.Cache; 022import org.apache.shiro.cache.CacheException; 023import org.apache.shiro.cache.CacheManager; 024import org.apache.shiro.config.ConfigurationException; 025import org.apache.shiro.io.ResourceUtils; 026import org.apache.shiro.util.Destroyable; 027import org.apache.shiro.util.Initializable; 028import org.slf4j.Logger; 029import org.slf4j.LoggerFactory; 030 031import java.io.IOException; 032import java.io.InputStream; 033 034/** 035 * Shiro {@code CacheManager} implementation utilizing the Ehcache framework for all cache functionality. 036 * <p/> 037 * This class can {@link #setCacheManager(net.sf.ehcache.CacheManager) accept} a manually configured 038 * {@link net.sf.ehcache.CacheManager net.sf.ehcache.CacheManager} instance, 039 * or an {@code ehcache.xml} path location can be specified instead and one will be constructed. If neither are 040 * specified, Shiro's failsafe <code><a href="./ehcache.xml">ehcache.xml</a>} file will be used by default. 041 * <p/> 042 * This implementation requires EhCache 1.2 and above. Make sure EhCache 1.1 or earlier 043 * is not in the classpath or it will not work. 044 * <p/> 045 * Please see the <a href="http://ehcache.sf.net" target="_top">Ehcache website</a> for their documentation. 046 * 047 * @see <a href="http://ehcache.sf.net" target="_top">The Ehcache website</a> 048 * @since 0.2 049 */ 050public class EhCacheManager implements CacheManager, Initializable, Destroyable { 051 052 /** 053 * This class's private log instance. 054 */ 055 private static final Logger log = LoggerFactory.getLogger(EhCacheManager.class); 056 057 /** 058 * The EhCache cache manager used by this implementation to create caches. 059 */ 060 protected net.sf.ehcache.CacheManager manager; 061 062 /** 063 * Indicates if the CacheManager instance was implicitly/automatically created by this instance, indicating that 064 * it should be automatically cleaned up as well on shutdown. 065 */ 066 private boolean cacheManagerImplicitlyCreated = false; 067 068 /** 069 * Classpath file location of the ehcache CacheManager config file. 070 */ 071 private String cacheManagerConfigFile = "classpath:org/apache/shiro/cache/ehcache/ehcache.xml"; 072 073 /** 074 * Default no argument constructor 075 */ 076 public EhCacheManager() { 077 } 078 079 /** 080 * Returns the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance. 081 * 082 * @return the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance. 083 */ 084 public net.sf.ehcache.CacheManager getCacheManager() { 085 return manager; 086 } 087 088 /** 089 * Sets the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance. 090 * 091 * @param manager the wrapped Ehcache {@link net.sf.ehcache.CacheManager CacheManager} instance. 092 */ 093 public void setCacheManager(net.sf.ehcache.CacheManager manager) { 094 this.manager = manager; 095 } 096 097 /** 098 * Returns the resource location of the config file used to initialize a new 099 * EhCache CacheManager instance. The string can be any resource path supported by the 100 * {@link org.apache.shiro.io.ResourceUtils#getInputStreamForPath(String)} call. 101 * <p/> 102 * This property is ignored if the CacheManager instance is injected directly - that is, it is only used to 103 * lazily create a CacheManager if one is not already provided. 104 * 105 * @return the resource location of the config file used to initialize the wrapped 106 * EhCache CacheManager instance. 107 */ 108 public String getCacheManagerConfigFile() { 109 return this.cacheManagerConfigFile; 110 } 111 112 /** 113 * Sets the resource location of the config file used to initialize the wrapped 114 * EhCache CacheManager instance. The string can be any resource path supported by the 115 * {@link org.apache.shiro.io.ResourceUtils#getInputStreamForPath(String)} call. 116 * <p/> 117 * This property is ignored if the CacheManager instance is injected directly - that is, it is only used to 118 * lazily create a CacheManager if one is not already provided. 119 * 120 * @param classpathLocation resource location of the config file used to create the wrapped 121 * EhCache CacheManager instance. 122 */ 123 public void setCacheManagerConfigFile(String classpathLocation) { 124 this.cacheManagerConfigFile = classpathLocation; 125 } 126 127 /** 128 * Acquires the InputStream for the ehcache configuration file using 129 * {@link ResourceUtils#getInputStreamForPath(String) ResourceUtils.getInputStreamForPath} with the 130 * path returned from {@link #getCacheManagerConfigFile() getCacheManagerConfigFile()}. 131 * 132 * @return the InputStream for the ehcache configuration file. 133 */ 134 protected InputStream getCacheManagerConfigFileInputStream() { 135 String configFile = getCacheManagerConfigFile(); 136 try { 137 return ResourceUtils.getInputStreamForPath(configFile); 138 } catch (IOException e) { 139 throw new ConfigurationException("Unable to obtain input stream for cacheManagerConfigFile [" + 140 configFile + "]", e); 141 } 142 } 143 144 /** 145 * Loads an existing EhCache from the cache manager, or starts a new cache if one is not found. 146 * 147 * @param name the name of the cache to load/create. 148 */ 149 public final <K, V> Cache<K, V> getCache(String name) throws CacheException { 150 151 if (log.isTraceEnabled()) { 152 log.trace("Acquiring EhCache instance named [" + name + "]"); 153 } 154 155 try { 156 net.sf.ehcache.Ehcache cache = ensureCacheManager().getEhcache(name); 157 if (cache == null) { 158 if (log.isInfoEnabled()) { 159 log.info("Cache with name '{}' does not yet exist. Creating now.", name); 160 } 161 this.manager.addCache(name); 162 163 cache = manager.getCache(name); 164 165 if (log.isInfoEnabled()) { 166 log.info("Added EhCache named [" + name + "]"); 167 } 168 } else { 169 if (log.isInfoEnabled()) { 170 log.info("Using existing EHCache named [" + cache.getName() + "]"); 171 } 172 } 173 return new EhCache<K, V>(cache); 174 } catch (net.sf.ehcache.CacheException e) { 175 throw new CacheException(e); 176 } 177 } 178 179 /** 180 * Initializes this instance. 181 * <p/> 182 * If a {@link #setCacheManager CacheManager} has been 183 * explicitly set (e.g. via Dependency Injection or programatically) prior to calling this 184 * method, this method does nothing. 185 * <p/> 186 * However, if no {@code CacheManager} has been set, the default Ehcache singleton will be initialized, where 187 * Ehcache will look for an {@code ehcache.xml} file at the root of the classpath. If one is not found, 188 * Ehcache will use its own failsafe configuration file. 189 * <p/> 190 * Because Shiro cannot use the failsafe defaults (fail-safe expunges cached objects after 2 minutes, 191 * something not desirable for Shiro sessions), this class manages an internal default configuration for 192 * this case. 193 * 194 * @throws org.apache.shiro.cache.CacheException 195 * if there are any CacheExceptions thrown by EhCache. 196 * @see net.sf.ehcache.CacheManager#create 197 */ 198 public final void init() throws CacheException { 199 ensureCacheManager(); 200 } 201 202 private net.sf.ehcache.CacheManager ensureCacheManager() { 203 try { 204 if (this.manager == null) { 205 if (log.isDebugEnabled()) { 206 log.debug("cacheManager property not set. Constructing CacheManager instance... "); 207 } 208 //using the CacheManager constructor, the resulting instance is _not_ a VM singleton 209 //(as would be the case by calling CacheManager.getInstance(). We do not use the getInstance here 210 //because we need to know if we need to destroy the CacheManager instance - using the static call, 211 //we don't know which component is responsible for shutting it down. By using a single EhCacheManager, 212 //it will always know to shut down the instance if it was responsible for creating it. 213 this.manager = new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()); 214 if (log.isTraceEnabled()) { 215 log.trace("instantiated Ehcache CacheManager instance."); 216 } 217 cacheManagerImplicitlyCreated = true; 218 if (log.isDebugEnabled()) { 219 log.debug("implicit cacheManager created successfully."); 220 } 221 } 222 return this.manager; 223 } catch (Exception e) { 224 throw new CacheException(e); 225 } 226 } 227 228 /** 229 * Shuts-down the wrapped Ehcache CacheManager <b>only if implicitly created</b>. 230 * <p/> 231 * If another component injected 232 * a non-null CacheManager into this instace before calling {@link #init() init}, this instance expects that same 233 * component to also destroy the CacheManager instance, and it will not attempt to do so. 234 */ 235 public void destroy() { 236 if (cacheManagerImplicitlyCreated) { 237 try { 238 net.sf.ehcache.CacheManager cacheMgr = getCacheManager(); 239 cacheMgr.shutdown(); 240 } catch (Exception e) { 241 if (log.isWarnEnabled()) { 242 log.warn("Unable to cleanly shutdown implicitly created CacheManager instance. " + 243 "Ignoring (shutting down)..."); 244 } 245 } 246 cacheManagerImplicitlyCreated = false; 247 } 248 } 249}