Refactor EH-CACHE integration classes to work with Spring IoC provided Cache rather than manage our own cache internally.

This commit is contained in:
Ben Alex 2004-12-05 04:37:05 +00:00
parent 01165ea0e1
commit 76c82db196
12 changed files with 187 additions and 158 deletions

View File

@ -21,40 +21,43 @@ import net.sf.acegisecurity.acl.basic.BasicAclEntryCache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataRetrievalFailureException;
/**
* Caches <code>BasicAclEntry</code>s using <A
* Caches <code>BasicAclEntry</code>s using a Spring IoC defined <A
* HREF="http://ehcache.sourceforge.net">EHCACHE</a>.
*
* @author Ben Alex
* @version $Id$
*/
public class EhCacheBasedAclEntryCache implements BasicAclEntryCache,
InitializingBean, DisposableBean {
InitializingBean {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(EhCacheBasedAclEntryCache.class);
private static final String CACHE_NAME = "ehCacheBasedAclEntryCache";
//~ Instance fields ========================================================
private Cache cache;
private CacheManager manager;
private int minutesToIdle = 5;
//~ Methods ================================================================
public void setCache(Cache cache) {
this.cache = cache;
}
public Cache getCache() {
return cache;
}
public BasicAclEntry[] getEntriesFromCache(
AclObjectIdentity aclObjectIdentity) {
Element element = null;
@ -85,43 +88,12 @@ public class EhCacheBasedAclEntryCache implements BasicAclEntryCache,
return holder.getBasicAclEntries();
}
public void setMinutesToIdle(int minutesToIdle) {
this.minutesToIdle = minutesToIdle;
}
/**
* Specifies how many minutes an entry will remain in the cache from when
* it was last accessed.
*
* <P>
* Defaults to 5 minutes.
* </p>
*
* @return Returns the minutes an element remains in the cache
*/
public int getMinutesToIdle() {
return minutesToIdle;
}
public void afterPropertiesSet() throws Exception {
if (CacheManager.getInstance().cacheExists(CACHE_NAME)) {
// dont remove the cache
cache = CacheManager.getInstance().getCache(CACHE_NAME);
} else {
manager = CacheManager.create();
// Cache name, max memory, overflowToDisk, eternal, timeToLive, timeToIdle
cache = new Cache(CACHE_NAME, Integer.MAX_VALUE, false, false,
minutesToIdle * 60, minutesToIdle * 60);
manager.addCache(cache);
if (cache == null) {
throw new IllegalArgumentException("cache mandatory");
}
}
public void destroy() throws Exception {
manager.removeCache(CACHE_NAME);
}
public void putEntriesInCache(BasicAclEntry[] basicAclEntry) {
BasicAclEntryHolder holder = new BasicAclEntryHolder(basicAclEntry);
Element element = new Element(basicAclEntry[0].getAclObjectIdentity(),

View File

@ -20,36 +20,32 @@ import net.sf.acegisecurity.providers.cas.StatelessTicketCache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataRetrievalFailureException;
/**
* Caches tickets using <A HREF="http://ehcache.sourceforge.net">EHCACHE</a>.
* Caches tickets using a Spring IoC defined <A
* HREF="http://ehcache.sourceforge.net">EHCACHE</a>.
*
* @author Ben Alex
* @version $Id$
*/
public class EhCacheBasedTicketCache implements StatelessTicketCache,
InitializingBean, DisposableBean {
InitializingBean {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(EhCacheBasedTicketCache.class);
private static final String CACHE_NAME = "ehCacheBasedTicketCache";
//~ Instance fields ========================================================
private Cache cache;
private CacheManager manager;
private int minutesToIdle = 20;
//~ Methods ================================================================
@ -75,43 +71,20 @@ public class EhCacheBasedTicketCache implements StatelessTicketCache,
}
}
public void setMinutesToIdle(int minutesToIdle) {
this.minutesToIdle = minutesToIdle;
public void setCache(Cache cache) {
this.cache = cache;
}
/**
* Specifies how many minutes an entry will remain in the cache from when
* it was last accessed. This is effectively the session duration.
*
* <P>
* Defaults to 20 minutes.
* </p>
*
* @return Returns the minutes an element remains in the cache
*/
public int getMinutesToIdle() {
return minutesToIdle;
public Cache getCache() {
return cache;
}
public void afterPropertiesSet() throws Exception {
if (CacheManager.getInstance().cacheExists(CACHE_NAME)) {
// dont remove the cache
cache = CacheManager.getInstance().getCache(CACHE_NAME);
} else {
manager = CacheManager.create();
// Cache name, max memory, overflowToDisk, eternal, timeToLive, timeToIdle
cache = new Cache(CACHE_NAME, Integer.MAX_VALUE, false, false,
minutesToIdle * 60, minutesToIdle * 60);
manager.addCache(cache);
if (cache == null) {
throw new IllegalArgumentException("cache mandatory");
}
}
public void destroy() throws Exception {
manager.removeCache(CACHE_NAME);
}
public void putTicketInCache(CasAuthenticationToken token) {
Element element = new Element(token.getCredentials().toString(), token);

View File

@ -20,56 +20,40 @@ import net.sf.acegisecurity.providers.dao.UserCache;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataRetrievalFailureException;
/**
* Caches <code>User</code> objects using <A
* Caches <code>User</code> objects using a Spring IoC defined <A
* HREF="http://ehcache.sourceforge.net">EHCACHE</a>.
*
* @author Ben Alex
* @version $Id$
*/
public class EhCacheBasedUserCache implements UserCache, InitializingBean,
DisposableBean {
public class EhCacheBasedUserCache implements UserCache, InitializingBean {
//~ Static fields/initializers =============================================
private static final Log logger = LogFactory.getLog(EhCacheBasedUserCache.class);
private static final String CACHE_NAME = "ehCacheBasedUserCache";
//~ Instance fields ========================================================
private Cache cache;
private CacheManager manager;
private int minutesToIdle = 5;
//~ Methods ================================================================
public void setMinutesToIdle(int minutesToIdle) {
this.minutesToIdle = minutesToIdle;
public void setCache(Cache cache) {
this.cache = cache;
}
/**
* Specifies how many minutes an entry will remain in the cache from when
* it was last accessed. This is effectively the session duration.
*
* <P>
* Defaults to 5 minutes.
* </p>
*
* @return Returns the minutes an element remains in the cache
*/
public int getMinutesToIdle() {
return minutesToIdle;
public Cache getCache() {
return cache;
}
public UserDetails getUserFromCache(String username) {
@ -95,24 +79,11 @@ public class EhCacheBasedUserCache implements UserCache, InitializingBean,
}
public void afterPropertiesSet() throws Exception {
if (CacheManager.getInstance().cacheExists(CACHE_NAME)) {
// dont remove the cache
cache = CacheManager.getInstance().getCache(CACHE_NAME);
} else {
manager = CacheManager.create();
// Cache name, max memory, overflowToDisk, eternal, timeToLive, timeToIdle
cache = new Cache(CACHE_NAME, Integer.MAX_VALUE, false, false,
minutesToIdle * 60, minutesToIdle * 60);
manager.addCache(cache);
if (cache == null) {
throw new IllegalArgumentException("cache mandatory");
}
}
public void destroy() throws Exception {
manager.removeCache(CACHE_NAME);
}
public void putUserInCache(UserDetails user) {
Element element = new Element(user.getUsername(), user);

View File

@ -17,11 +17,16 @@ package net.sf.acegisecurity.acl.basic.cache;
import junit.framework.TestCase;
import net.sf.acegisecurity.MockApplicationContext;
import net.sf.acegisecurity.acl.basic.AclObjectIdentity;
import net.sf.acegisecurity.acl.basic.BasicAclEntry;
import net.sf.acegisecurity.acl.basic.NamedEntityObjectIdentity;
import net.sf.acegisecurity.acl.basic.SimpleAclEntry;
import net.sf.ehcache.Cache;
import org.springframework.context.ApplicationContext;
/**
* Tests {@link EhCacheBasedAclEntryCache}.
@ -65,9 +70,7 @@ public class EhCacheBasedAclEntryCacheTests extends TestCase {
public void testCacheOperation() throws Exception {
EhCacheBasedAclEntryCache cache = new EhCacheBasedAclEntryCache();
cache.afterPropertiesSet();
// execute a second time to test detection of existing instance
cache.setCache(getCache());
cache.afterPropertiesSet();
cache.putEntriesInCache(new BasicAclEntry[] {OBJECT_100_SCOTT, OBJECT_100_MARISSA});
@ -83,13 +86,28 @@ public class EhCacheBasedAclEntryCacheTests extends TestCase {
assertEquals(OBJECT_200_PETER,
cache.getEntriesFromCache(
new NamedEntityObjectIdentity("OBJECT", "200"))[0]);
cache.destroy();
assertNull(cache.getEntriesFromCache(
new NamedEntityObjectIdentity("OBJECT", "NOT_IN_CACHE")));
}
public void testGettersSetters() {
public void testStartupDetectsMissingCache() throws Exception {
EhCacheBasedAclEntryCache cache = new EhCacheBasedAclEntryCache();
cache.setMinutesToIdle(15);
assertEquals(15, cache.getMinutesToIdle());
try {
cache.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
Cache myCache = getCache();
cache.setCache(myCache);
assertEquals(myCache, cache.getCache());
}
private Cache getCache() {
ApplicationContext ctx = MockApplicationContext.getContext();
return (Cache) ctx.getBean("eHCacheBackend");
}
}

View File

@ -19,9 +19,14 @@ import junit.framework.TestCase;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.MockApplicationContext;
import net.sf.acegisecurity.providers.cas.CasAuthenticationToken;
import net.sf.acegisecurity.providers.dao.User;
import net.sf.ehcache.Cache;
import org.springframework.context.ApplicationContext;
import java.util.List;
import java.util.Vector;
@ -55,8 +60,8 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
public void testCacheOperation() throws Exception {
EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
cache.setCache(getCache());
cache.afterPropertiesSet();
cache.afterPropertiesSet(); // second run for test coverage
// Check it gets stored in the cache
cache.putTicketInCache(getToken());
@ -70,14 +75,27 @@ public class EhCacheBasedTicketCacheTests extends TestCase {
// Check it doesn't return values for null or unknown service tickets
assertNull(cache.getByTicketId(null));
assertNull(cache.getByTicketId("UNKNOWN_SERVICE_TICKET"));
cache.destroy();
}
public void testGettersSetters() {
public void testStartupDetectsMissingCache() throws Exception {
EhCacheBasedTicketCache cache = new EhCacheBasedTicketCache();
cache.setMinutesToIdle(5);
assertEquals(5, cache.getMinutesToIdle());
try {
cache.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
Cache myCache = getCache();
cache.setCache(myCache);
assertEquals(myCache, cache.getCache());
}
private Cache getCache() {
ApplicationContext ctx = MockApplicationContext.getContext();
return (Cache) ctx.getBean("eHCacheBackend");
}
private CasAuthenticationToken getToken() {

View File

@ -19,8 +19,13 @@ import junit.framework.TestCase;
import net.sf.acegisecurity.GrantedAuthority;
import net.sf.acegisecurity.GrantedAuthorityImpl;
import net.sf.acegisecurity.MockApplicationContext;
import net.sf.acegisecurity.providers.dao.User;
import net.sf.ehcache.Cache;
import org.springframework.context.ApplicationContext;
/**
* Tests {@link EhCacheBasedUserCache}.
@ -51,6 +56,7 @@ public class EhCacheBasedUserCacheTests extends TestCase {
public void testCacheOperation() throws Exception {
EhCacheBasedUserCache cache = new EhCacheBasedUserCache();
cache.setCache(getCache());
cache.afterPropertiesSet();
// Check it gets stored in the cache
@ -65,14 +71,27 @@ public class EhCacheBasedUserCacheTests extends TestCase {
// Check it doesn't return values for null or unknown users
assertNull(cache.getUserFromCache(null));
assertNull(cache.getUserFromCache("UNKNOWN_USER"));
cache.destroy();
}
public void testGettersSetters() {
public void testStartupDetectsMissingCache() throws Exception {
EhCacheBasedUserCache cache = new EhCacheBasedUserCache();
cache.setMinutesToIdle(15);
assertEquals(15, cache.getMinutesToIdle());
try {
cache.afterPropertiesSet();
fail("Should have thrown IllegalArgumentException");
} catch (IllegalArgumentException expected) {
assertTrue(true);
}
Cache myCache = getCache();
cache.setCache(myCache);
assertEquals(myCache, cache.getCache());
}
private Cache getCache() {
ApplicationContext ctx = MockApplicationContext.getContext();
return (Cache) ctx.getBean("eHCacheBackend");
}
private User getUser() {

View File

@ -26,4 +26,18 @@
<!-- Automatically receives AuthenticationEvent messages from AbstractSecurityInterceptor -->
<bean id="secureObjectLoggerListener" class="net.sf.acegisecurity.intercept.event.LoggerListener"/>
<!-- Setup a cache we can use in tests of the caching layer -->
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
</bean>
<bean id="eHCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>testingCache</value>
</property>
</bean>
</beans>

View File

@ -110,36 +110,17 @@
release. These are:</para>
<itemizedlist spacing="compact">
<listitem>
<para>Replacing the Ant build with a Maven build. When this
happens the <literal>lib</literal> directory will no longer be
distributed in ZIP releases or hosted in CVS.</para>
</listitem>
<listitem>
<para>"Remember me" functionality. Some discussion on this can be
found at
<literal>http://sourceforge.net/mailarchive/forum.php?thread_id=5177499&amp;forum_id=40659</literal>.</para>
</listitem>
<listitem>
<para>A sample web application which demonstrates the access
control list package.</para>
</listitem>
<listitem>
<para>Implementation of an
<literal>ObjectDefinitionSource</literal> that retrieves its
details from a database.</para>
</listitem>
<listitem>
<para>Deprecation of Acegi Security's various EH-CACHE-based cache
implementations. Instead Acegi Security will provide new cache
implementations which use Spring Framework's new (currently in
CVS) <literal>EhCacheManagerFactoryBean</literal> factory. The
deprecated classes may be removed from the 1.0.0 release.</para>
</listitem>
</itemizedlist>
<para>Whilst this list is subject to change and not in any particular
@ -982,7 +963,7 @@ public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
authorities that have been granted to the principal. The principal and
its credentials are populated by the client code, whilst the granted
authorities are populated by the
<literal>AuthenticationManager</literal>. </para>
<literal>AuthenticationManager</literal>.</para>
<para><mediaobject>
<imageobject role="html">
@ -1232,10 +1213,30 @@ public aspect DomainObjectInstanceSecurityAspect implements InitializingBean {
&lt;property name="userCache"&gt;&lt;ref bean="userCache"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/&gt;
&lt;bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;
&lt;property name="cacheManager"&gt;
&lt;ref local="cacheManager"/&gt;
&lt;/property&gt;
&lt;property name="cacheName"&gt;
&lt;value&gt;userCache&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache"&gt;
&lt;property name="minutesToIdle"&gt;&lt;value&gt;5&lt;/value&gt;&lt;/property&gt;
&lt;property name="cache"&gt;&lt;ref local="userCacheBackend"/&gt;&lt;/property&gt;
&lt;/bean&gt;</programlisting></para>
<para>All Acegi Security EH-CACHE implementations (including
<literal>EhCacheBasedUserCache</literal>) require an EH-CACHE
<literal>Cache</literal> object. The <literal>Cache</literal> object
can be obtained from wherever you like, although we recommend you use
Spring's factory classes as shown in the above configuration. If using
Spring's factory classes, please refer to the Spring documentation for
further details on how to optimise the cache storage location, memory
usage, eviction policies, timeouts etc.</para>
<para>For a class to be able to provide the
<literal>DaoAuthenticationProvider</literal> with access to an
authentication repository, it must implement the
@ -3415,8 +3416,19 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
&lt;!-- &lt;property name="trustStore"&gt;&lt;value&gt;/some/path/to/your/lib/security/cacerts&lt;/value&gt;&lt;/property&gt; --&gt;
&lt;/bean&gt;
&lt;bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/&gt;
&lt;bean id="ticketCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean"&gt;
&lt;property name="cacheManager"&gt;
&lt;ref local="cacheManager"/&gt;
&lt;/property&gt;
&lt;property name="cacheName"&gt;
&lt;value&gt;ticketCache&lt;/value&gt;
&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="statelessTicketCache" class="net.sf.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache"&gt;
&lt;property name="minutesToIdle"&gt;&lt;value&gt;20&lt;/value&gt;&lt;/property&gt;
&lt;property name="cache"&gt;&lt;ref local="ticketCacheBackend"/&gt;&lt;/property&gt;
&lt;/bean&gt;
&lt;bean id="casAuthoritiesPopulator" class="net.sf.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator"&gt;
@ -3785,7 +3797,7 @@ $CATALINA_HOME/bin/startup.sh</programlisting></para>
<para>The <literal>net.sf.acegisecurity.acl</literal> package is very
simple, comprising only a handful of interfaces and a single class, as
shown in Figure 5. It provides the basic foundation for access control
list (ACL) lookups. </para>
list (ACL) lookups.</para>
<para><mediaobject>
<imageobject role="html">
@ -3847,7 +3859,7 @@ public AclEntry[] getAcls(java.lang.Object domainInstance, Authentication authen
<title>Integer Masked ACLs</title>
<para>Acegi Security System for Spring includes a production-quality
ACL provider implementation, which is shown in Figure 6. </para>
ACL provider implementation, which is shown in Figure 6.</para>
<para><mediaobject>
<imageobject role="html">

View File

@ -49,6 +49,7 @@
<action dev="benalex" type="update">Improved performance of JBoss container adapter (see reference docs)</action>
<action dev="benalex" type="update">Made DaoAuthenticationProvider detect null in Authentication.principal</action>
<action dev="benalex" type="update">Improved JaasAuthenticationProvider startup error detection</action>
<action dev="benalex" type="update">Refactored EH-CACHE implementations to use Spring IoC defined caches instead</action>
<action dev="benalex" type="fix">Fixed AbstractProcessingFilter to use removeAttribute (JRun compatibility)</action>
<action dev="benalex" type="fix">Fixed GrantedAuthorityEffectiveAclResolver support of UserDetails principals</action>
<action dev="benalex" type="update">Moved MethodSecurityInterceptor to ...intercept.method.aopalliance package</action>

View File

@ -34,6 +34,15 @@ applications:
net.sf.acegisecurity.intercept.method.MethodSecurityInterceptor to
net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor.
A simple find and replace will suffice to update your application contexts.</li>
<li>All of the EH-CACHE cache implementations provided with Acegi Security have
now been refactored to use a net.sf.ehcache.Cache obtained from
EhCacheManagerFactoryBean, which is included with Spring 1.1.1 and above.
See http://opensource.atlassian.com/confluence/spring/display/DISC/Caching+the+result+of+methods+using+Spring+and+EHCache
for more about this bean, or the Contacts sample application for how to
configure the EH-CACHE implementations provided with Acegi Security.
Note the "cache" property is now required, and the old internally-managed
cache properties have been removed.</li>
</ul>
</body>
</html>

View File

@ -52,8 +52,19 @@
<!-- <property name="trustStore"><value>/some/path/to/your/lib/security/cacerts</value></property> -->
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
<bean id="ticketCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>ticketCache</value>
</property>
</bean>
<bean id="statelessTicketCache" class="net.sf.acegisecurity.providers.cas.cache.EhCacheBasedTicketCache">
<property name="minutesToIdle"><value>20</value></property>
<property name="cache"><ref local="ticketCacheBackend"/></property>
</bean>
<bean id="casAuthoritiesPopulator" class="net.sf.acegisecurity.providers.cas.populator.DaoCasAuthoritiesPopulator">

View File

@ -34,8 +34,19 @@
<property name="passwordEncoder"><ref local="passwordEncoder"/></property>
</bean>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"/>
<bean id="userCacheBackend" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<ref local="cacheManager"/>
</property>
<property name="cacheName">
<value>userCache</value>
</property>
</bean>
<bean id="userCache" class="net.sf.acegisecurity.providers.dao.cache.EhCacheBasedUserCache">
<property name="minutesToIdle"><value>5</value></property>
<property name="cache"><ref local="userCacheBackend"/></property>
</bean>
<!-- Automatically receives AuthenticationEvent messages from DaoAuthenticationProvider -->