You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by bd...@apache.org on 2016/07/13 20:07:59 UTC

[2/3] shiro git commit: SHIRO-412: I got tired referencing Hazelcast-based examples I created outside of Shiro, so I'm bringing it into the project properly. Works great. Hazelcast is Apache-licensed as well and probably should be our default caching r

SHIRO-412: I got tired referencing Hazelcast-based examples I created outside of Shiro, so I'm bringing it into the project properly.  Works great.  Hazelcast is Apache-licensed as well and probably should be our default caching recommendation (Ehcache is I think still LGPL and a touchy issue - we can't extend any of its code - just invoke it).

git-svn-id: https://svn.apache.org/repos/asf/shiro/trunk@1433611 13f79535-47bb-0310-9956-ffa450edef68


Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/0b505c27
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/0b505c27
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/0b505c27

Branch: refs/heads/1.3.x
Commit: 0b505c272d61372bd2674ee88fd103e62d71714e
Parents: bdc0b7c
Author: Les Hazlewood <lh...@apache.org>
Authored: Tue Jan 15 20:08:12 2013 +0000
Committer: Brian Demers <bd...@apache.org>
Committed: Wed Jul 13 13:23:07 2016 -0400

----------------------------------------------------------------------
 .../cache/MemoryConstrainedCacheManager.java    |   4 +-
 pom.xml                                         |  24 +-
 support/ehcache/pom.xml                         |   2 +-
 .../shiro/cache/ehcache/EhCacheManager.java     |   8 +-
 support/features/pom.xml                        |   3 +-
 .../features/src/main/resources/features.xml    |  19 ++
 support/hazelcast/pom.xml                       |  83 +++++++
 .../hazelcast/cache/HazelcastCacheManager.java  | 245 +++++++++++++++++++
 .../cache/HazelcastCacheManagerTest.groovy      | 181 ++++++++++++++
 .../src/test/resources/log4j.properties         |  35 +++
 support/pom.xml                                 |   1 +
 11 files changed, 597 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/core/src/main/java/org/apache/shiro/cache/MemoryConstrainedCacheManager.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/cache/MemoryConstrainedCacheManager.java b/core/src/main/java/org/apache/shiro/cache/MemoryConstrainedCacheManager.java
index 56cf12c..9b331e4 100644
--- a/core/src/main/java/org/apache/shiro/cache/MemoryConstrainedCacheManager.java
+++ b/core/src/main/java/org/apache/shiro/cache/MemoryConstrainedCacheManager.java
@@ -28,8 +28,8 @@ import org.apache.shiro.util.SoftHashMap;
  * <p/>
  * While the {@code Cache} instances created are thread-safe, they do not offer any enterprise-level features such as
  * cache coherency, optimistic locking, failover or other similar features.  For more enterprise features, consider
- * using a different {@code CacheManager} implementation backed by an enterprise-grade caching product (EhCache,
- * TerraCotta, Coherence, GigaSpaces, etc, etc).
+ * using a different {@code CacheManager} implementation backed by an enterprise-grade caching product (Hazelcast,
+ * EhCache, TerraCotta, Coherence, GigaSpaces, etc, etc).
  *
  * @since 1.0
  */

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 05b7836..67b9b9a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,7 +80,9 @@
         <crowd.version>1.5.2</crowd.version>
         <!-- Don't change this version without also changing the shiro-ehcache and shiro-features
              modules' OSGi metadata: -->
-        <ehcache.version>2.5.0</ehcache.version>
+        <ehcache.version>2.5.3</ehcache.version>
+        <!-- Don't change this version without also changing the shiro-hazelcast and shiro-features OSGi metadata: -->
+        <hazelcast.version>2.4.1</hazelcast.version>
         <hsqldb.version>1.8.0.7</hsqldb.version>
         <jdk.version>1.6</jdk.version>
         <jetty.version>6.1.26</jetty.version>
@@ -96,6 +98,9 @@
         <gmaven.version>1.3</gmaven.version>
         <groovy.version>1.8.5</groovy.version>
         <junit.version>4.8.2</junit.version>
