1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 package org.apache.shiro.hazelcast.cache; 20 21 import com.hazelcast.config.Config; 22 import com.hazelcast.core.Hazelcast; 23 import com.hazelcast.core.HazelcastInstance; 24 import org.apache.shiro.lang.ShiroException; 25 import org.apache.shiro.cache.Cache; 26 import org.apache.shiro.cache.CacheException; 27 import org.apache.shiro.cache.CacheManager; 28 import org.apache.shiro.cache.MapCache; 29 import org.apache.shiro.lang.util.Destroyable; 30 import org.apache.shiro.lang.util.Initializable; 31 import org.slf4j.Logger; 32 import org.slf4j.LoggerFactory; 33 34 import java.util.Map; 35 36 /** 37 * A {@code CacheManager} implementation backed by <a href="http://www.hazelcast.com/">Hazelcast</a>, 38 * "an open source clustering and highly scalable data distribution platform for Java" 39 * <p/> 40 * This implementation interacts with a {@link HazelcastInstance} to 41 * {@link HazelcastInstance#getMap(String) acquire} named {@link java.util.concurrent.ConcurrentMap ConcurrentMap} 42 * instances. Those clustered/distributed Map instances are then wrapped and made available to {@code CacheManager} 43 * callers as {@link MapCache} instances via {@link #getCache(String)}. 44 * <h2>Configuration</h2> 45 * This implementation's backing {@code HazelcastInstance} can be configured in one of three ways: 46 * <ol> 47 * <li>Doing nothing and leveraging default Hazelcast configuration mechanisms</li> 48 * <li>Supplying an already-existing {@code HazelcastInstance}</li> 49 * <li>Supplying a {@link Config} instance and using that to create a new {@code HazelcastInstance}</li> 50 * </ol> 51 * <h3>Default Configuration</h3> 52 * If you simply instantiate a {@code HazelcastCacheManager} and do nothing further, its backing 53 * {@link HazelcastInstance} instance will be created automatically by calling 54 * {@link Hazelcast#newHazelcastInstance(com.hazelcast.config.Config) Hazelcast.newHazelcastInstance(null)}. 55 * <p/> 56 * The null argument instructs Hazelcast to use whatever default configuration mechanism it has at its disposal, 57 * usually a {@code hazelcast.xml} file at the root of the classpath, or if that is not present, the 58 * {@code hazelcast-default.xml} file contained in the Hazelcast {@code .jar} file itself. 59 * <p/> 60 * <h3>An existing {@code HazelcastInstance}</h3> 61 * If you have created a {@code HazelcastInstance} outside of Shiro's knowledge/control, you can simply configure it 62 * to be used by calling {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}. 63 * <p/> 64 * <h3>A {@link Config} instance</h3> 65 * If you do not want to use the above two options, you can have programmatic control over all of Hazelcast's 66 * configuration by <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">creating and configuring a 67 * Config instance</a>. 68 * <p/> 69 * Once constructed, you can set it via {@link #setConfig(com.hazelcast.config.Config) setConfig(config)}. This config 70 * instance will be used to acquire a new Hazelcast instance by calling 71 * {@link Hazelcast#newHazelcastInstance(Config) Hazelcast.newHazelcastInstance(config)} 72 * 73 * @see <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast Configuration Documentation</a> 74 * @since 1.3 75 */ 76 public class HazelcastCacheManager implements CacheManager, Initializable, Destroyable { 77 78 private static final Logger LOGGER = LoggerFactory.getLogger(HazelcastCacheManager.class); 79 80 private boolean implicitlyCreated; 81 private HazelcastInstance hazelcastInstance; 82 private Config config; 83 84 /** 85 * Returns a {@link MapCache} instance representing the named Hazelcast-managed 86 * {@link com.hazelcast.core.IMap IMap}. The Hazelcast Map is obtained by calling 87 * {@link HazelcastInstance#getMap(String) hazelcastInstance.getMap(name)}. 88 * 89 * @param name the name of the cache to acquire. 90 * @param <K> the type of map key 91 * @param <V> the type of map value 92 * @return a {@link MapCache} instance representing the named Hazelcast-managed {@link com.hazelcast.core.IMap IMap}. 93 * @throws CacheException 94 * @see HazelcastInstance#getMap(String) 95 * @see #ensureHazelcastInstance() 96 */ 97 public <K, V> Cache<K, V> getCache(String name) throws CacheException { 98 //returned map is a ConcurrentMap 99 Map<K, V> map = ensureHazelcastInstance().getMap(name); 100 return new MapCache<K, V>(name, map); 101 } 102 103 /** 104 * Ensures that this implementation has a backing {@link HazelcastInstance}, and if not, implicitly creates one 105 * via {@link #createHazelcastInstance()}. 106 * 107 * @return the backing (potentially newly created) {@code HazelcastInstance}. 108 * @see #createHazelcastInstance() 109 * @see HazelcastInstance 110 */ 111 protected HazelcastInstance ensureHazelcastInstance() { 112 if (this.hazelcastInstance == null) { 113 this.hazelcastInstance = createHazelcastInstance(); 114 this.implicitlyCreated = true; 115 } 116 return this.hazelcastInstance; 117 } 118 119 /** 120 * Initializes this instance by {@link #ensureHazelcastInstance() ensuring} there is a backing 121 * {@link HazelcastInstance}. 122 * 123 * @throws ShiroException 124 * @see #ensureHazelcastInstance() 125 * @see HazelcastInstance 126 */ 127 public void init() throws ShiroException { 128 ensureHazelcastInstance(); 129 } 130 131 /** 132 * Implicitly creates and returns a new {@link HazelcastInstance} that will be used to back this implementation. 133 * This implementation calls: 134 * <pre> 135 * return Hazelcast.newHazelcastInstance(this.config); 136 * </pre> 137 * using any {@link #setConfig(com.hazelcast.config.Config) configured} {@code Config} object. If no config 138 * object has been specified, {@code this.config} will be {@code null}, thereby using Hazelcast's 139 * <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">default configuration mechanism</a>. 140 * <p/> 141 * Can be overridden by subclasses for custom creation behavior. 142 * 143 * @return a new {@link HazelcastInstance} that will be used to back this implementation 144 * @see Hazelcast#newHazelcastInstance(com.hazelcast.config.Config) 145 * @see Config 146 */ 147 protected HazelcastInstance createHazelcastInstance() { 148 return Hazelcast.newHazelcastInstance(this.config); 149 } 150 151 //needed for unit tests only - not part of Shiro's public API 152 153 /** 154 * NOT PART OF SHIRO'S ACCESSIBLE API. DO NOT DEPEND ON THIS. This method was added for testing purposes only. 155 * <p/> 156 * Returns {@code true} if this {@code HazelcastCacheManager} instance implicitly created the backing 157 * {@code HazelcastInstance}, or {@code false} if one was externally provided via 158 * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}. 159 * 160 * @return {@code true} if this {@code HazelcastCacheManager} instance implicitly created the backing 161 * {@code HazelcastInstance}, or {@code false} if one was externally provided via 162 * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}. 163 */ 164 protected final boolean isImplicitlyCreated() { 165 return this.implicitlyCreated; 166 } 167 168 /** 169 * Destroys any {@link #ensureHazelcastInstance() implicitly created} backing {@code HazelcastInstance}. If the 170 * backing Hazelcast was not implicitly created (i.e. because it was configured externally and supplied via 171 * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}), this method does 172 * nothing. 173 * 174 * @throws Exception if there is a problem shutting down 175 */ 176 public void destroy() throws Exception { 177 if (this.implicitlyCreated) { 178 try { 179 this.hazelcastInstance.getLifecycleService().shutdown(); 180 } catch (Throwable t) { 181 if (LOGGER.isWarnEnabled()) { 182 LOGGER.warn("Unable to cleanly shutdown implicitly created HazelcastInstance. " 183 + "Ignoring (shutting down)...", t); 184 } 185 } finally { 186 this.hazelcastInstance = null; 187 this.implicitlyCreated = false; 188 } 189 } 190 } 191 192 /** 193 * Returns the {@code HazelcastInstance} from which named {@link java.util.concurrent.ConcurrentMap ConcurrentMap} 194 * instances will be acquired to create {@link MapCache} instances. 195 * 196 * @return the {@code HazelcastInstance} from which named {@link java.util.concurrent.ConcurrentMap ConcurrentMap} 197 * instances will be acquired to create {@link MapCache} instances. 198 */ 199 public HazelcastInstance getHazelcastInstance() { 200 return hazelcastInstance; 201 } 202 203 /** 204 * Sets the {@code HazelcastInstance} from which named {@link java.util.concurrent.ConcurrentMap ConcurrentMap} 205 * instances will be acquired to create {@link MapCache} instances. 206 * 207 * @param hazelcastInstance the {@code HazelcastInstance} from which named 208 * {@link java.util.concurrent.ConcurrentMap ConcurrentMap} instances will be acquired to create 209 * {@link MapCache} instances. 210 */ 211 public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { 212 this.hazelcastInstance = hazelcastInstance; 213 } 214 215 /** 216 * Returns the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not 217 * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}, or {@code null} if the 218 * default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration 219 * mechanisms</a> will be used. 220 * 221 * @return the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not 222 * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}, or {@code null} if the 223 * default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration 224 * mechanisms</a> will be used. 225 * @see Hazelcast#newHazelcastInstance(com.hazelcast.config.Config) 226 */ 227 public Config getConfig() { 228 return config; 229 } 230 231 /** 232 * Sets the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not 233 * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}. {@code null} can be set if the 234 * default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration 235 * mechanisms</a> will be used. 236 * 237 * @param config the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not 238 * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}, or {@code null} if the 239 * default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration 240 * mechanisms</a> will be used. 241 */ 242 public void setConfig(Config config) { 243 this.config = config; 244 } 245 246 }