+        <!-- so we can mock static methods in 3rd party libraries that sometimes don't use proper interfaces
+             ahem, hazelcast, ahem... -->
+        <powermock.version>1.5</powermock.version>
 
     </properties>
 
@@ -494,6 +499,18 @@
             <version>${groovy.version}</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-module-junit4</artifactId>
+            <version>${powermock.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-easymock</artifactId>
+            <version>${powermock.version}</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <dependencyManagement>
@@ -687,6 +704,11 @@
                 </exclusions>
             </dependency>
             <dependency>
+                <groupId>com.hazelcast</groupId>
+                <artifactId>hazelcast</artifactId>
+                <version>2.4.1</version>
+            </dependency>
+            <dependency>
                 <!-- Used for sample applications only - not required for the framework: -->
                 <groupId>org.hibernate</groupId>
                 <artifactId>hibernate</artifactId>

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/ehcache/pom.xml
----------------------------------------------------------------------
diff --git a/support/ehcache/pom.xml b/support/ehcache/pom.xml
index 02004d5..3020d9d 100644
--- a/support/ehcache/pom.xml
+++ b/support/ehcache/pom.xml
@@ -67,7 +67,7 @@
                         <Export-Package>org.apache.shiro.cache.ehcache*;version=${project.version}</Export-Package>
                         <Import-Package>
                             org.apache.shiro*;version="${shiro.osgi.importRange}",
-                            net.sf.ehcache*;version="[2.3, 3)",
+                            net.sf.ehcache*;version="[2.5, 3)",
                             *
                         </Import-Package>
                     </instructions>

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/ehcache/src/main/java/org/apache/shiro/cache/ehcache/EhCacheManager.java
----------------------------------------------------------------------
diff --git a/support/ehcache/src/main/java/org/apache/shiro/cache/ehcache/EhCacheManager.java b/support/ehcache/src/main/java/org/apache/shiro/cache/ehcache/EhCacheManager.java
index 3a802f2..ce1a637 100644
--- a/support/ehcache/src/main/java/org/apache/shiro/cache/ehcache/EhCacheManager.java
+++ b/support/ehcache/src/main/java/org/apache/shiro/cache/ehcache/EhCacheManager.java
@@ -237,13 +237,15 @@ public class EhCacheManager implements CacheManager, Initializable, Destroyable
             try {
                 net.sf.ehcache.CacheManager cacheMgr = getCacheManager();
                 cacheMgr.shutdown();
-            } catch (Exception e) {
+            } catch (Throwable t) {
                 if (log.isWarnEnabled()) {
                     log.warn("Unable to cleanly shutdown implicitly created CacheManager instance.  " +
-                            "Ignoring (shutting down)...");
+                            "Ignoring (shutting down)...", t);
                 }
+            } finally {
+                this.manager = null;
+                this.cacheManagerImplicitlyCreated = false;
             }
-            cacheManagerImplicitlyCreated = false;
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/features/pom.xml
----------------------------------------------------------------------
diff --git a/support/features/pom.xml b/support/features/pom.xml
index f0a4379..fb28c8a 100644
--- a/support/features/pom.xml
+++ b/support/features/pom.xml
@@ -33,7 +33,8 @@
 
     <properties>
         <aspectj.bundle.version>1.6.16_1</aspectj.bundle.version>
-        <ehcache.bundle.version>2.5.0_1</ehcache.bundle.version>
+        <ehcache.bundle.version>2.5.3_1</ehcache.bundle.version>
+        <hazelcast.bundle.version>2.4.1_1</hazelcast.bundle.version>
         <quartz.bundle.version>1.6.1_1</quartz.bundle.version>
         <!-- Not a Shiro dependency - used for quartz bundle resolution only (see features.xml): -->
         <commons.collections.version>3.2.1</commons.collections.version>

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/features/src/main/resources/features.xml
----------------------------------------------------------------------
diff --git a/support/features/src/main/resources/features.xml b/support/features/src/main/resources/features.xml
index 72451a2..5f9b3fb 100644
--- a/support/features/src/main/resources/features.xml
+++ b/support/features/src/main/resources/features.xml
@@ -53,6 +53,25 @@
         <bundle>mvn:org.apache.shiro/shiro-ehcache/${project.version}</bundle>
     </feature>
 
+    <feature name="shiro-hazelcast" version="${project.version}" resolver="(obr)">
+        <feature version="${project.version}">shiro-core</feature>
+        <!-- Is there a Hazelcast osgi bundle somewhere? The following line just assumes it exists: -->
+        <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.hazelcast/${hazelcast.bundle.version}</bundle>
+        <bundle>mvn:org.apache.shiro/shiro-hazelcast/${project.version}</bundle>
+    </feature>
+
+    <!--
+    TODO enable when openid4j module is done
+    <feature name="shiro-openid4j" version="${project.version}" resolver="(obr)">
+        <feature version="${project.version}">shiro-core</feature>
+        <feature version="${project.version}">shiro-web</feature>
+        <feature version="[3,4)">spring</feature>
+        TODO: is there an openid4j OSGi bundle?
+        <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.openid4j/${openid4j.version}</bundle>
+        <bundle>mvn:org.apache.shiro/shiro-openid4j/${project.version}</bundle>
+    </feature>
+    -->
+
     <feature name="shiro-quartz" version="${project.version}" resolver="(obr)">
         <feature version="${project.version}">shiro-core</feature>
         <bundle dependency='true'>mvn:commons-collections/commons-collections/${commons.collections.version}</bundle>

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/hazelcast/pom.xml
----------------------------------------------------------------------
diff --git a/support/hazelcast/pom.xml b/support/hazelcast/pom.xml
new file mode 100644
index 0000000..d5f6194
--- /dev/null
+++ b/support/hazelcast/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~     http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <parent>
+        <groupId>org.apache.shiro</groupId>
+        <artifactId>shiro-root</artifactId>
+        <version>1.3.0-SNAPSHOT</version>
+        <relativePath>../../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>shiro-hazelcast</artifactId>
+    <name>Apache Shiro :: Support :: Hazelcast</name>
+    <packaging>bundle</packaging>
+
+    <properties>
+        <hazelcast.osgi.importRange>[2.4, 3)</hazelcast.osgi.importRange>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.shiro</groupId>
+            <artifactId>shiro-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.hazelcast</groupId>
+            <artifactId>hazelcast</artifactId>
+        </dependency>
+        <!-- Test dependencies -->
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>jcl-over-slf4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>org.apache.shiro.hazelcast</Bundle-SymbolicName>
+                        <Export-Package>org.apache.shiro.hazelcast*;version=${project.version}</Export-Package>
+                        <Import-Package>
+                            org.apache.shiro*;version="${shiro.osgi.importRange}",
+                            com.hazelcast*;version="${hazelcast.osgi.importRange}",
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/hazelcast/src/main/java/org/apache/shiro/hazelcast/cache/HazelcastCacheManager.java
----------------------------------------------------------------------
diff --git a/support/hazelcast/src/main/java/org/apache/shiro/hazelcast/cache/HazelcastCacheManager.java b/support/hazelcast/src/main/java/org/apache/shiro/hazelcast/cache/HazelcastCacheManager.java
new file mode 100644
index 0000000..68db4c9
--- /dev/null
+++ b/support/hazelcast/src/main/java/org/apache/shiro/hazelcast/cache/HazelcastCacheManager.java
@@ -0,0 +1,245 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.hazelcast.cache;
+
+import com.hazelcast.config.Config;
+import com.hazelcast.core.Hazelcast;
+import com.hazelcast.core.HazelcastInstance;
+import org.apache.shiro.ShiroException;
+import org.apache.shiro.cache.Cache;
+import org.apache.shiro.cache.CacheException;
+import org.apache.shiro.cache.CacheManager;
+import org.apache.shiro.cache.MapCache;
+import org.apache.shiro.util.Destroyable;
+import org.apache.shiro.util.Initializable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+
+/**
+ * A {@code CacheManager} implementation backed by <a href="http://www.hazelcast.com/">Hazelcast</a>,
+ * &quot;an open source clustering and highly scalable data distribution platform for Java&quot;
+ * <p/>
+ * This implementation interacts with a {@link HazelcastInstance} to
+ * {@link HazelcastInstance#getMap(String) acquire} named {@link java.util.concurrent.ConcurrentMap ConcurrentMap}
+ * instances.  Those clustered/distributed Map instances are then wrapped and made available to {@code CacheManager}
+ * callers as {@link MapCache} instances via {@link #getCache(String)}.
+ * <h2>Configuration</h2>
+ * This implementation's backing {@code HazelcastInstance} can be configured in one of three ways:
+ * <ol>
+ * <li>Doing nothing and leveraging default Hazelcast configuration mechanisms</li>
+ * <li>Supplying an already-existing {@code HazelcastInstance}</li>
+ * <li>Supplying a {@link Config} instance and using that to create a new {@code HazelcastInstance}</li>
+ * </ol>
+ * <h3>Default Configuration</h3>
+ * If you simply instantiate a {@code HazelcastCacheManager} and do nothing further, its backing
+ * {@link HazelcastInstance} instance will be created automatically by calling
+ * {@link Hazelcast#newHazelcastInstance(com.hazelcast.config.Config) Hazelcast.newHazelcastInstance(null)}.
+ * <p/>
+ * The null argument instructs Hazelcast to use whatever default configuration mechanism it has at its disposal,
+ * usually a {@code hazelcast.xml} file at the root of the classpath, or if that is not present, the
+ * {@code hazelcast-default.xml} file contained in the Hazelcast {@code .jar} file itself.
+ * <p/>
+ * <h3>An existing {@code HazelcastInstance}</h3>
+ * If you have created a {@code HazelcastInstance} outside of Shiro's knowledge/control, you can simply configure it
+ * to be used by calling {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}.
+ * <p/>
+ * <h3>A {@link Config} instance</h3>
+ * If you do not want to use the above two options, you can have programmatic control over all of Hazelcast's
+ * configuration by <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">creating and configuring a
+ * Config instance</a>.
+ * <p/>
+ * Once constructed, you can set it via {@link #setConfig(com.hazelcast.config.Config) setConfig(config)}. This config
+ * instance will be used to acquire a new Hazelcast instance by calling
+ * {@link Hazelcast#newHazelcastInstance(Config) Hazelcast.newHazelcastInstance(config)}
+ *
+ * @see <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast Configuration Documentation</a>
+ * @since 1.3
+ */
+public class HazelcastCacheManager implements CacheManager, Initializable, Destroyable {
+
+    public static final Logger log = LoggerFactory.getLogger(HazelcastCacheManager.class);
+
+    private boolean implicitlyCreated = false;
+    private HazelcastInstance hazelcastInstance;
+    private Config config;
+
+    /**
+     * Returns a {@link MapCache} instance representing the named Hazelcast-managed
+     * {@link com.hazelcast.core.IMap IMap}.  The Hazelcast Map is obtained by calling
+     * {@link HazelcastInstance#getMap(String) hazelcastInstance.getMap(name)}.
+     *
+     * @param name the name of the cache to acquire.
+     * @param <K> the type of map key
+     * @param <V> the type of map value
+     * @return a {@link MapCache} instance representing the named Hazelcast-managed {@link com.hazelcast.core.IMap IMap}.
+     * @throws CacheException
+     * @see HazelcastInstance#getMap(String)
+     * @see #ensureHazelcastInstance()
+     *
+     */
+    public <K, V> Cache<K, V> getCache(String name) throws CacheException {
+        Map<K, V> map = ensureHazelcastInstance().getMap(name); //returned map is a ConcurrentMap
+        return new MapCache<K, V>(name, map);
+    }
+
+    /**
+     * Ensures that this implementation has a backing {@link HazelcastInstance}, and if not, implicitly creates one
+     * via {@link #createHazelcastInstance()}.
+     *
+     * @return the backing (potentially newly created) {@code HazelcastInstance}.
+     * @see #createHazelcastInstance()
+     * @see HazelcastInstance
+     */
+    protected HazelcastInstance ensureHazelcastInstance() {
+        if (this.hazelcastInstance == null) {
+            this.hazelcastInstance = createHazelcastInstance();
+            this.implicitlyCreated = true;
+        }
+        return this.hazelcastInstance;
+    }
+
+    /**
+     * Initializes this instance by {@link #ensureHazelcastInstance() ensuring} there is a backing
+     * {@link HazelcastInstance}.
+     *
+     * @throws ShiroException
+     * @see #ensureHazelcastInstance()
+     * @see HazelcastInstance
+     */
+    public void init() throws ShiroException {
+        ensureHazelcastInstance();
+    }
+
+    /**
+     * Implicitly creates and returns a new {@link HazelcastInstance} that will be used to back this implementation.
+     * This implementation calls:
+     * <pre>
+     * return Hazelcast.newHazelcastInstance(this.config);
+     * </pre>
+     * using any {@link #setConfig(com.hazelcast.config.Config) configured} {@code Config} object.  If no config
+     * object has been specified, {@code this.config} will be {@code null}, thereby using Hazelcast's
+     * <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">default configuration mechanism</a>.
+     * <p/>
+     * Can be overridden by subclasses for custom creation behavior.
+     *
+     * @return a new {@link HazelcastInstance} that will be used to back this implementation
+     * @see Hazelcast#newHazelcastInstance(com.hazelcast.config.Config)
+     * @see Config
+     */
+    protected HazelcastInstance createHazelcastInstance() {
+        return Hazelcast.newHazelcastInstance(this.config);
+    }
+
+    //needed for unit tests only - not part of Shiro's public API
+
+    /**
+     * NOT PART OF SHIRO'S ACCESSIBLE API.  DO NOT DEPEND ON THIS.  This method was added for testing purposes only.
+     * <p/>
+     * Returns {@code true} if this {@code HazelcastCacheManager} instance implicitly created the backing
+     * {@code HazelcastInstance}, or {@code false} if one was externally provided via
+     * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}.
+     *
+     * @return {@code true} if this {@code HazelcastCacheManager} instance implicitly created the backing
+     *         {@code HazelcastInstance}, or {@code false} if one was externally provided via
+     *         {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}.
+     */
+    protected final boolean isImplicitlyCreated() {
+        return this.implicitlyCreated;
+    }
+
+    /**
+     * Destroys any {@link #ensureHazelcastInstance() implicitly created} backing {@code HazelcastInstance}.  If the
+     * backing Hazelcast was not implicitly created (i.e. because it was configured externally and supplied via
+     * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) setHazelcastInstance}), this method does
+     * nothing.
+     *
+     * @throws Exception if there is a problem shutting down
+     */
+    public void destroy() throws Exception {
+        if (this.implicitlyCreated) {
+            try {
+                this.hazelcastInstance.getLifecycleService().shutdown();
+            } catch (Throwable t) {
+                if (log.isWarnEnabled()) {
+                    log.warn("Unable to cleanly shutdown implicitly created HazelcastInstance.  " +
+                            "Ignoring (shutting down)...", t);
+                }
+            } finally {
+                this.hazelcastInstance = null;
+                this.implicitlyCreated = false;
+            }
+        }
+    }
+
+    /**
+     * Returns the {@code HazelcastInstance} from which named {@link java.util.concurrent.ConcurrentMap ConcurrentMap}
+     * instances will be acquired to create {@link MapCache} instances.
+     *
+     * @return the {@code HazelcastInstance} from which named {@link java.util.concurrent.ConcurrentMap ConcurrentMap}
+     *         instances will be acquired to create {@link MapCache} instances.
+     */
+    public HazelcastInstance getHazelcastInstance() {
+        return hazelcastInstance;
+    }
+
+    /**
+     * Sets the {@code HazelcastInstance} from which named {@link java.util.concurrent.ConcurrentMap ConcurrentMap}
+     * instances will be acquired to create {@link MapCache} instances.
+     *
+     * @param hazelcastInstance the {@code HazelcastInstance} from which named
+     *                          {@link java.util.concurrent.ConcurrentMap ConcurrentMap} instances will be acquired to create
+     *                          {@link MapCache} instances.
+     */
+    public void setHazelcastInstance(HazelcastInstance hazelcastInstance) {
+        this.hazelcastInstance = hazelcastInstance;
+    }
+
+    /**
+     * Returns the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not
+     * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}, or {@code null} if the
+     * default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration
+     * mechanisms</a> will be used.
+     *
+     * @return the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not
+     *         {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}, or {@code null} if the
+     *         default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration
+     *         mechanisms</a> will be used.
+     * @see Hazelcast#newHazelcastInstance(com.hazelcast.config.Config)
+     */
+    public Config getConfig() {
+        return config;
+    }
+
+    /**
+     * Sets the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not
+     * {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}.  {@code null} can be set if the
+     * default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration
+     * mechanisms</a> will be used.
+     *
+     * @param config the Hazelcast {@code Config} object to use to create a backing {@code HazelcastInstance} if one is not
+     *               {@link #setHazelcastInstance(com.hazelcast.core.HazelcastInstance) supplied}, or {@code null} if the
+     *               default <a href="http://www.hazelcast.com/docs/2.5/manual/multi_html/ch12.html">Hazelcast configuration
+     *               mechanisms</a> will be used.
+     */
+    public void setConfig(Config config) {
+        this.config = config;
+    }
+}

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/hazelcast/src/test/groovy/org/apache/shiro/hazelcast/cache/HazelcastCacheManagerTest.groovy
----------------------------------------------------------------------
diff --git a/support/hazelcast/src/test/groovy/org/apache/shiro/hazelcast/cache/HazelcastCacheManagerTest.groovy b/support/hazelcast/src/test/groovy/org/apache/shiro/hazelcast/cache/HazelcastCacheManagerTest.groovy
new file mode 100644
index 0000000..d22eacb
--- /dev/null
+++ b/support/hazelcast/src/test/groovy/org/apache/shiro/hazelcast/cache/HazelcastCacheManagerTest.groovy
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.hazelcast.cache
+
+import com.hazelcast.config.Config
+import com.hazelcast.core.Hazelcast
+import com.hazelcast.core.HazelcastInstance
+import com.hazelcast.core.IMap
+import com.hazelcast.core.LifecycleService
+import org.apache.shiro.cache.MapCache
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.powermock.core.classloader.annotations.PrepareForTest
+import org.powermock.modules.junit4.PowerMockRunner
+
+import static org.easymock.EasyMock.*
+import static org.junit.Assert.*
+import static org.powermock.api.easymock.PowerMock.*
+
+/**
+ * Unit tests for {@link HazelcastCacheManager}.  Uses PowerMock to mock Hazelcast's static method calls.
+ *
+ * @since 1.3
+ */
+@RunWith(PowerMockRunner)
+@PrepareForTest(Hazelcast)
+class HazelcastCacheManagerTest {
+
+    @Test
+    void testGetSetHazelcastInstance() {
+        def hc = createStrictMock(HazelcastInstance)
+        def manager = new HazelcastCacheManager();
+        manager.hazelcastInstance = hc
+
+        replay hc
+
+        assertSame hc, manager.hazelcastInstance
+
+        verify hc
+    }
+
+    @Test
+    void testInit() {
+
+        mockStatic(Hazelcast)
+        //create a mock instead of starting a networked node:
+        def hc = createStrictMock(HazelcastInstance)
+
+        expect(Hazelcast.newHazelcastInstance(null)).andReturn(hc)
+
+        replay Hazelcast, hc
+
+        def manager = new HazelcastCacheManager()
+
+        try {
+            manager.init()
+
+            assertNull manager.config
+            assertSame hc, manager.hazelcastInstance
+            assertTrue manager.implicitlyCreated
+        } finally {
+            verify Hazelcast, hc
+        }
+    }
+
+    @Test
+    void testDestroy() {
+
+        mockStatic Hazelcast
+        def hc = createStrictMock(HazelcastInstance)
+        def lcService = createStrictMock(LifecycleService)
+
+        expect(Hazelcast.newHazelcastInstance(null)).andReturn(hc)
+        expect(hc.getLifecycleService()).andReturn(lcService)
+        lcService.shutdown()
+
+        replay Hazelcast, hc, lcService
+
+        def manager = new HazelcastCacheManager()
+        manager.init() //force implicit creation
+
+        manager.destroy()
+
+        assertNull manager.hazelcastInstance
+        assertFalse manager.implicitlyCreated
+
+        verify Hazelcast, hc, lcService
+    }
+
+    @Test
+    void testDestroyWithThrowable() {
+
+        mockStatic Hazelcast
+        def hc = createStrictMock(HazelcastInstance)
+        def lcService = createStrictMock(LifecycleService)
+
+        expect(Hazelcast.newHazelcastInstance(null)).andReturn(hc)
+        expect(hc.getLifecycleService()).andReturn(lcService)
+        lcService.shutdown()
+        expectLastCall().andThrow(new IllegalStateException())
+
+        replay Hazelcast, hc, lcService
+
+        def manager = new HazelcastCacheManager()
+        manager.init() //force implicit creation
+
+        manager.destroy()
+
+        assertNull manager.hazelcastInstance
+        assertFalse manager.implicitlyCreated
+
+        verify Hazelcast, hc, lcService
+    }
+
+
+    @Test
+    void testGetCache() {
+
+        mockStatic Hazelcast
+        def hc = createStrictMock(HazelcastInstance)
+        def hcMap = createStrictMock(IMap)
+
+        expect(Hazelcast.newHazelcastInstance(null)).andReturn(hc)
+        expect(hc.getMap("foo")).andReturn(hcMap)
+
+        replay Hazelcast, hc, hcMap
+
+        try {
+            def manager = new HazelcastCacheManager()
+            def cache = manager.getCache("foo")
+
+            assertNotNull cache
+            assertTrue cache instanceof MapCache
+            assertNotNull cache.map
+            assertTrue cache.map instanceof IMap
+        } finally {
+            verify Hazelcast, hc, hcMap
+        }
+    }
+
+    @Test
+    void testCustomConfig() {
+
+        mockStatic Hazelcast
+
+        def hc = createStrictMock(HazelcastInstance)
+        def config = createStrictMock(Config)
+
+        expect(Hazelcast.newHazelcastInstance(same(config))).andReturn(hc)
+
+        replay Hazelcast, config
+
+        def manager = new HazelcastCacheManager()
+        manager.config = config
+
+        manager.init()
+
+        assertSame config, manager.config
+        assertSame hc, manager.hazelcastInstance
+
+        verify Hazelcast, config
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/hazelcast/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/support/hazelcast/src/test/resources/log4j.properties b/support/hazelcast/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e629e58
--- /dev/null
+++ b/support/hazelcast/src/test/resources/log4j.properties
@@ -0,0 +1,35 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+log4j.rootLogger=TRACE, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
+
+# Pattern to output: date priority [category] - message
+log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
+log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
+
+# General Apache libraries is WARN
+log4j.logger.org.apache=WARN
+
+log4j.logger.com.hazelcast=DEBUG
+
+log4j.logger.org.apache.shiro=INFO
+log4j.logger.org.apache.shiro.util.ThreadContext=WARN
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/shiro/blob/0b505c27/support/pom.xml
----------------------------------------------------------------------
diff --git a/support/pom.xml b/support/pom.xml
index 7fbd4c8..7d0dbff 100644
--- a/support/pom.xml
+++ b/support/pom.xml
@@ -34,6 +34,7 @@
     <modules>
         <module>aspectj</module>
         <module>ehcache</module>
+        <module>hazelcast</module>
         <module>quartz</module>
         <module>spring</module>
         <module>guice</module>